none
MVVM RelayCommand Erklärung RRS feed

  • Frage

  • Hallo zusammen,

    ich bin neu hier in dem Forum und hätte gerne ein paar Fragen beantwortet. Und zwar geht es um das MVVM Prinzip, im speziellen um das RelayCommand. Ich hab da irgendwie noch nicht so wirklich den Durchblick, was da im Detail passiert.

    Ich habe mir ein Buch gekauft und ein Beispiel nachprogrammiert. Das Problem ist, auf einige Dinge wird überhaupt nicht eingegangen.

    Das was für mich persönlich sehr ärgerlich ist, dass ich das MVVM Prinzip eigentlich komplett verstanden habe. Auch was die einzelnen Klassen machen. Nur beim RelayCommand hapert es. Ich programmiere seit einem Jahr in etwa. Aber so mit Handlern, Actions ect, hab ich bisher nur wenig bis gar nix gemacht.

    Im speziellen sind es folgende Passagen, die ich nicht wirklich so verstehe. Eventuell kann es jemand mit eifachen Worten wiedergeben:

     public event EventHandler CanExecuteChanged
            {
                add
                {
                    CommandManager.RequerySuggested += value;
                    this.CanExecuteChangedInternal += value;
                }
                remove
                {
                    CommandManager.RequerySuggested -= value;
                    this.CanExecuteChangedInternal -= value;
                }
            }
    
    
    
    public void OnCanExecuteChanged()
            {
                EventHandler handler = this.CanExecuteChangedInternal;
    
                if (handler != null)
                {
                    handler.Invoke(this, EventArgs.Empty);
                }
            }
    
    
     public void Destroy()
            {
                this.canExecute = _ => false;
                this.execute = _ => { return; };
    
            }

    Und im speziellen verwirrt mich, dass es 2 Konstruktoren gibt. Warum ist das so?

    public RelayCommand(Action<object> execute) : this(execute, DefaultCanExecute)
            {
    
            }
    
            public RelayCommand(Action<object> execute, Predicate<object> canExecute)
            {
                if (execute == null)
                {
                    throw new ArgumentNullException("execute");
                }
                if (canExecute == null)
                {
                    throw new ArgumentNullException("canExecute");
                }
                this.execute = execute;
                this.canExecute = canExecute;
            }

    Vielen Dank im Voraus für die Antworten

    Samstag, 16. Februar 2019 11:20

