создать новый WinForm внешнего проекта с Assembly.Load - PullRequest
1 голос
/ 13 июля 2020

У меня есть 2 проекта windowsForm (Project A и B) в C #, но я хотел бы добавить в Project B ссылку на Project A по коду и вызвать Project A из Project B. Я использовал Assembly.Load и он работает, только если я удалю аргументы Main void.

Форма проекта A должна быть открыта как MdiParent из проекта B.

Я пробовал использовать Assembly.load и активатор .createinstance, но он не работал , когда я пытаюсь передать параметр метода.

With param args возвращает ошибку (System.MissingMethodException: 'Constructor in введите 'CompareXMLTools.Main' not found. ')

#using  System.Reflection.Assembly

Project A Program.cs

namespace CompareXMLTools
{
    static class Program
    {
        /// <summary>
        /// The main entry point for the application.
        /// </summary>
        [STAThread]
        static void Main(string[] args)
        {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            Application.Run(new Main(args));

        }
    }
}

WindowsForm

namespace CompareXMLTools
{
    public partial class Main : Form
    {
        public Main(string[] args)
        {
            InitializeComponent();
            ArgsPassed = args;
        }
    }
}

Проект B

namespace XMLValidator
{
    public partial class frmMain : Form
    {
        public frmMain(string[] args)
        {
            InitializeComponent();
            ArgsPassed = args;
        }
    }

    private void tsbrnCompareXML_Click(object sender, EventArgs e)
    {
        object dllUserControl = null;
        System.Reflection.Assembly assembly2 = AppDomain.CurrentDomain.Load(File.ReadAllBytes(@"D:\Projetos C#\XMLValidator\XMLValidator\CompareXMLTools.exe"));
        dllUserControl = assembly2.CreateInstance("CompareXMLTools.Main", true, System.Reflection.BindingFlags.Default, null, new string[] { }, System.Globalization.CultureInfo.CurrentCulture, null);

        ((frmMain)dllUserControl).MdiParent = this;
        ((frmMain)dllUserControl).Show();
    }
}

Примечание: команда Project B работает только в том случае, если я удалю поле ** string [] args ** из основного метода.

Мне нужно передать аргументы при вызове новой WinForm of pro ject A, как я могу это сделать?

1 Ответ

0 голосов
/ 13 июля 2020

Я предлагаю добавить конструктор без параметров в форму Form1, чтобы вы могли вызвать его после из внешней сборки, которая может не знать, какое количество параметров она ожидает (если есть), и добавить logi c, необходимые для обработки null параметры.

namespace XMLValidator
{
    public partial class Main : Form
    {
        public Main() : this(null) { }

        public Main(string[] args) {
            InitializeComponent();
            [Something] = args;
        }
    }
}

После этого, если вы не хотите / не можете использовать интерфейсы (чтобы иметь общее понимание контракта между этими сборками), у вас есть полагаться на свои знания о том, какие формы вы хотите загрузить, и вызывать их с помощью Name.

Параметры могут быть переданы с помощью перегрузки Activator.CreateInstance , которая принимает тип (вы знаете, что тип, который вы хотите загрузить, форма) и аргументы в форме params object[].

public static object CreateInstance (Type type, params object[] args);

Каждый объект в params представляет аргументы, ожидаемые конструктором указанного вами класса. Конструктор без параметров не ожидает никаких аргументов, поэтому вы передаете null в args[0]. В противном случае args[0] содержит аргументы непустого конструктора, который вы хотите вызвать, для инициализации указанного класса.

object[] args = new object[1] { new string[] { "Args String", "Args Value", "Other args" } };

и вызовите:

Activator.CreateInstance([Type], args) as [Type];

Я предлагаю построить промежуточный класс, который обрабатывает инициализацию внешней сборки, автоматически извлекает некоторую полезную информацию (NameSpace, ресурсы определенного типа c - Forms, ресурсы проекта и др. c.). поэтому вам просто нужно указать имя формы для ее активации и отображения.

Например, из меню в вашей MDIParent Form:

public partial class MDIParent : Form
{
    private ResourceBag formResources = null;

    public MDIParent()
    {
        InitializeComponent();
        formResources = new ResourceBag([Assembly Path]);
    }

    // [...]

    // From a ToolStrip MenuItem, load with arguments
    private void loadExternalFormToolStripMenuItem_Click(object sender, EventArgs e)
    {
        object[] args = new object[1] { new string[] { "Args String", "Args Value", "Other args" } };
        Form form1 = formResources.LoadForm("Form1", args);
        form1.MdiParent = this;
        form1.Show();
    }

    // From another ToolStrip MenuItem, load using the default Constructor
    private void loadExternalFormNoParamsToolStripMenuItem_Click(object sender, EventArgs e)
    {
        Form form1 = formResources.LoadForm("Form1");
        form1.MdiParent = this;
        form1.Show();
    }

}

Помощник ResourceBag class :

Возможно, добавьте перегрузку в public Form LoadForm(), чтобы передать другой NameSpace, если вы хотите загрузить объект класса, который не является частью значения по умолчанию NameSpace.

using System.IO;
using System.Reflection;
using System.Windows.Forms;

internal class ResourceBag
{
    private string m_AssemblyName = string.Empty;
    private static Assembly asm = null;

    public ResourceBag() : this(null) { }

    public ResourceBag(string assemblyPath)
    {
        if (!string.IsNullOrEmpty(assemblyPath)) {
            this.AssemblyName = assemblyPath;
        }
    }

    public string NameSpace { get; set; }

    public string AssemblyName {
        get => m_AssemblyName;
        set {
            if (File.Exists(value)) {
                m_AssemblyName = value;
                asm = Assembly.LoadFrom(m_AssemblyName);
                this.NameSpace= asm.GetName().Name;
            }
            else {
                throw new ArgumentException("Invalid Assembly path");
            }
        }
    }

    public Form LoadForm(string formName, params object[] args)
    {
        if (asm == null) throw new BadImageFormatException("Resource Library not loaded");
        return Activator.CreateInstance(asm.GetType($"{NameSpace}.{formName}"), args) as Form;
    }
}
...