我有一个第三方编辑器,基本上包括一个文本框和一个按钮(DevExpress ButtonEdit控件).我想做一个特定的击键(Alt+ Down)模拟点击按钮.为了避免一遍又一遍地写这个,我想创建一个通用的KeyUp事件处理程序,它将引发ButtonClick事件.不幸的是,控件中似乎没有引发ButtonClick事件的方法,所以...
如何通过反射从外部函数引发事件?
这是使用泛型的演示(省略错误检查):
using System; using System.Reflection; static class Program { private class Sub { public event EventHandlerSomethingHappening; } internal static void Raise (this object source, string eventName, TEventArgs eventArgs) where TEventArgs : EventArgs { var eventDelegate = (MulticastDelegate)source.GetType().GetField(eventName, BindingFlags.Instance | BindingFlags.NonPublic).GetValue(source); if (eventDelegate != null) { foreach (var handler in eventDelegate.GetInvocationList()) { handler.Method.Invoke(handler.Target, new object[] { source, eventArgs }); } } } public static void Main() { var p = new Sub(); p.Raise("SomethingHappening", EventArgs.Empty); p.SomethingHappening += (o, e) => Console.WriteLine("Foo!"); p.Raise("SomethingHappening", EventArgs.Empty); p.SomethingHappening += (o, e) => Console.WriteLine("Bar!"); p.Raise("SomethingHappening", EventArgs.Empty); Console.ReadLine(); } }
一般来说,你不能.将事件看作基本上成对AddHandler
/ RemoveHandler
方法(因为它基本上就是它们是什么).它们如何实施取决于课程.大多数WinForms控件都EventHandlerList
用作它们的实现,但是如果它开始获取私有字段和键,你的代码将非常脆弱.
ButtonEdit
控件是否公开了一个OnClick
可以调用的方法?
脚注:实际上,事件可以 "提升"成员,因此EventInfo.GetRaiseMethod
.但是,这从未被C#填充过,我也不相信它在框架中也是如此.
你通常不能提出另一个类事件.事件实际上存储为私有委托字段,以及两个访问器(add_event和remove_event).
要通过反射来完成它,您只需要找到私有委托字段,获取它,然后调用它.
我写了一个类的扩展,它实现了INotifyPropertyChanged以注入RaisePropertyChange
this.RaisePropertyChanged(() => MyProperty);
没有在任何基类中实现该方法.对于我的使用它是慢,但也许源代码可以帮助某人.
所以这里是:
using System; using System.Collections.Generic; using System.ComponentModel; using System.Diagnostics; using System.Linq.Expressions; using System.Reflection; using System.Globalization; namespace Infrastructure { ////// Adds a RaisePropertyChanged method to objects implementing INotifyPropertyChanged. /// public static class NotifyPropertyChangeExtension { #region private fields private static readonly DictionaryeventArgCache = new Dictionary (); private static readonly object syncLock = new object(); #endregion #region the Extension's /// /// Verifies the name of the property for the specified instance. /// /// The bindable object. /// Name of the property. [Conditional("DEBUG")] public static void VerifyPropertyName(this INotifyPropertyChanged bindableObject, string propertyName) { bool propertyExists = TypeDescriptor.GetProperties(bindableObject).Find(propertyName, false) != null; if (!propertyExists) throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, "{0} is not a public property of {1}", propertyName, bindableObject.GetType().FullName)); } ////// Gets the property name from expression. /// /// The notify object. /// The property expression. ///a string containing the name of the property. public static string GetPropertyNameFromExpression(this INotifyPropertyChanged notifyObject, Expression > propertyExpression) { return GetPropertyNameFromExpression(propertyExpression); } /// /// Raises a property changed event. /// ////// The bindable object. /// The property expression. public static void RaisePropertyChanged (this INotifyPropertyChanged bindableObject, Expression > propertyExpression) { RaisePropertyChanged(bindableObject, GetPropertyNameFromExpression(propertyExpression)); } #endregion /// /// Raises the property changed on the specified bindable Object. /// /// The bindable object. /// Name of the property. private static void RaisePropertyChanged(INotifyPropertyChanged bindableObject, string propertyName) { bindableObject.VerifyPropertyName(propertyName); RaiseInternalPropertyChangedEvent(bindableObject, GetPropertyChangedEventArgs(propertyName)); } ////// Raises the internal property changed event. /// /// The bindable object. /// Theinstance containing the event data. private static void RaiseInternalPropertyChangedEvent(INotifyPropertyChanged bindableObject, PropertyChangedEventArgs eventArgs) { // get the internal eventDelegate var bindableObjectType = bindableObject.GetType(); // search the base type, which contains the PropertyChanged event field. FieldInfo propChangedFieldInfo = null; while (bindableObjectType != null) { propChangedFieldInfo = bindableObjectType.GetField("PropertyChanged", BindingFlags.Instance | BindingFlags.NonPublic); if (propChangedFieldInfo != null) break; bindableObjectType = bindableObjectType.BaseType; } if (propChangedFieldInfo == null) return; // get prop changed event field value var fieldValue = propChangedFieldInfo.GetValue(bindableObject); if (fieldValue == null) return; MulticastDelegate eventDelegate = fieldValue as MulticastDelegate; if (eventDelegate == null) return; // get invocation list Delegate[] delegates = eventDelegate.GetInvocationList(); // invoke each delegate foreach (Delegate propertyChangedDelegate in delegates) propertyChangedDelegate.Method.Invoke(propertyChangedDelegate.Target, new object[] { bindableObject, eventArgs }); } /// /// Gets the property name from an expression. /// /// The property expression. ///The property name as string. private static string GetPropertyNameFromExpression(Expression > propertyExpression) { var lambda = (LambdaExpression)propertyExpression; MemberExpression memberExpression; if (lambda.Body is UnaryExpression) { var unaryExpression = (UnaryExpression)lambda.Body; memberExpression = (MemberExpression)unaryExpression.Operand; } else memberExpression = (MemberExpression)lambda.Body; return memberExpression.Member.Name; } /// /// Returns an instance of PropertyChangedEventArgs for the specified property name. /// /// /// The name of the property to create event args for. /// private static PropertyChangedEventArgs GetPropertyChangedEventArgs(string propertyName) { PropertyChangedEventArgs args; lock (NotifyPropertyChangeExtension.syncLock) { if (!eventArgCache.TryGetValue(propertyName, out args)) eventArgCache.Add(propertyName, args = new PropertyChangedEventArgs(propertyName)); } return args; } } }
我删除了原始代码的一些部分,因此扩展应该按原样工作,而不引用我的库的其他部分.但它并没有真正经过测试.
PS代码的某些部分是从别人那里借来的.对我感到羞耻,我忘记了从哪里得到它.:(
从通过反思提升事件,虽然我认为VB.NET中的答案,即在此之前的两个帖子将为您提供通用方法(例如,我将看一下VB.NET的灵感来源于引用不在同一类中的类型):
public event EventHandlerMyEventToBeFired; public void FireEvent(Guid instanceId, string handler) { // Note: this is being fired from a method with in the same // class that defined the event (that is, "this"). EventArgs e = new EventArgs(instanceId); MulticastDelegate eventDelagate = (MulticastDelegate)this.GetType().GetField(handler, System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic).GetValue(this); Delegate[] delegates = eventDelagate.GetInvocationList(); foreach (Delegate dlg in delegates) { dlg.Method.Invoke(dlg.Target, new object[] { this, e }); } } FireEvent(new Guid(), "MyEventToBeFired");
事实证明,我可以做到这一点并没有意识到:
buttonEdit1.Properties.Buttons[0].Shortcut = new DevExpress.Utils.KeyShortcut(Keys.Alt | Keys.Down);
但如果我不能,我将不得不深入研究源代码并找到引发事件的方法.
谢谢你们的帮助.
如果您知道控件是一个按钮,则可以调用其PerformClick()
方法.我有其他事件像类似的问题OnEnter
,OnExit
.如果我不想为每种控件类型派生新类型,则无法引发这些事件.