32

基本的にテキストボックスとボタン(DevExpress ButtonEditコントロール)で構成されるサードパーティのエディターがあります。Alt特定のキーストローク( + Down)でボタンのクリックをエミュレートしたい。これを何度も書くことを避けるために、ButtonClickイベントを発生させる汎用のKeyUpイベントハンドラーを作成したいと思います。残念ながら、ButtonClickイベントを発生させるメソッドがコントロールにないようです。そのため...

リフレクションを介して外部関数からイベントを発生させるにはどうすればよいですか?

4

8 に答える 8

40

ジェネリックを使用したデモを次に示します (エラー チェックは省略されています)。

using System;
using System.Reflection;
static class Program {
  private class Sub {
    public event EventHandler<EventArgs> SomethingHappening;
  }
  internal static void Raise<TEventArgs>(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();
  }
}
于 2009-02-25T14:19:41.900 に答える
14

一般的に、あなたはできません。AddHandlerイベントは基本的に/メソッドのペアと考えてくださいRemoveHandler(基本的にはそれが何であるかということです)。それらがどのように実装されるかはクラス次第です。ほとんどのWinFormsコントロールはEventHandlerList実装として使用しますが、プライベートフィールドとキーのフェッチを開始すると、コードは非常に脆弱になります。

コントロールは、呼び出すことができるメソッドをButtonEdit公開していますか?OnClick

脚注:実際には、イベントには「レイズ」メンバーが含まれる可能性があるためEventInfo.GetRaiseMethod。ただし、これはC#によって入力されることはなく、一般的にフレームワークにも含まれているとは思いません。

于 2008-10-13T18:44:21.707 に答える
11

通常、別のクラスのイベントを発生させることはできません。イベントは、実際にはプライベートデリゲートフィールドと2つのアクセサー(add_eventおよびremove_event)として保存されます。

リフレクションを介してそれを行うには、プライベートデリゲートフィールドを見つけて取得し、それを呼び出す必要があります。

于 2008-10-13T18:43:31.287 に答える
10

クラスの拡張機能を作成しました。これは、RaisePropertyChange <T>メソッドを挿入するためにINotifyPropertyChangedを実装するため、次のように使用できます。

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
{
    /// <summary>
    /// Adds a RaisePropertyChanged method to objects implementing INotifyPropertyChanged.
    /// </summary>
    public static class NotifyPropertyChangeExtension
    {
        #region private fields

        private static readonly Dictionary<string, PropertyChangedEventArgs> eventArgCache = new Dictionary<string, PropertyChangedEventArgs>();
        private static readonly object syncLock = new object();

        #endregion

        #region the Extension's

        /// <summary>
        /// Verifies the name of the property for the specified instance.
        /// </summary>
        /// <param name="bindableObject">The bindable object.</param>
        /// <param name="propertyName">Name of the property.</param>
        [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));
        }

        /// <summary>
        /// Gets the property name from expression.
        /// </summary>
        /// <param name="notifyObject">The notify object.</param>
        /// <param name="propertyExpression">The property expression.</param>
        /// <returns>a string containing the name of the property.</returns>
        public static string GetPropertyNameFromExpression<T>(this INotifyPropertyChanged notifyObject, Expression<Func<T>> propertyExpression)
        {
            return GetPropertyNameFromExpression(propertyExpression);
        }

        /// <summary>
        /// Raises a property changed event.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="bindableObject">The bindable object.</param>
        /// <param name="propertyExpression">The property expression.</param>
        public static void RaisePropertyChanged<T>(this INotifyPropertyChanged bindableObject, Expression<Func<T>> propertyExpression)
        {
            RaisePropertyChanged(bindableObject, GetPropertyNameFromExpression(propertyExpression));
        }

        #endregion

        /// <summary>
        /// Raises the property changed on the specified bindable Object.
        /// </summary>
        /// <param name="bindableObject">The bindable object.</param>
        /// <param name="propertyName">Name of the property.</param>
        private static void RaisePropertyChanged(INotifyPropertyChanged bindableObject, string propertyName)
        {
            bindableObject.VerifyPropertyName(propertyName);
            RaiseInternalPropertyChangedEvent(bindableObject, GetPropertyChangedEventArgs(propertyName));
        }

        /// <summary>
        /// Raises the internal property changed event.
        /// </summary>
        /// <param name="bindableObject">The bindable object.</param>
        /// <param name="eventArgs">The <see cref="System.ComponentModel.PropertyChangedEventArgs"/> instance containing the event data.</param>
        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 });
        }

        /// <summary>
        /// Gets the property name from an expression.
        /// </summary>
        /// <param name="propertyExpression">The property expression.</param>
        /// <returns>The property name as string.</returns>
        private static string GetPropertyNameFromExpression<T>(Expression<Func<T>> 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;
        }

        /// <summary>
        /// Returns an instance of PropertyChangedEventArgs for the specified property name.
        /// </summary>
        /// <param name="propertyName">
        /// The name of the property to create event args for.
        /// </param>
        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コードの一部は他の誰かから借りたものです。恥ずかしい、どこから手に入れたのか忘れてしまった。:(

于 2010-07-22T19:25:56.340 に答える
7

リフレクションを介してイベントを発生させることから、 VB.NETでの答え、つまり、この投稿の2つ前の投稿は、一般的なアプローチを提供すると思います(たとえば、VB.NETの1つにインスピレーションを求めます。同じクラスにない型を参照する):

 public event EventHandler<EventArgs> MyEventToBeFired;

    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");
于 2008-10-13T18:42:51.943 に答える
6

結局のところ、私はこれを行うことができましたが、それを認識していませんでした:

buttonEdit1.Properties.Buttons[0].Shortcut = new DevExpress.Utils.KeyShortcut(Keys.Alt | Keys.Down);

しかし、それができなければ、ソース コードを詳しく調べて、イベントを発生させるメソッドを見つける必要がありました。

助けてくれてありがとう。

于 2008-10-14T14:40:45.583 に答える
5

コントロールがボタンであることがわかっている場合は、そのPerformClick()メソッドを呼び出すことができます。、などの他のイベントでも同様の問題が発生しOnEnterますOnExit。コントロールタイプごとに新しいタイプを派生させたくない場合、これらのイベントを発生させることはできません。

于 2009-02-25T12:44:18.370 に答える