【问题标题】:How to raise a generic event?如何引发通用事件?
【发布时间】:2016-09-15 02:07:04
【问题描述】:

背景

问题标题可能有点误导,但我不知道如何快速提出问题。我正在构建一个 winforms 应用程序并遵循 MVP 设计模式,带有被动视图。在我的应用程序的主视图(表单)中,有一个导航面板,其中包含按钮,单击这些按钮将打开另一个视图(表单)。我正在尝试创建泛型按钮:

// The type T represents the view (form) that should be opened when the button is clicked
NavigationButton<T>

主视图的演示者在运行时单独创建每个按钮:

// Code in Main Presenter - register each button
View.RegisterNavigationButton(new NavigationButton<IViewExample1>("Example 1")); // Pass in text to show on button
View.RegisterNavigationButton(new NavigationButton<IViewExample1>("Example 2"));

// Code in View
public void RegisterNavigationButton<T>(NavigationButton<T> button) where T : class, IView
{
    // Add button to flow layout panel
    _flpNavigation.Controls.Add(button);

    // Subscribe to click event
    button.Clicked += ButtonClicked<T>;
}

现在,如果这不是被动视图,当单击导航按钮时,您可能会继续从主视图直接创建新视图:

// This is the method subscribed to the click event as shown in the above code
private void ButtonClicked<T>(object sender, EventArgs e) where T : class, IView
{
    // The ApplicationController creates the new view (form) using an IoC container (Simple Injector)
    ApplicationController.ShowModelessForm<T>();
}

但是,这是一个被动视图,所以它不应该创建新的视图(表单)...

我的每个视图都实现了一个接口,演示者通过该接口持有对其随附视图的引用。视图接口定义了演示者可以订阅的事件。也就是说,presenter 可以直接从视图中调用方法,只要在接口中定义了方法,但是视图必须引发事件才能与 Presenter 进行通信。

问题

考虑到这种设置,我将如何与演示者沟通它应该创建一个新视图?我的每个视图都实现了一个接口,演示者通过该接口持有对其随附视图的引用。视图接口定义了演示者可以订阅的事件。给定我的通用设置,是否可以设置某种事件来与演示者交流以创建视图?

** 编辑 **

我的障碍是我不知道如何定义要引发的事件,以及如何引发它们。假设我有两个要打开的辅助视图,由以下视图定义:IView1 和 IView2。我是否必须在主视图中定义两个单独的事件处理程序,每个辅助视图一个?然后,单击按钮后,如何引发相应的事件?

【问题讨论】:

  • 我不确定我是否理解最后一个问题,因为您似乎已经确定了一种机制,可以以被动表单上定义的事件的形式与演示者进行交流。您在定义或提出这些事件时是否有具体问题?请添加更多关于您当前包版的性质的详细信息。
  • 感谢@Luc 的回复。我对我的问题进行了编辑!

标签: c# winforms generics simple-injector


【解决方案1】:

您决定事件处理程序需要什么。查看按钮的用途并引发相应的事件。如果您有多个具有相同目的的按钮,请让它们引发相同的事件。如果您有另一个与按钮具有相同目的的操作,请再次让它引发相同的事件。

您引发的事件与按钮的用途相关,并且基本上不知道因引发事件而可能发生的任何事情。

事件可以是更字面的“单击详细信息按钮”或更抽象的“请求详细数据”。

关于如何引发事件,https://msdn.microsoft.com/en-us/library/edzehd2t(v=vs.110).aspx 提供了一个相当标准的引发事件技术的示例。 (下文转述)

class Counter
{
    public event EventHandler DetailsButtonClicked;

    protected virtual void OnDetailsButtonClicked(EventArgs e)
    {
        if (DetailsButtonClicked != null)
        {
            DetailsButtonClicked(this, e);
        }
    }

    // provide remaining implementation for the class
}

在演示者中,订阅事件并执行操作,例如打开新视图。


有关在引发事件时传递消息的更多信息

您可以在调用事件时使用通用的 EventHandler 来传递消息。我只是鼓励您评估您提出的代码的可读性和轻松重构的能力。

例如,避免发送以编程方式表示某事的字符串消息。而是发送一个枚举或常量值。

public void TryIt()
{
    var z = new Counter();
    z.DetailsButtonClicked += Z_DetailsButtonClicked;

    z.OnDetailsButtonClicked("Greetings Earthlings");
}

private void Z_DetailsButtonClicked(object sender, CustomEventArgs e)
{
    Debug.Print(e.Message);
}

public class CustomEventArgs : EventArgs
{
    public CustomEventArgs(string message) { this.Message = message; }
    public string Message { get; set; }
}

class Counter
{
    public event EventHandler<CustomEventArgs> DetailsButtonClicked;

    public virtual void OnDetailsButtonClicked(string message)
    {
        if (DetailsButtonClicked != null)
        {
            DetailsButtonClicked(this, new CustomEventArgs(message));
        }
    }

    // provide remaining implementation for the class
}

【讨论】:

  • 感谢您的回复。我确实了解事件的基础知识。我试图以某种方式使用泛型,这样我只需要使用一个事件来传达需要打开一个新视图。我最初的想法是所有不同的导航按钮都会引发相同的事件,但会提供 eventargs 来告诉演示者要打开什么视图。现在,我想那是不可能的。更重要的是,这也不值得花时间。就像你说的,一个简单的例子,每个按钮都有一个单独的事件,效果很好,很简单!
  • @Andrew 您可以为每个按钮使用相同的事件处理程序,但如果您需要传递不同的数据类型,它会变得复杂。它还可以使调试成为一种真正的痛苦。除非您的程序流程可以保证一次只有一个源可以引发事件,否则最好使用单独的事件。
猜你喜欢
  • 2012-09-20
  • 1970-01-01
  • 2016-12-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多