Antworten

  • Hi,
    Steuerelemente wie z.B. Button oder MenuItem haben eine Command-Eigenschaft, der ein delegate zugewiesen werden kann, welcher die Adresse einer Methode enthält, die beim Click-Ereignis ausgeführt wird (Invoke). Eine direkte Zuweisung eines delegate im XAML ist beim Commandnicht möglich, im Gegensatz zu den Click-Ereignissen, da Command weitere Funktionalitäten kapselt. Wenn bei der Ausführung der Button als Objekt instanziiert wird, kann über einen Umweg solch ein delegate zugewiesen werden. Dazu kann die Instanz einer Klasse genutzt werden, die üblicherweise als RelayCommand bezeichnet wird. Diese Klasse implementiert das ICommand-Interface. Das ICommand-Interface hat 3 Member:

    1. void Execute(object parameter) - Methode, die beim Klick ausgeführt wird. Dem formalen Parameter "parameter" wird der Inhalt von CommandParameter übergeben.

    2. bool CanExecute(object parameter) - Funktions-Methode, die true oder false zurückgibt, womit der Enabled-Zustand des Steuerelementes beeinflusst wird. Dem formalen Parameter "parameter" wird der Inhalt von CommandParameter übergeben.

    3. event EventHandler CanExecuteChanged - wenn dieses Ereignis jemand abonniert hat, dann werden die Ereignismethoden im Falle der Änderung des Zustandes CanExecute aufgerufen.

    Wenn also ein Steuerelement anzuzeigen ist, wird durch die Laufzeitumgebung eine Instanz des Steuerelementes erzeugt. Beim Erzeugen der Instanz wird die an die Command-Eigenschaft gebundene Eigenschaft im ViewModel abgerufen. Diese Eigenschaft ist vom Typ ICommand und liefert eine Instanz der RelayCommand-Klasse. Die RelayCommand-Klasse kann 2 Konstruktoren haben. Der erste Konstruktor ist nur für Execute, der zweite Konstruktor sowohl für Execute als auch für CanExecute. Für Execute wird ein delegate vom Type Action übergeben, d.h. einen Verweis (Adresse) auf eine Methode ohne Rückgabewert (void in C#). Für CanExecute wird ein delegate vom Type Predicate übergeben, d.h. ein Verweis (Adresse) auf eine Methode, die true/false zurückgibt (bool f... in C#).

    Wenn das Steuerelement geklickt wurde, wird die Execute-Action ausgeführt. Wenn das Steuerelement refresht wird (PropertyChanged) oder auch CanExecuteChanged ausgelöst wurde, dann wird das Predicate "CanExecute" abgearbeitet und der Rückgabewert setzt die Enabled-Eigenschaft.

    Add und remove im Ereignishandler dienen dem Hinzufügen und Entfernen der Ereignishandler, die das Ereignis abonnieren, z.B. die Interna das Steuerelementes mit der gebundenen Command-Eigenschaft.


    Hier mal eine dazu passende Implementierung:

      /// <summary>
      /// Klasse, die es erlaubt, ein Objekt, welches eine Befehlsauführung (command) initiiert, von der Programmlogik zu trennen, in der die konkreten Befehle abgearbeitet werden.
      /// </summary>
      /// <typeparam name="T">Typ für die zu übergebenden Parameter</typeparam>
      public class RelayCommand<T> : ICommand
      {
        #region Fields
    
        private readonly Predicate<T> _canExecute;
        private readonly Action<T> _execute;
    
        #endregion
    
        #region Constructors
    
        /// <summary>
        /// Initalisieren einer neuen Instanz eines <see cref="DelegateCommand{T}".
        /// </summary>
        /// <param name="execute">Ein Delegate, der die Adresse der Methode referenziert, die ausgeführt wird, wenn Command aufgerufen wird.</param>
        /// <remarks><seealso cref="CanExecute"/> gibt immer true zurück.</remarks>
        public RelayCommand(Action<T> execute) : this(execute, null) { }
    
        /// <summary>
        /// Creates a new command.
        /// </summary>
        /// <param name="execute">Ein Delegate, der die Adresse der Methode referenziert, die ausgeführt wird, wenn Command aufgerufen wird.</param>
        /// <param name="canExecute">Ein Delegate, der die Adresse der Methode referenziert, die die Logik des Ausführungsstatus beinhaltet.</param>
        public RelayCommand(Action<T> execute, Predicate<T> canExecute)
        {
          if (execute == null) throw new ArgumentNullException("execute");
          _execute = execute; _canExecute = canExecute;
        }
    
        #endregion
    
        #region ICommand Members
    
        ///<summary>
        ///Methode, die aufgerufen wird, um den aktuellen Status zur Zulässigkeit der Ausführung zu ermitteln.
        ///</summary>
        ///<param name="parameter">Daten, die von command übermittelt werden. Wenn es keine Daten gibt, dann kann null übergeben werden.</param>
        ///<returns>
        ///true, wenn command ausgeführt werden kann, sonst false.
        ///</returns>
        public bool CanExecute(object parameter) => _canExecute == null ? true : _canExecute((T)parameter);
    
        ///<summary>
        ///Ereignis, mit welchem die Änderungen des Ausführungsstatus mitgeteilt wird.
        ///</summary>
        public event EventHandler CanExecuteChanged
        {
          add { CommandManager.RequerySuggested += value; }
          remove { CommandManager.RequerySuggested -= value; }
        }
    
        ///<summary>
        ///Methode, die aufgerufen wird, wenn command auszuführen ist.
        ///</summary>
        ///<param name="parameter">Daten, die von command übermittelt werden. Wenn es keine Daten gibt, dann kann null übergeben werden.</param>
        public void Execute(object parameter) => _execute((T)parameter);
    
        #endregion
      }


    --
    Viele Grüsse
    Peter Fleischer (ehem. MVP für Developer Technologies)
    Meine Homepage mit Tipps und Tricks


    Samstag, 16. Februar 2019 21:15