首页 > 解决方案 > 使用简单注入器c#时从父窗体设置子winform属性

问题描述

我有一个 form1(不是 mdi),它在按钮单击事件上显示对话框,对话框基本上是一个弹出窗体,它显示 datagridview 控件上的数据。

我正在使用简单的注射器。

PopUpForm 有一个名为 LocationData 的属性,它是一个数据表。我需要在 form1 (父级)中设置该属性,以便在屏幕上显示数据时可以在 PopUpForm 上显示数据。

抱歉,我是简单注射器的新手,仍在学习,任何帮助或指导将不胜感激。我什至不知道我是否以正确的方式做事。

表格1

按钮点击事件

this._formOpener.ShowModalForm<PopUpForm>();

弹出窗体

public partial class PopUpForm : Form
{
    public DataTable LocationData { get; set; }

    public PopUpForm()
    {
        InitializeComponent();
    }

    private void PopUpForm_Load(object sender, EventArgs e)
    {
        dgvNearestLocations.DataSource = LocationData;

    }
}

节目类

 static class Program
{
    private static Container container;

    /// <summary>
    /// The main entry point for the application.
    /// </summary>
    [STAThread]
    static void Main()
    {
        Application.EnableVisualStyles();
        Application.SetCompatibleTextRenderingDefault(false);
        Bootstrap();
        Application.Run(container.GetInstance<Form1>());
    }

    private static void Bootstrap()
    {
        // Create the container as usual.
        container = new Container();

        // Register your types, for instance:

        container.RegisterSingleton<IFormOpener, FormOpener>();
        container.Register<Form1>(Lifestyle.Singleton); ;
        container.Register<PopUpForm>(Lifestyle.Singleton); ;

        // Optionally verify the container.
        container.Verify();
    }

}

表单打开器

public class FormOpener : IFormOpener
{
    private readonly Container container;
    private readonly Dictionary<Type, Form> openedForms;

    public FormOpener(Container container)
    {
        this.container = container;
        this.openedForms = new Dictionary<Type, Form>();
    }

    public DialogResult ShowModalForm<TForm>() where TForm : Form
    {
        using (var form = this.GetForm<TForm>())
        {
            return form.ShowDialog();
        }
    }

    private Form GetForm<TForm>() where TForm : Form
    {
        return this.container.GetInstance<TForm>();
    }
}

标签: c#dependency-injectioninversion-of-controlsimple-injector

解决方案


首先,您可能从这个答案中复制了 FormOpener 。但是你错过了关于表单需要是瞬态的部分。不要将您的表单注册为Singleton. 尤其是因为您处置了它们,这将一次又一次地起作用。下次你想显示 aForm时,你会得到一个ObjectDisposedException.

当您将表单注册为TransientSimple Injector 时,会告诉您表单已实现IDisposable,这(当然)是正确的。但是因为你小心处理,FormOpener你可以安全地抑制这个警告。像这样注册您的表格:

private static void RegisterWindowsForms(
      this Container container, IEnumerable<Assembly> assemblies)
{
    var formTypes =
        from assembly in assemblies
        from type in assembly.GetTypes()
        where type.IsSubclassOf(typeof(Form))
        where !type.IsAbstract
        select type;

    foreach (var type in formTypes)
    {
        var registration = Lifestyle.Transient.CreateRegistration(type, container);

        registration.SuppressDiagnosticWarning(DiagnosticType.DisposableTransientComponent,
            "Forms are disposed by application code. Letting Simple Injector do this " +
            "is problematic because that would need a scope, which is impossible to do.");

        container.AddRegistration(type, registration);
    }
}

来回答你的问题:你需要一些额外的基础设施来初始化Form.

通过让您的表单实现一个接口IFormInit<T>,您可以将数据传递给表单并直接显示它。

public interface IFormInit<T> : IDisposable
{
    DialogResult InitAndShowForm(T data);
}

为了让 Simple Injector 基于这个接口创建表单,我们需要在容器中注册它们。我们可以通过提供程序集列表让 Simple Injector 搜索所有关闭的实现,如下所示:

container.Register(typeof(IFormInit<>), assemblies, Lifestyle.Transient);

请注意,Simple Injector 将自动将这些注册与来自RegisterWindowsForms. 所以你现在可以Form通过调用获得一个实例:

container.GetInstance<PopupForm>();
      or
container.GetInstance<IFormInit<SomeDataClass>>();   

您现在可以将此代码添加到您的FormOpener课程中:

public DialogResult ShowModalForm<TData>(TData data)
{
    Type formType = typeof(IFormInit<>).MakeGenericType(typeof(TData));
    dynamic initForm = this.container.GetInstance(formType);

    DialogResult result = (DialogResult) initForm.InitAndShowForm(data);

    initForm.Dispose();
    return result;
}

这将根据容器实现的类型Form从容器中获取。IFormInit<T>获取表单后,在界面上调用函数,而不是直接调用Form.ShowDialog()。当表单关闭时,您将处理表单。

注意:dynamic打字的使用可能需要澄清。为什么需要它是由这里QueryHandler描述的模式启发的。

用法如下:

// Add a specific class to pass to the form
public class LocationDataWrapper
{
    public DataTable LocationData { get; set; }
}

public partial class PopUpForm : Form, IFormInit<LocationDataWrapper>
{
    public PopUpForm() => InitializeComponent();

    // Implement the interface, the loaded event can be removed
    public DialogResult InitAndShowForm(LocationDataWrapper data)
    {
        dgvNearestLocations.DataSource = data.LocationData;
        return this.ShowDialog();
    }
}

按钮点击事件

DialogResult result = this._formOpener.ShowModalForm(new LocationDataWrapper
{
    LocationData = locationDataTable,
});

Form您可以为每个表单创建包装器或数据类,当您让它实现时,它将自动显示正确的表单IFormInit<ThisSpecificDataClass>


推荐阅读