【问题标题】:Winforms Events ArchitectureWinforms 事件架构
【发布时间】:2020-07-13 08:42:39
【问题描述】:

尝试在 winforms 中构建一个简单的应用程序,但我注意到我对 winforms 模型内部事件的理解存在差距。我知道我非常接近解决方案,但有些事情我不知道如何使用“winforms 方式”

这是我想要完成的 UI 布局。 Form1 是通过 VS2019 模板生成的,但 LeftSide 和 RightSide 都是我构建的两个独立的类。

*****Form1************************
|                                |
| **LeftSide***   **RightSide**  |
| |           |   |           |  |
| | _TextBox  |   | _TextBox  |  |
| | _Button   |   | _Button   |  |
| |           |   |           |  |
| |           |   |           |  |
| *************   *************  |
|                                |
**********************************

左侧和右侧:

  • 返回一个带有文本框和按钮的面板
  • 具有未公开的“子”控件(即,类本身负责 UI 布局和数据验证)
  • 需要相互和 Form1 通信
  • 不是从任何东西继承的;它们通过命名空间在 Form1 中使用
  • 在 Form1 中实例化

这里是(不完整的)Form1,想知道这里要添加什么

namespace myapp
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }
    }
}

这里是 LeftSide 和 RightSide 类(注意它们是相同的,除了将单词“left”替换为“right”)

namespace myapp.LeftSide
{
     class LeftSide
     {
            private Panel LeftSide_Panel = new Panel();
            private Textbox LeftSide_TextBox = new Textbox();
            
            private Button LeftSide_Button = new Button();

            private string LeftSide_Data; //store TextBox.text here

            public ReturnPanel()
            {
             //return a Panel that Form1 can use
             LeftSide_Panel.Controls.Add(LeftSide_Button);
             LeftSide_Panel.Controls.Add(LeftSide_TextBox);
             return this.LeftSide_Panel;
            }
      }
}

我知道我可以通过在 LeftSide 中创建这样的函数来为 LeftSide_Button 制作自定义事件处理程序:

private void LeftSide_Button_Click(object sender, EventArgs e)
{
 //read the textbox, assume it is valid and store the string in our LeftSide_Data
 this.LeftSide_Data = this.LeftSide_TextBox.Text;
}

如果我想将数据保留在类的本地,上述方法有效,但我不知道如何将 LeftSide_Button_Click 主要暴露给 Form1,有时也暴露给 RightSide。

我的问题是:

  • 如果 LeftSide 拥有其文本框内的 数据(并存储在 LeftSide_Data 中)并且 LeftSide_Button_Click 是我的自定义验证事件(验证未在上面显示以简化)Form1 如何变得不仅通知该按钮已被单击,但也发送了 LeftSide_Data 中的字符串?
  • 如何向任何想要订阅的人公开 LeftSide 事件?是使用“delegate”关键字还是“event”关键字?或者是其他东西?基于事件的关键字太多,因此是造成混淆的主要原因。
  • 是否应该将 private void LeftSide_Button_Click(object sender, EventArgs e) 从返回 void 更改为 private Eventhandler LeftSide_Button_Click(object sender, EventArgs e),如果是,我返回什么... EventHandler “由什么组成”?

编辑:我找到了下图,即使它没有将数据与事件一起传递,是否朝着正确的方向迈出了一步?

【问题讨论】:

  • 这里缺少的是称为事件聚合器的设计模式。它使您可以将事件与其发布者和订阅者分离。
  • @WiktorZychla 有趣...来自link “事件聚合器通过将该事件传播到目标对象来响应来自源对象的任何事件。”这更是如此我想要做的。谢谢你给我一些东西给谷歌:)
  • 太宽泛了。太宽泛了。这里有太多不同的问题,每个问题都太主观了……有多种解决问题的好方法。您应该首先创建“左”和“右”对象UserControl 对象。然后可以像使用任何其他控件一样使用它们,而您可以使用表单设计器来设计布局。除此之外,使用属性和事件等标准封装技术来保持对象解耦,并指定某个对象作为“控制器”来管理哪个对象订阅什么(可能是一个全新的对象)。

标签: c# winforms


【解决方案1】:

您可以声明一个代表您的自定义事件的签名的委托,提供您希望传递的任意数量的参数。然后,您只需声明该委托类型的事件。任何人都可以订阅您的自定义事件,以便在事件发生时收到通知。

这是一个简单的例子,委托名为dlgLeftSideData,事件名为LeftSideData。它传递发送者(LeftSide 产生事件的实例)以及字符串数据。如果您愿意,还可以通过发件人访问公共只读属性。我已经连接了按钮的单击事件以进行“验证”,然后如果有任何订阅者,它就会引发自定义事件:

class LeftSide
{

    public event dlgLeftSideData LeftSideData;
    public delegate void dlgLeftSideData(LeftSide sender, string data);
    
    private Panel LeftSide_Panel = new Panel();
    private TextBox LeftSide_TextBox = new TextBox();
    private Button LeftSide_Button = new Button();

    public LeftSide()
    {
        LeftSide_TextBox.Location = new Point(5, 5);
        LeftSide_Button.Location = new Point(5, 25);            
        LeftSide_Panel.Controls.Add(LeftSide_TextBox);
        LeftSide_Panel.Controls.Add(LeftSide_Button);

        LeftSide_Button.Click += LeftSide_Button_Click;
    }

    private void LeftSide_Button_Click(object sender, EventArgs e)
    {
        if (true) // some kind of validation
        {
            LeftSideData?.Invoke(this, this.Data); // raise the event if there are any subscribers
        }
    }

    public string Data
    {
        get
        {
            return LeftSide_TextBox.Text;
        }
    }
    
    public Panel LeftSidePanel
    {
        get {
            return this.LeftSide_Panel;
        }                        
    }

}

这是 Form1 中订阅自定义事件并对接收到的数据进行处理的示例代码:

public partial class Form1 : Form
{

    LeftSide ls = new LeftSide();

    public Form1()
    {
        InitializeComponent();
        this.Controls.Add(ls.LeftSidePanel);
        ls.LeftSideData += Ls_LeftSideData;
    }

    private void Ls_LeftSideData(LeftSide sender, string data)
    {
        label1.Text = data;
        // or we could use "sender" somehow as well
        label1.Text = sender.Data; // get value from the read-only property
    }

}

您可以在您的类中创建一个属性或方法,以接收对另一个 LeftSide/RightSide 的引用并在内部订阅事件。

【讨论】:

  • 首先...非常感谢您,我一直在努力解决这个问题,您真的澄清了。我会玩这个答案,希望它会开始沉没一点。
  • 没问题。在玩游戏时提出任何问题。
猜你喜欢
  • 1970-01-01
  • 2011-10-19
  • 1970-01-01
  • 2012-12-21
  • 2021-12-29
  • 2011-10-12
  • 2023-03-31
  • 2010-09-20
  • 1970-01-01
相关资源
最近更新 更多