Frage

Ich habe einen Drittanbieter-Editor, der im Grunde ein Textfeld umfasst und eine Taste (die DevExpress ButtonEdit Kontrolle). Ich möchte einen bestimmten Tastendruck machen ( Alt + unten ) emulieren die Schaltfläche klicken. Um dieses Stolpern zu vermeiden Schreiben und darüber möchte ich einen generischen KeyUp Event-Handler machen, die das Button Ereignis auslösen werden. Leider es scheint nicht zu einem Verfahren, bei der Kontrolle zu sein, der das Button Ereignis auslöst, so ...

Wie erhöhe ich die Veranstaltung von einer externen Funktion über Reflexion?

War es hilfreich?

Lösung

Hier ist ein Demo-Generika (Fehlerprüfungen weggelassen) mit:

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

Andere Tipps

In der Regel können Sie nicht. Denken Sie an Veranstaltungen finden als grundsätzlich Paare von AddHandler / RemoveHandler Methoden (wie das ist im Grunde, was, was sie sind). Wie sie umgesetzt sind bis zu der Klasse. Die meisten WinForms-Steuerelemente verwenden EventHandlerList deren Umsetzung, aber Ihr Code wird sehr spröde, wenn es private Felder und Schlüssel beginnt zu holen.

Ist die ButtonEdit Steuerung eine OnClick Methode aussetzen, die Sie nennen könnte?

Fußnote: Eigentlich Ereignisse können haben "heben" Mitglieder, daher EventInfo.GetRaiseMethod. Dies ist jedoch nie von C # bevölkert und ich glaube nicht, dass es im Rahmen in der Regel auch nicht.

Sie können normalerweise nicht andere Klassen Ereignisse auslösen. Die Ereignisse werden als privater Delegat Feld, plus zwei Accessoren (add_event und remove_event) wirklich gespeichert.

Um es durch Reflexion zu tun, müssen Sie einfach auf den privaten Bereich delegieren finden, bekommen sie, dann rufen Sie es.

Ich schrieb eine Erweiterung Klassen, die INotifyPropertyChanged implementiert die RaisePropertyChange Methode zu injizieren, so kann ich es wie folgt verwendet werden:

this.RaisePropertyChanged(() => MyProperty);

, ohne das Verfahren in jeder Basisklasse zu implementieren. Ich empfand war es langsam, aber vielleicht kann der Quellcode jemand helfen.

Also hier ist es:

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

Ich entfernte einige Teile des ursprünglichen Codes, so sollte die Erweiterung so ist die Arbeit, ohne Verweise auf andere Teile meiner Bibliothek. Aber es ist nicht wirklich getestet.

P. S. Einige Teile des Codes wurde von jemand anderem geliehen. Schande über mich, dass ich vergessen, von wo ich es bekommen habe. : (

Wie sich herausstellt, könnte ich dies tun, und haben es nicht erkennen:

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

Aber wenn ich könnte nicht, dass ich in den Quellcode vertiefen müssen haben und die Methode finden, die das Ereignis auslöst.

Danke für die Hilfe, die alle.

Von VB.NET , das heißt, zwei Beiträge vor diesen werden Sie mit dem allgemeinen Ansatz bietet (zum Beispiel, würde ich auf die VB.NET einer für Inspiration sucht einen Typen nicht in der gleichen Klasse auf Referenzierung) :

 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");

Wenn Sie wissen, dass die Steuerung eine Taste, die Sie seine PerformClick() Methode aufrufen können. Ich habe ähnliches Problem für andere Veranstaltungen wie OnEnter, OnExit. Ich kann nicht die Ereignisse auslösen, wenn ich nicht will, einen neuen Typ für jeden Steuertyp abzuleiten.

Es scheint, dass der Code aus der akzeptierten Antwort von Wiebe Cnossen dieses Motto könnte vereinfacht werden:

((Delegate)source.GetType().GetField(eventName, BindingFlags.Instance | BindingFlags.NonPublic).GetValue(source))
    .DynamicInvoke(source, eventArgs);
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top