在 C# MVP 中处理菜单事件

Handle menu event in C# MVP

我正在学习 C# 和 MVP。我能够处理简单的菜单点击事件。我正在尝试使用 MVP 模式处理菜单单击事件,但无法弄清楚为什么它不起作用。我在互联网上查看了很多代码并提出了以下内容。

interface IMenuBarView
{
    event EventHandler AboutMenuItemClicked;
    ...
}

public partial class MenuBarView : UserControl, IMenuBarView
{
    public event EventHandler AboutMenuItemClicked;
    private MenuBarPresenter m_menuBarPresenter;

    public MenuBarView()
    {
        this.aboutToolStripMenuItem.Click += AboutMenuItemClicked;
        m_menuBarPresenter = new MenuBarPresenter(this);
    }
}

class MenuBarPresenter
{
    private IMenuBarView m_menuView;

    public MenuBarPresenter(IMenuBarView menu)
    {
        m_menuView = menu;
        m_menuView.AboutMenuItemClicked += AboutMenuItemClicked;
    }
    public  void AboutMenuItemClicked(object sender, EventArgs e)
    {
        MessageBox.Show("Hello");
    }
}

我可以在 MenuBarView 中有一个事件处理程序,它又可以调用演示者来执行逻辑。不过我比较感兴趣的是上面那个。这不是正确的做法吗? 我对 C# 真的很陌生,所以不要杀了我。 :)

编辑后: 我很快根据@Enigmativity 评论编造了一些我尝试编译的东西。它运行但从未调用该事件。创建一个窗体,添加一个菜单项并将菜单项名称命名为 item1ToolStripMenuItem。单击菜单时,不会触发该事件。

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace testEventHandler
{

    public partial class Form1 : Form, IMenuBarView
    {
        public event EventHandler AboutMenuItemClicked;
        private MenuBarPresenter m_menuBarPresenter;

        public Form1()
        {
            InitializeComponent();

            this.item1ToolStripMenuItem.Click += AboutMenuItemClicked;
            m_menuBarPresenter = new MenuBarPresenter(this);
        }


    }

    interface IMenuBarView
    {
        event EventHandler AboutMenuItemClicked;
    }

    class MenuBarPresenter
    {
        private IMenuBarView m_menuView;
        public MenuBarPresenter(IMenuBarView menu)
        {
            m_menuView = menu;
            m_menuView.AboutMenuItemClicked += item1ToolStripMenuItem_Click;
        }
        private void item1ToolStripMenuItem_Click(object sender, EventArgs e)
        {
            MessageBox.Show("hello");
        }
    }
}

您的命令顺序有问题。

当你写这篇文章时:

        this.item1ToolStripMenuItem.Click += this.AboutMenuItemClicked;

您是说“任何附加到 this.AboutMenuItemClicked 的处理程序都应该 复制 this.item1ToolStripMenuItem.Click”。当时没有附加的处理程序。您没有复制任何内容。

然后调用 m_menuBarPresenter = new MenuBarPresenter(this);item1ToolStripMenuItem_Click 附加到 this.AboutMenuItemClicked 事件委托。

如果你调换它的顺序:

        m_menuBarPresenter = new MenuBarPresenter(this);
        this.item1ToolStripMenuItem.Click += this.AboutMenuItemClicked;

但是,这不是附加事件的正确方法。您不需要关心是否有人已附加或处理程序。

        this.item1ToolStripMenuItem.Click += (s, e) => this.AboutMenuItemClicked?.Invoke(s, e);
        m_menuBarPresenter = new MenuBarPresenter(this);

这是正确的方法。

如果有订阅者,?.Invoke 会调用 .Invoke,但如果没有订阅者,它什么也不做。


我也玩过你的代码。这更接近我做 MVP 的方式:

public partial class Form1 : Form
{
    public event EventHandler AboutMenuItemClicked;
    private Presenter _presenter;
    public Form1()
    {
        InitializeComponent();

        this.item1ToolStripMenuItem.Click += (s, e) => this.AboutMenuItemClicked?.Invoke(s, e);

        _presenter = new Presenter(this);
    }

    public void ShowMessage(string message)
    {
        MessageBox.Show(message);
    }
}

interface IMenuBarView
{
    event EventHandler AboutMenuItemClicked;
}

class Presenter
{
    private readonly Form1 _form;
    private readonly AlertModel _alertModel;

    public Presenter(Form1 form)
    {
        _form = form;
        _form.AboutMenuItemClicked += _form_AboutMenuItemClicked;
        _alertModel = new AlertModel();
        _alertModel.Message += _alertModel_Message;
    }

    private void _alertModel_Message(object sender, string e)
    {
        _form.ShowMessage(e);
    }

    private void _form_AboutMenuItemClicked(object sender, EventArgs e)
    {
        _alertModel.UpdateModel("About Menu Item");
    }
}

class AlertModel
{
    public event EventHandler<string> Message;

    public void UpdateModel(string action)
    {
        this.Message?.Invoke(this, $"User has selected \"{action}\"");
    }
}