繁体   English   中英

如何从 WinForms 应用程序中用户控件上的子控件获取事件?

[英]How do I grab events from sub-controls on a user-control in a WinForms App?

有没有办法让主窗体能够拦截在用户控件上的子控件上触发的事件?

我在应用程序的主窗体中嵌入了一个自定义用户控件。 该控件包含处理数据的各种子控件,这些子控件本身由主窗体上的其他控件显示。 我想要的是当用户更改子控件时是否可以以某种方式通知主窗体,以便我可以在其他地方更新数据和相应的显示。

现在,我在作弊。 我有一个委托连接到子控件的焦点离开事件。 这个委托改变了我没有在其他地方使用的用户控件的一个属性(在这个原因中,CausesValidation)。 然后,当用户控件的 CausesValidation 属性更改时,我在主窗体上定义了一个委托,然后指示应用程序更新数据和显示。

出现问题是因为我还为焦点离开用户控件时设置了一个委托,因为我需要先验证用户控件中的字段,然后才能允许用户执行任何其他操作。 但是,如果用户只是在子控件之间切换,我不想验证,因为它们可能没有完成编辑。

基本上,我希望数据在用户切换子控件或离开用户控件时更新,但不进行验证。 当用户离开控件时,我想更新和验证。 现在,离开用户控件会导致验证触发两次。

最佳做法是在UserControl上公开事件,将事件冒泡到父窗体。 我已经为你整理了一个例子。 下面是对该示例提供的内容的描述。

  • UserControl1
  • 使用TextBox1创建UserControl
  • UserControl上注册一个名为ControlChanged的公共事件
  • UserControlTextBox1 TextChangedEvent 注册一个事件处理程序
  • TextChangeEvent处理函数中,我调用ControlChanged事件以冒泡到父窗体
  • Form1
  • 在设计器上放置UserControl1的实例
  • UserControl1MouseLeaveControlChanged注册一个事件处理程序

这是一个屏幕截图,说明我在UserControl上定义的ControlChanged事件可通过父 Windows 窗体上的 Visual Studio 中的 UX 使用。

用户控制的事件处理程序

此类事情的最佳模型是在您的用户控件上创建自定义事件,并在适当的时间引发它们。

您的场景相当复杂,但并非闻所未闻。 (我实际上在我当前的一个项目中处于一种非常相似的模式。)我处理它的方式是用户控件负责它自己的验证。 我不使用 CausesValidation; 相反,在适当的用户控制点,我通过覆盖 ValidateChildren() 执行验证。 (对我来说,这通常发生在用户单击用户控件上的“保存”或“下一步”时。)

不熟悉您的用户控件 UI,这可能不是 100% 适合您的方法。 但是,如果您引发自定义事件(可能使用指定是否执行验证的自定义 EventArgs),您应该能够到达您想要的位置。

您需要在用户控件内连接您关心的事件,并通过用户控件本身的一些自定义事件属性发布它们。 一个简单的例子是包装一个按钮点击事件:

// CustomControl.cs
// Assumes a Button 'myButton' has been added through the designer

// we need a delegate definition to type our event
public delegate void ButtonClickHandler(object sender, EventArgs e);

// declare the public event that other classes can subscribe to
public event ButtonClickHandler ButtonClickEvent;

// wire up the internal button click event to trigger our custom event
this.myButton.Click += new System.EventHandler(this.myButton_Click);
public void myButton_Click(object sender, EventArgs e)
{
  if (ButtonClickEvent != null)
  {
    ButtonClickEvent(sender, e);
  }
}

然后,在使用该控件的 Form 中,您可以像其他任何事件一样连接该事件:

// CustomForm.cs
// Assumes a CustomControl 'myCustomControl' has been added through the desinger
this.myCustomControl.ButtonClickEvent += new System.EventHandler(this.myCustomControl_ButtonClickEvent);
myCustomControl_ButtonClickEvent(object sender, EventArgs e)
{
  // do something with the newly bubbled event
}

如果有人仍然想知道如何在 WinForm 中模拟事件冒泡,那么Application.AddMessageFilter方法是一个很好的地方。

使用此方法,您可以安装自己的过滤器,该过滤器会监视发布到当前线程的消息队列的所有消息。

您应该知道此过滤器无法处理正在发送(未发布)的消息。 Fourtounatly,最有趣的事件(如点击事件)被发布而不发送,因此可以被这个过滤器监控

我想补充一点,正如所描述的,这实际上听起来像是在追逐红鲱鱼。 虽然看起来 WinForms 中缺少事件冒泡会给您带来麻烦,但实际情况是,糟糕的架构迫使您在不应该使用事件冒泡时需要使用事件冒泡。

如果您可以重构/重构您的设计,以便控件使用通用数据模型(MVC/MVP 是显而易见的选择),那么您可以简单地在模型上应用常见的 WinForms 模式(如 PropertyChanged 事件)来告诉您的主窗体和任何其他控件消耗这些数据来更新自己。

简而言之,其他答案是合理的,因为它们按要求回答了问题。 但从代码质量的角度来看,我认为更好的答案是将数据与 UI 分开。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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