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

时间:2020-03-06 15:02:05  来源:igfitidea点击:

主窗体是否可以拦截用户控件的子控件上触发的事件?

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

现在,我在作弊。我有一个与子控件的焦点离开事件有关的代表。该委托更改了我不在其他地方使用的用户控件的属性(因此,CausesValidation)。然后,当用户控件的CausesValidation属性更改时,我在主窗体上定义了一个委托,该委托然后指示应用程序更新数据和显示。

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

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

解决方案

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

情况相当复杂,但并非闻所未闻。 (实际上,在我当前的一个项目中,我实际上处于非常相似的模式。)我采用的方式是用户控件负责其自身的验证。我不使用CausesValidation;而是在适当的用户控制点上,我通过重写ValidateChildren()来执行验证。 (对于我来说,这通常发生在用户单击用户控件上的"保存"或者"下一步"时。)

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

最佳做法是在" UserControl"上公开事件,使事件冒泡至父窗体。我已经为我们整理了一个例子。这是此示例提供的说明。

  • 在名为" ControlChanged"的" UserControl"上注册一个公共事件
  • 在" UserControl"中注册" TextBox1" TextChangedEvent的事件处理程序
  • TextChangeEvent处理函数中,我调用ControlChanged事件以冒泡到父窗体
  • 在" UserControl1"上为" MouseLeave"和" ControlChanged"注册一个事件处理程序

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

用户控制的事件处理程序http://friendfeed.s3.amazonaws.com/0d5a3968cb785625c8afb3974a8d84894c476291

我们将需要连接自己关心的事件,并将其通过用户控件本身上的一些自定义事件属性发布,以将它们捕获到用户控件中。一个简单的例子是包装按钮单击事件:

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

然后,在使用该控件的窗体中,像其他任何事件一样连接事件:

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

如前所述,我想对此进行提示,实际上听起来像是我们在追赶红鲱鱼。看起来我们似乎遇到了WinForms中缺少事件冒泡的情况,这给我们带来麻烦,但现实情况是,糟糕的体系结构迫使我们在不应该发生事件冒泡的情况下。

如果我们可以重构/重构设计,使控件可以与通用数据模型一起工作(MVC / MVP是显而易见的选择),那么我们只需在模型上应用诸如PropertyChanged事件之类的通用WinForms模式即可告知主窗体和其他控件消耗这些数据以进行自我更新。

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