取消订阅来自长期事件源的表单组件 - 何时?

时间:2010-08-06 14:04:45

标签: .net winforms event-handling

我有一个.NET 1.1。应用程序通过定制的翻译系统提供字符串资源,看起来有点像这样:

interface ITranslationProvider 
{
    string GetTranslation(string key);
    event LanguageChangedEvent LanguageChanged;
}

ie:语言可以在运行时更改,组件需要通过更新显示字符串来响应。

单个翻译提供程序会持续应用程序的生命周期,而使用翻译服务的Windows窗体组件会动态创建。如果我编写使用此功能的表单组件,何时是取消订阅LanguageChanged事件的正确时间?

例如,似乎覆盖Disposing()应该起作用:

class MyPanel : System.Windows.Forms.Panel 
{
    public MyPanel(ITranslationProvider translator) 
    {
        this.translator = translator;
        translator.LanguageChanged += new LanguageChangedEvent(SetText);
        SetText();
    }

    protected override void Dispose(bool disposing) 
    {
        base.Dispose(disposing);

        // is this the correct place to unregister? will Dispose() get 
        // called on this panel, even though the translator's event has 
        // a reference to it?
        translator.LanguageChanged -= new LanguageChangedEvent(SetText);
    }

    private void SetText() 
    {
        this.Text = translator.GetTranslation("my.panel.text");
    }

    private ITranslationProvider translator;
}

...但我无法找到这个是否安全的明确答案。有任何想法吗?

3 个答案:

答案 0 :(得分:2)

当处理其父表单时,您的控件将为Dispose

如果您通过致电Show()来显示表单,.Net将在关闭时自动处理。
如果您致电ShowDialog(),则您有责任处理该表单,可能是using块。 (您应该在任何情况下处理该表单,即使它不添加事件处理程序)

答案 1 :(得分:1)

是的,使用Disposing()很好。如果客户端代码混淆了它,那么它就不会被调用,那么由于句柄泄漏会导致更大的问题。

请注意,这些“反向事件”很难处理。如果您知道事件源总是超过消费者,则回调可以是更合适的解决方案。一个示例接口声明:

public interface ITranslatableControl {
    void SetText();
}

public MyPanel : Panel, ITranslatableControl {
    public MyPanel() {
       TranslationManager.RegisterControl(this);
    }
    void SetText() {
       this.Text = TranslationManager.GetText(this, "mumble");
    }
}

public static class TranslationManager {
    private List<ITranslatableControl> controls;
    public void RegisterControl(ITranslatableControl text) {
       Control ctl = (Control)text;
       ctl.Disposed += delegate { controls.Remove(text); }
       controls.Add(text);
       text.SetText();    // optional
    }
}

请注意,如何收听Disposed事件,管理员可以从注册的控件列表中自动删除控件。忘记覆盖Disposing,客户端控件再也无法搞砸了。在语言更改时,只需迭代列表并调用SetText()方法。另请注意,如果控件具有多个可翻译字符串,您现在可以为同一控件注册多个回调。现在还允许您在Register方法中指定字符串的键,并将转换作为SetText()的参数提供。等等。

答案 2 :(得分:0)

我个人更喜欢取消订阅HandleDestroyed事件中的外部事件(因为我不喜欢自我订阅,所以使用OnHandleDestroyed覆盖)。这不依赖于我的组件的用户做正确的事情 - 如果他们使用ShowDialog则调用Dispose。

我也订阅了HandleCreated事件,因为可以多次调用ShowDialog(Show不能调用)。