如何使用Simple Injector注册Windows窗体 [英] How to register Windows Forms with Simple Injector

查看:52
本文介绍了如何使用Simple Injector注册Windows窗体的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

背景

我正在构建一个Winforms应用程序,该应用程序使用IoC容器(SimpleInjector)注册我的类型。在我的应用程序中,大多数屏幕(即表单)在任何给定时间都只有一个实例。

I'm building a winforms application where I am using an IoC container (SimpleInjector) to register my types. In my application, a majority of the screens (i.e. forms) will only have one instance at any given time.

问题

对于在任何给定时间只需要一个实例的表单,我可以将它们注册为单例:

For forms that only need one instance at any given time, I can register them as singletons:

container.Register<IHomeView, HomeView>(Lifestyle.Singleton);

这使我可以使用容器跟踪所有表格。但是,在这种情况下,当窗体关闭时,它将被处置(窗体实现IDisposable)。如果应用程序尝试使用容器再次打开该表单,则将丢弃该容器的表单实例,并引发异常。

This allows me to use the container to keep track of all forms. In this case, however, when a form gets closed it will then get disposed (forms implement IDisposable). If the application tries to open that form again using the container, the container's instance of the form will be disposed, and an exception is thrown.

问题

处理此问题的正确方法是什么?我目前看到两个解决方案:

What is the proper way to deal with this? I currently see two solutions:


  1. 对于每个表单,请覆盖靠近的表单以代替隐藏表单,而不是实际上将其关闭。我不太喜欢这个主意我觉得我宁愿每次都关闭表格并以新表格/新表格开始。

  2. 以暂时的生活方式而不是单身的方式注册表格。在这种情况下,容器实际上只是充当工厂的角色。我遇到两个问题:a)我失去了通过容器跟踪表单的能力,并且b)容器在验证过程中引发异常,指出不应将一次性类型注册为临时类型(我不知道为什么会这样) )。这些问题都适用于同时需要多个实例的表单。

我可以通过在验证过程中抑制诊断警告来解决问题b)。

I can get around problem b) by suppressing the diagnostic warning during verification.

registration = container.GetRegistration(typeof(ILoginView)).Registration;
registration.SuppressDiagnosticWarning(
    DiagnosticType.DisposableTransientComponent,
    "Winforms registration supression.");

在这里采用的正确方法是什么?我会丢失什么吗?

What is the correct approach to be taking here? Am I missing something?

推荐答案

理想情况下,您希望将表单注册为 Singleton 。但是,以我的经验,这将导致难以调试的错误,尤其是当您使用 BindingSource 用于将数据绑定到任何内容。

Ideally, you would want to register your forms as Singleton. In my experience, however, this will result in hard to debug errors, especially when you use a BindingSource for binding your data to whatever.

使用 Singleton 作为生活方式的第二个问题是,如果您的应用程序使用无模式窗口,则此窗口将抛出 ObjectDisposedException 再次打开时,因为Windows窗体应用程序框架将在第一次关闭时处理该窗体,而Simple Injector应该负责。因此,如果注册为Singleton,Simple Injector将创建一个实例,并且恰好一个实例。如果其他人(例如您的应用程序,Windows窗体框架)将处理该对象,则不会重新创建该对象。

A second problem with using Singleton as the lifestyle is that if your application uses modeless windows, this windows will throw an ObjectDisposedException when opened a second time, because the Windows Forms Application framework will dispose the Form on the first close, while Simple Injector should be in charge of that. So Simple Injector will create one–and exactly one–instance, if registered as Singleton. If somebody else (e.g. your application, the windows forms framework) will dispose the object, it won't be recreated.

最简单的解决方案,它也很容易实现可以理解,就是将您的表单注册为 Transient 。是的,您需要取消诊断警告。根据文档

The most easy solution, which is also easy to understand, is to register your forms as Transient. And yes, you need to suppress the diagnostic warnings. The reason for this diagnostic warning according to the documentation:

实现 IDisposable 的组件通常需要确定性的清理,但是Simple Injector不会隐式跟踪和处置在临时生活方式中注册的组件。

A component that implements IDisposable would usually need deterministic clean-up but Simple Injector does not implicitly track and dispose components registered with the transient lifestyle.

简单注入器无法处理瞬态组件,因为它无法确定应何时处置对象。但是,这意味着以模态方式打开并调用 .ShowDialog()的表单将永远不会被废弃!而且由于Windows窗体应用程序通常运行很长时间,甚至可能是一周或一个月,因此最终会导致 Win32Exception ,并显示一条消息:错误创建窗口句柄。从本质上讲,这意味着您用尽了计算机的所有资源。

Simple Injector is unable to dispose a transient component because it is unable to determine when the object should be disposed. This means, however, that forms that are opened in a modal fashion with a call to .ShowDialog() will never be disposed! And because a windows forms application typically runs for a long time, maybe even a week or month, this will eventually result in a 'Win32Exception' with a message: "Error Creating Window Handle". Which essentially means you exhausted all resources of the computer.

处理表格很重要。而且,即使您使用 Scope ,Simple Injector能够完成此任务,但Windows窗体实施起来并不容易。因此,您必须自己处理使用 ShowDialog()显示的封闭窗体。

Disposing of the forms is therefore important. And although Simple Injector is able to do this job if you would use a Scope, this is with Windows Forms not so easy to implement. So you yourself have to take care of disposing the closed Forms which have been shown using ShowDialog().

在您的特定用例上,有几种方法可以实现 FormOpener NavigationService 。一种实现方式:

Depending on your specific use case there are several ways to implement a FormOpener or NavigationService. One way to do it:

public interface IFormOpener
{
    void ShowModelessForm<TForm>() where TForm : Form;
    DialogResult ShowModalForm<TForm>() where TForm : Form;
}

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 void ShowModelessForm<TForm>() where TForm : Form
    {
        Form form;
        if (this.openedForms.ContainsKey(typeof(TForm)))
        {
            // a form can be held open in the background, somewhat like 
            // singleton behavior, and reopened/reshown this way
            // when a form is 'closed' using form.Hide()   
            form = this.openedForms[typeof(TForm)];
        }
        else
        {
            form = this.GetForm<TForm>();
            this.openedForms.Add(form.GetType(), form);
            // the form will be closed and disposed when form.Closed is called
            // Remove it from the cached instances so it can be recreated
            form.Closed += (s, e) => this.openedForms.Remove(form.GetType());
        }

        form.Show();
    }

    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>();
    }
}

该课程必须注册为 Singleton

container.RegisterSingleton<IFormOpener, FormOpener>();

例如,可以通过以您的应用程序的根形式注入此服务来使用它:

And can be used by injecting this service in for example your root form of the application:

public partial class RootForm : Form
{
    private readonly IFormOpener formOpener;

    public RootForm(IFormOpener formOpener)
    {
        this.formOpener = formOpener;
        this.InitializeComponent();
    }

    private void ShowCustomers_Click(object sender, EventArgs e)
    {
        this.formOpener.ShowModelessForm<AllCustomersForm>();
    }

    private void EditCustomer_Click(object sender, EventArgs e)
    {
        var result = this.formOpener.ShowModalForm<EditCustomerForm>();
        // do something with result
    }
}

这篇关于如何使用Simple Injector注册Windows窗体的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