当我在VB中使用AddHandler为Click事件添加自己的方法时:
AddHandler Button.Click, AddressOf myButton_Click
我发现我的代码在Button_Click事件的其他事件处理程序之后才执行。有没有办法将我的事件处理程序插入到其他事件的前面,以便它首先执行?
我将此问题标记为C#和VB,请随意使用任何一种语言提出建议。
谢谢!
当我在VB中使用AddHandler为Click事件添加自己的方法时:
AddHandler Button.Click, AddressOf myButton_Click
我发现我的代码在Button_Click事件的其他事件处理程序之后才执行。有没有办法将我的事件处理程序插入到其他事件的前面,以便它首先执行?
我将此问题标记为C#和VB,请随意使用任何一种语言提出建议。
谢谢!
不太容易。
话虽如此,不要这样做。你的代码不应该关心它被调用的顺序——它只应该关心所涉及的按钮是否被点击。所有的处理程序,包括你的处理程序,都将被执行。如果顺序很重要,你应该重新考虑你的设计,并使用其他机制来控制它。
private List<EventHandler> MyHandlers = new List<EventHandler>();
private void MasterClickHandler(object sender, EventArgs e)
{
foreach(var handler in MyHandlers)
handler(sender, e);
}
public event EventHandler MyControlButtonClick
{
add { MyHandlers.Add(value); }
remove { MyHandlers.Remove(value); }
}
public void InsertButtonClickHandler(EventHandler handler)
{
MyHandlers.Insert(handler,0); //calling this to add a handler puts the handler up front
}
...
myForm.MyControl.Click += MasterClickHandler;
这更多是VB.NET的实现细节,它有一种使用WithEvents和Handles关键字处理事件的替代方法。使用Handles的事件处理程序会被自动生成的代码在窗体构造函数中订阅。该代码将在任何您的代码之前运行,包括InitializeComponent或自定义AddHandler语句。因此,它总是首先运行。
确保您的代码始终首先运行是可能的。从Button派生自己的类并覆盖OnClick方法:
Public Class MyButton
Inherits Button
Protected Overrides Sub OnClick(ByVal e As System.EventArgs)
'' Do your stuff here
''....
'' Other event handlers will run now:
MyBase.OnClick(e)
End Sub
End Class
我曾经遇到过类似的问题。
我有一个事件:两个不同的对象都在监听。而我必须确保第一个对象的事件会在第二个对象之前被调用。
这是我的解决方案(使用C#):
public class Screen
{
public event EventHandler Closed;
public event EventHandler ScreenRemoved;
protected virtual void OnClosed()
{
if (Closed != null)
{
Closed.Invoke(this, EventArgs.Empty);
}
OnScreenRemoved();
}
protected virtual void OnScreenRemoved()
{
if (ScreenRemoved != null)
{
ScreenRemoved.Invoke(this, EventArgs.Empty);
}
}
}
这样,我想要首先被调用的所有事件都可以使用这种方式:
m_Screen.Closed += Screen_Closed;
而且对于所有的事件,我希望最后被调用:
m_Screen.ScreenRemoved += new EventHandler(Screen_Closed);
*添加事件的两种方式是一样的, 带和不带 "new EventHandler()"
希望我的回答有所帮助。
/// <summary>
/// Method, that can add delegate callback on the start of invocation list. So when the event is fired, this callback is called first.
/// </summary>
/// <param name="instance">instance of object with desired event</param>
/// <param name="eventName">name of the event on <see cref="instance"/></param>
/// <param name="addHandler">delegate for adding event callback</param>
/// <param name="removeHandler">delegate for removing event callback</param>
/// <param name="handler">desired callback, that will be called first</param>
private static void PrependExecutedRoutedCallbackTo<T>(
T instance,
string eventName,
Action<T, ExecutedRoutedEventHandler> addHandler,
Action<T, ExecutedRoutedEventHandler> removeHandler,
ExecutedRoutedEventHandler handler
)
where T : class
{
var classType = typeof(T);
// compiler automaticly creates backing field with event name - searching for it
FieldInfo eventField = classType.GetField(eventName, BindingFlags.GetField | BindingFlags.NonPublic | BindingFlags.Instance);
ExecutedRoutedEventHandler eventDelegate = (ExecutedRoutedEventHandler)eventField.GetValue(instance);
// if the field is null, it means no callbacks are registered - handler can be easily added
if (eventDelegate == null)
{
addHandler(instance, handler);
return;
}
// Here is some magic - removing all callbacks, adding desired callback on the begining and adding callbacks back
// getting current registered callbacks
var delegates = eventDelegate.GetInvocationList().OfType<ExecutedRoutedEventHandler>().ToList();
// removing all of them
foreach (var del in delegates)
{
removeHandler(instance, del);
}
// adding my special callback
addHandler(instance, handler);
// returning back all original callbacks
foreach (var del in delegates)
{
addHandler(instance, del);
}
}
使用示例:
//binding.Executed += BeforeOperationExecute;
// same as this ^^^^^^, but the callback is added as first to invocation list
PrependExecutedRoutedCallbackTo(
binding,
nameof(CommandBinding.Executed),
(b, c) => { b.Executed += c; },
(b, c) => { b.Executed -= c; },
BeforeOperationExecute
);