it-swarm.com.de

Aktualisieren Sie den WPF-Befehl

Weiß jemand, wie ich CanExecute zwingen kann, auf einen benutzerdefinierten Befehl (Josh Smiths RelayCommand ) gerufen zu werden?

Normalerweise wird CanExecute aufgerufen, wenn auf der Benutzeroberfläche Interaktionen auftreten. Wenn ich auf etwas klicke, werden meine Befehle aktualisiert. 

Ich habe eine Situation, in der die Bedingung für CanExecute durch einen Timer im Hintergrund ein-/ausgeschaltet wird. Da dies nicht von der Benutzerinteraktion gesteuert wird, wird CanExecute erst aufgerufen, wenn der Benutzer mit der Benutzeroberfläche interagiert. Das Endergebnis ist, dass meine Button aktiviert/deaktiviert ist, bis der Benutzer darauf klickt. Nach dem Klick wird es korrekt aktualisiert. Manchmal scheint die Variable Button aktiviert zu sein. Wenn der Benutzer jedoch klickt, wird er deaktiviert und nicht ausgelöst.

Wie kann ich eine Aktualisierung im Code erzwingen, wenn der Timer die Eigenschaft ändert, die CanExecute betrifft? Ich habe versucht, PropertyChanged (INotifyPropertyChanged) auf die Eigenschaft abzufeuern, die CanExecute betrifft, aber das hat nicht geholfen.

Beispiel XAML:

<Button Content="Button" Command="{Binding Cmd}"/>

Beispielcode hinter:

private ICommand m_cmd;
public ICommand Cmd
{
    if (m_cmd == null)
        m_cmd = new RelayCommand(
            (param) => Process(),
            (param) => EnableButton);

    return m_cmd;
}

// Gets updated from a timer (not direct user interaction)
public bool EnableButton { get; set; }
68
Josh G
100
Kent Boogaart

Ich war vor langer Zeit auf CommandManager.InvalidateRequerySuggested () aufmerksam und habe es verwendet, aber es funktionierte manchmal nicht für mich. Ich habe endlich herausgefunden, warum das so war! Obwohl es nicht wie andere Aktionen ausgelöst wird, MÜSSEN Sie es im Haupt-Thread aufrufen. 

Das Aufrufen eines Hintergrund-Threads scheint zu funktionieren, aber die Benutzeroberfläche bleibt manchmal deaktiviert. Ich hoffe wirklich, dass dies jemandem hilft und die Stunden, die ich gerade verschwendet habe, rettet.

28
SilverSideDown

Eine Problemumgehung bindet IsEnabled an eine Eigenschaft:

<Button Content="Button" Command="{Binding Cmd}" IsEnabled="{Binding Path=IsCommandEnabled}"/>

und implementieren Sie diese Eigenschaft dann in Ihrem ViewModel. Dies macht es auch für UnitTesting etwas einfacher, mit den Eigenschaften zu arbeiten, anstatt mit Befehlen zu sehen, ob der Befehl zu einem bestimmten Zeitpunkt ausgeführt werden kann.

Ich persönlich finde es bequemer.

16
tridy.net

Wahrscheinlich wird diese Variante zu Ihnen passen:

 public interface IRelayCommand : ICommand
{
    void UpdateCanExecuteState();
}

Implementierung:

 public class RelayCommand : IRelayCommand
{
    public event EventHandler CanExecuteChanged;


    readonly Predicate<Object> _canExecute = null;
    readonly Action<Object> _executeAction = null;

   public RelayCommand( Action<object> executeAction,Predicate<Object> canExecute = null)
    {
        _canExecute = canExecute;
        _executeAction = executeAction;
    }


    public bool CanExecute(object parameter)
    {
       if (_canExecute != null)
            return _canExecute(parameter);
        return true;
    }

    public void UpdateCanExecuteState()
    {
        if (CanExecuteChanged != null)
            CanExecuteChanged(this, new EventArgs());
    }



    public void Execute(object parameter)
    {
        if (_executeAction != null)
            _executeAction(parameter);
        UpdateCanExecuteState();
    }
}

Mit einfachen:

public IRelayCommand EditCommand { get; protected set; }
...
EditCommand = new RelayCommand(EditCommandExecuted, CanEditCommandExecuted);

 protected override bool CanEditCommandExecuted(object obj)
    {
        return SelectedItem != null ;
    }

    protected override void EditCommandExecuted(object obj)
    {
        // Do something
    }

   ...

    public TEntity SelectedItem
    {
        get { return _selectedItem; }
        set
        {
            _selectedItem = value;

            //Refresh can execute
            EditCommand.UpdateCanExecuteState();

            RaisePropertyChanged(() => SelectedItem);
        }
    }

XAML:

<Button Content="Edit" Command="{Binding EditCommand}"/>
6
R.Titov

Danke Jungs für die Tipps. Hier ist ein bisschen Code, wie man diesen Aufruf von einem BG-Thread an den UI-Thread marshallt:

private SynchronizationContext syncCtx; // member variable

Im Konstruktor:

syncCtx = SynchronizationContext.Current;

Im Hintergrund-Thread, um das Requery auszulösen:

syncCtx.Post( delegate { CommandManager.InvalidateRequerySuggested(); }, null );

Hoffentlich hilft das.

- Michael

4
Michael Kennedy

Um nur einen einzelnen GalaSoft.MvvmLight.CommandWpf.RelayCommand zu aktualisieren, können Sie verwenden 

mycommand.RaiseCanExecuteChanged();

und für mich habe ich eine Erweiterungsmethode erstellt:

public static class ExtensionMethods
    {
        public static void RaiseCanExecuteChangedDispatched(this RelayCommand cmd)
        {
            System.Windows.Application.Current.Dispatcher.Invoke(DispatcherPriority.Normal, new Action(() => { cmd.RaiseCanExecuteChanged(); }));
        }

        public static void RaiseCanExecuteChangedDispatched<T>(this RelayCommand<T> cmd)
        {
            System.Windows.Application.Current.Dispatcher.Invoke(DispatcherPriority.Normal, new Action(() => { cmd.RaiseCanExecuteChanged(); }));
        }
    }
0
Apfelkuacha