简体   繁体   English

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

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

Is there any way for the main form to be able to intercept events firing on a subcontrol on a user control?有没有办法让主窗体能够拦截在用户控件上的子控件上触发的事件?

I've got a custom user-control embedded in the main Form of my application.我在应用程序的主窗体中嵌入了一个自定义用户控件。 The control contains various subcontrols that manipulate data, which itself is displayed by other controls on the main form.该控件包含处理数据的各种子控件,这些子控件本身由主窗体上的其他控件显示。 What I'd like is if the main form could be somehow informed when the user changes subcontrols, so I could update the data and the corresponding display elsewhere.我想要的是当用户更改子控件时是否可以以某种方式通知主窗体,以便我可以在其他地方更新数据和相应的显示。

Right now, I am cheating.现在,我在作弊。 I have a delegate hooked up to the focus-leaving event of the subcontrols.我有一个委托连接到子控件的焦点离开事件。 This delegate changes a property of the user-control I'm not using elsewhere (in this cause, CausesValidation).这个委托改变了我没有在其他地方使用的用户控件的一个属性(在这个原因中,CausesValidation)。 I then have a delegate defined on the main form for when the CausesValidation property of the user control changes, which then directs the app to update the data and display.然后,当用户控件的 CausesValidation 属性更改时,我在主窗体上定义了一个委托,然后指示应用程序更新数据和显示。

A problem arises because I also have a delegate set up for when focus leaves the user-control, because I need to validate the fields in the user-control before I can allow the user to do anything else.出现问题是因为我还为焦点离开用户控件时设置了一个委托,因为我需要先验证用户控件中的字段,然后才能允许用户执行任何其他操作。 However, if the user is just switching between subcontrols, I don't want to validate, because they might not be done editing.但是,如果用户只是在子控件之间切换,我不想验证,因为它们可能没有完成编辑。

Basically, I want the data to update when the user switches subcontrols OR leaves the user control, but not validate.基本上,我希望数据在用户切换子控件或离开用户控件时更新,但不进行验证。 When the user leaves the control, I want to update AND validate.当用户离开控件时,我想更新和验证。 Right now, leaving the user-control causes validation to fire twice.现在,离开用户控件会导致验证触发两次。

The best practice would be to expose events on the UserControl that bubble the events up to the parent form.最佳做法是在UserControl上公开事件,将事件冒泡到父窗体。 I have gone ahead and put together an example for you.我已经为你整理了一个例子。 Here is a description of what this example provides.下面是对该示例提供的内容的描述。

  • UserControl1
  • Create a UserControl with TextBox1使用TextBox1创建UserControl
  • Register a public event on the UserControl called ControlChangedUserControl上注册一个名为ControlChanged的公共事件
  • Within the UserControl register an event handler for the TextBox1 TextChangedEventUserControlTextBox1 TextChangedEvent 注册一个事件处理程序
  • Within the TextChangeEvent handler function I call the ControlChanged event to bubble to the parent formTextChangeEvent处理函数中,我调用ControlChanged事件以冒泡到父窗体
  • Form1
  • Drop an instance of UserControl1 on the designer在设计器上放置UserControl1的实例
  • Register an event handler on UserControl1 for MouseLeave and for ControlChangedUserControl1MouseLeaveControlChanged注册一个事件处理程序

Here is a screenshot illustrating that the ControlChanged event that I defined on the UserControl is available through the UX in Visual Studio on the parent Windows form.这是一个屏幕截图,说明我在UserControl上定义的ControlChanged事件可通过父 Windows 窗体上的 Visual Studio 中的 UX 使用。

用户控制的事件处理程序

The best model for this sort of thing will be creating custom events on your user control, and raising them at the appropriate times.此类事情的最佳模型是在您的用户控件上创建自定义事件,并在适当的时间引发它们。

Your scenario is fairly complex, but not unheard of.您的场景相当复杂,但并非闻所未闻。 (I'm actually in a very similar mode on one of my current projects.) The way I approach it is that the user control is responsible for its own validation. (我实际上在我当前的一个项目中处于一种非常相似的模式。)我处理它的方式是用户控件负责它自己的验证。 I don't use CausesValidation;我不使用 CausesValidation; instead at the appropriate user control point, I perform validation through an override of ValidateChildren().相反,在适当的用户控制点,我通过覆盖 ValidateChildren() 执行验证。 (This usually happens when the user clicks "Save" or "Next" on the user control, for me.) (对我来说,这通常发生在用户单击用户控件上的“保存”或“下一步”时。)

Not being familiar with your user control UI, that may not be 100% the right approach for you.不熟悉您的用户控件 UI,这可能不是 100% 适合您的方法。 However, if you raise custom events (possibly with a custom EventArgs which specifies whether or not to perform validation), you should be able to get where you want to be.但是,如果您引发自定义事件(可能使用指定是否执行验证的自定义 EventArgs),您应该能够到达您想要的位置。

You will need to wire up the events you care about capturing inside your user control, and publish them through some custom event properties on the user control itself.您需要在用户控件内连接您关心的事件,并通过用户控件本身的一些自定义事件属性发布它们。 A simple example would be wrapping a button click event:一个简单的例子是包装一个按钮点击事件:

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

Then, in the Form that uses that control, you wire up the event like you would any other:然后,在使用该控件的 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
}

In case someone is still wondering how to simulate event bubbling in WinForm the method Application.AddMessageFilter is a good place to look at.如果有人仍然想知道如何在 WinForm 中模拟事件冒泡,那么Application.AddMessageFilter方法是一个很好的地方。

Using this method you can install your own filter which monitors all messages being posted to the current thread's message queue.使用此方法,您可以安装自己的过滤器,该过滤器会监视发布到当前线程的消息队列的所有消息。

You should be aware that messages being sent (not posted) cannot be handled by this filter.您应该知道此过滤器无法处理正在发送(未发布)的消息。 Fourtounatly, most interesting events (like click events) are posted and not sent and therefore can be monitored by this filter Fourtounatly,最有趣的事件(如点击事件)被发布而不发送,因此可以被这个过滤器监控

I would like to chime in that, as described, it actually sounds like you're chasing a red herring.我想补充一点,正如所描述的,这实际上听起来像是在追逐红鲱鱼。 While it seems like you have a situation where the lack of event bubbling in WinForms is causing you trouble, the reality is that a poor architecture is forcing you into needing event bubbling when you shouldn't.虽然看起来 WinForms 中缺少事件冒泡会给您带来麻烦,但实际情况是,糟糕的架构迫使您在不应该使用事件冒泡时需要使用事件冒泡。

If you can refactor/restructure your design such that the controls are working with a common data model (MVC/MVP are obvious choices) then you can simply apply common WinForms patterns like PropertyChanged events on the model to tell your main form and any other controls which consume that data to update themselves.如果您可以重构/重构您的设计,以便控件使用通用数据模型(MVC/MVP 是显而易见的选择),那么您可以简单地在模型上应用常见的 WinForms 模式(如 PropertyChanged 事件)来告诉您的主窗体和任何其他控件消耗这些数据来更新自己。

In short, the other answers are reasonable in that they answer the question as asked.简而言之,其他答案是合理的,因为它们按要求回答了问题。 But from a code quality standpoint, I think the better answer is to separate your data from the UI.但从代码质量的角度来看,我认为更好的答案是将数据与 UI 分开。

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

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