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). 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)。然后,我在主窗体上定义了一个委托,用于何时更改用户控件的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.
基本上,我希望在用户切换子控件或离开用户控件但不验证时更新数据。当用户离开控件时,我想更新并验证。现在,离开用户控件会导致验证两次。
5 个解决方案
#1
15
The best practice would be to expose events on the UserControl
that bubble the events up to the parent form. I have gone ahead and put together an example for you. Here is a description of what this example provides.
最佳做法是在UserControl上公开事件,将事件冒泡到父表单。我已经为你做了一个例子。以下是此示例提供的内容的说明。
-
UserControl1
- Create a
UserControl
withTextBox1
- Register a public event on the
UserControl
calledControlChanged
- Within the
UserControl
register an event handler for theTextBox1
TextChangedEvent - Within the
TextChangeEvent
handler function I call theControlChanged
event to bubble to the parent form
使用TextBox1创建UserControl
在UserControl上注册一个名为ControlChanged的公共事件
在UserControl寄存器中,TextBox1 TextChangedEvent的事件处理程序
在TextChangeEvent处理函数中,我调用ControlChanged事件以冒泡到父窗体
- Create a
-
Form1
- Drop an instance of
UserControl1
on the designer - Register an event handler on
UserControl1
forMouseLeave
and forControlChanged
在设计器上删除UserControl1的实例
在UserControl1上为MouseLeave和ControlChanged注册事件处理程序
- Drop an instance of
UserControl1使用TextBox1创建UserControl在UserControl上注册一个名为ControlChanged的公共事件在UserControl寄存器中,TextBox1 TextChangedEvent的事件处理程序在TextChangeEvent处理函数中我调用ControlChanged事件以鼓泡到父窗体
Form1在设计器上删除UserControl1的实例在UserControl1上为MouseLeave和ControlChanged注册事件处理程序
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获得。
用户控制的事件处理程序http://friendfeed.s3.amazonaws.com/0d5a3968cb785625c8afb3974a8d84894c476291
#2
2
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; instead at the appropriate user control point, I perform validation through an override of ValidateChildren(). (This usually happens when the user clicks "Save" or "Next" on the user control, for me.)
你的场景相当复杂,但并非闻所未闻。 (我实际上在我目前的一个项目中的模式非常相似。)我接近它的方式是用户控件负责自己的验证。我不使用CausesValidation;而是在适当的用户控制点,我通过覆盖ValidateChildren()执行验证。 (这通常发生在用户点击用户控件上的“保存”或“下一步”时。)
Not being familiar with your user control UI, that may not be 100% the right approach for you. 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.
不熟悉您的用户控件UI,这可能不是100%正确的方法。但是,如果您引发自定义事件(可能使用指定是否执行验证的自定义EventArgs),您应该能够获得您想要的位置。
#3
2
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:
然后,在使用该控件的表单中,您可以像处理其他任何内容一样连接事件:
// 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
}
#4
1
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
您应该知道此过滤器无法处理正在发送的消息(未发布)。有趣的是,最有趣的事件(如点击事件)会被发布而不会被发送,因此可以通过此过滤器进行监控
#5
0
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分开。
#1
15
The best practice would be to expose events on the UserControl
that bubble the events up to the parent form. I have gone ahead and put together an example for you. Here is a description of what this example provides.
最佳做法是在UserControl上公开事件,将事件冒泡到父表单。我已经为你做了一个例子。以下是此示例提供的内容的说明。
-
UserControl1
- Create a
UserControl
withTextBox1
- Register a public event on the
UserControl
calledControlChanged
- Within the
UserControl
register an event handler for theTextBox1
TextChangedEvent - Within the
TextChangeEvent
handler function I call theControlChanged
event to bubble to the parent form
使用TextBox1创建UserControl
在UserControl上注册一个名为ControlChanged的公共事件
在UserControl寄存器中,TextBox1 TextChangedEvent的事件处理程序
在TextChangeEvent处理函数中,我调用ControlChanged事件以冒泡到父窗体
- Create a
-
Form1
- Drop an instance of
UserControl1
on the designer - Register an event handler on
UserControl1
forMouseLeave
and forControlChanged
在设计器上删除UserControl1的实例
在UserControl1上为MouseLeave和ControlChanged注册事件处理程序
- Drop an instance of
UserControl1使用TextBox1创建UserControl在UserControl上注册一个名为ControlChanged的公共事件在UserControl寄存器中,TextBox1 TextChangedEvent的事件处理程序在TextChangeEvent处理函数中我调用ControlChanged事件以鼓泡到父窗体
Form1在设计器上删除UserControl1的实例在UserControl1上为MouseLeave和ControlChanged注册事件处理程序
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获得。
用户控制的事件处理程序http://friendfeed.s3.amazonaws.com/0d5a3968cb785625c8afb3974a8d84894c476291
#2
2
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; instead at the appropriate user control point, I perform validation through an override of ValidateChildren(). (This usually happens when the user clicks "Save" or "Next" on the user control, for me.)
你的场景相当复杂,但并非闻所未闻。 (我实际上在我目前的一个项目中的模式非常相似。)我接近它的方式是用户控件负责自己的验证。我不使用CausesValidation;而是在适当的用户控制点,我通过覆盖ValidateChildren()执行验证。 (这通常发生在用户点击用户控件上的“保存”或“下一步”时。)
Not being familiar with your user control UI, that may not be 100% the right approach for you. 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.
不熟悉您的用户控件UI,这可能不是100%正确的方法。但是,如果您引发自定义事件(可能使用指定是否执行验证的自定义EventArgs),您应该能够获得您想要的位置。
#3
2
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:
然后,在使用该控件的表单中,您可以像处理其他任何内容一样连接事件:
// 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
}
#4
1
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
您应该知道此过滤器无法处理正在发送的消息(未发布)。有趣的是,最有趣的事件(如点击事件)会被发布而不会被发送,因此可以通过此过滤器进行监控
#5
0
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分开。