简体   繁体   中英

How to raise a generic event?

Background

The question title might be a little misleading, but I'm not sure how to quickly ask the question. I'm building a winforms app and following the MVP design pattern, with passive views. In the main view (form) for my application, there is a navigation panel that hosts buttons that when clicked will open another view (form). I'm attempting to create generics buttons:

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

The presenter for the main view creates each button individually at run time:

// 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>;
}

Now, if this wasn't a passive view, when a navigation button was clicked, you could potentially go ahead and create the new view directly from the main view:

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

But, this is a passive view, so it shouldn't be creating new views (forms)...

Each of my views implements an interface, and the presenters hold a reference to its accompanying view via that interface. The view interfaces define events that the presenter can subscribe to. In other words, the presenter can call methods directly from the view as long as the method is defined in the interface, but the view must raise events to communicate to the presenter.

Question

Considering this setup, how would I go about communicating to the presenter that it should be creating a new view? Each of my views implements an interface, and the presenters hold a reference to its accompanying view via that interface. The view interfaces define events that the presenter can subscribe to. Is it possible to set up some kind of event(s) to communicate the presenter to create a view given my generic setup?

** EDIT **

My road block is that I don't know how to define the events to raise, and how to raise them. Say I have two secondary views that I want to open, defined by the following views: IView1 and IView2. Do I have to have two separate eventhandlers defined in my main view, one for each secondary view? And then, once the button is clicked, how do I raise the appropriate event?

You decide what you need for event handlers. Look at the purpose of the button and raise the appropriate event. If you have multiple buttons with the same purpose, have them raise the same event. If you have another action that has the same purpose as the button, again have it raise the same event.

The event you raise is linked to the button's purpose and is basically ignorant of any things that may happen because the event was raised.

The event could be more literal "Details Button Clicked" or a bit more abstract "Detailed Data Requested".

On the subject of how to raise the event, https://msdn.microsoft.com/en-us/library/edzehd2t(v=vs.110).aspx has an example of a fairly standard technique to raise an event. (Paraphrased below)

class Counter
{
    public event EventHandler DetailsButtonClicked;

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

    // provide remaining implementation for the class
}

In the presenter, subscribe to the event and perform the action, such as opening a new view.


More info on passing messages when raising events

You can use the generic EventHandler to pass messages when you call events. I would just encourage you to evaluate the code you come up with for readability and ability to easily refactor.

For example, avoid sending a string message that means something programmatically. Instead send an enum or constant value.

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
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM