Impedire l’utilizzo di Dispatcher.Invoke nel codice WPF

Sono un programmatore di web e back-end per natura. Normalmente cerco di fare programmi Windows. Ora devo creare un client WPF.

Ho un compito in background che solleva un evento ogni volta. (Funziona come un poller e quando i criteri vengono soddisfatti viene generato un evento). Noob as I am Ho scritto questo codice che è stato allegato all’evento per aggiornare l’interfaccia utente.

private void IsDisconnectedEvent() { UserWindow.Visibility = Visibility.Hidden; DisconnectWindow.Visibility = Visibility.Visible; } 

Questo dà un’eccezione perché non sono sullo stesso thread. Dopo alcuni googling ho scoperto che dovrei cambiare il codice con:

  private void IsDisconnectedEvent() { Dispatcher.Invoke(() => { UserWindow.Visibility = Visibility.Hidden; DisconnectWindow.Visibility = Visibility.Visible; }); } 

Funziona, ma questo non è l’unico evento e quindi rende il mio codice orribile brutto. Ci sono modi migliori per farlo?

A proposito di questo:

Funziona, ma questo non è l’unico evento e quindi rende il mio codice orribile brutto

, il tuo codice basato su WPF sarà estremamente orribile a meno che tu non comprenda e abbracci il WPF Mentality .

In sostanza, tutte le interazioni tra la logica personalizzata (logica di business AKA o logica dell’applicazione) e l’interfaccia utente WPF devono manifestarsi sotto forma di dichiarazione dei dati dichiarativa rispetto all’approccio tradizionale imperativo.

Ciò significa che non dovrebbe esserci nulla di simile a questo:

 UserWindow.Visibility = Visibility.Hidden; 

ovunque nel tuo codice, semplicemente perché l’introduzione di cose del genere rende il tuo codice dipendente dall’interfaccia utente e quindi solo eseguibile sul thread dell’interfaccia utente.

Invece, l’approccio WPF sarebbe quello di dichiarare in modo dichiarativo la proprietà Visibility dell’elemento UI ( IN XAML ) a una proprietà bool pertinente che è ansible operare dall’esterno, in questo modo:

    

Quindi, sarà necessario creare una class pertinente che contenga le proprietà a cui l’UI si aspetta di associare. Questo è chiamato ViewModel .

Si noti che per supportare correttamente DataBinding WPF bidirezionale, i ViewModels devono implementare l’interfaccia INotifyPropertyChanged .

In tal caso, è anche conveniente avere l’evento PropertyChanged da quell’interfaccia inserita nel thread dell’interfaccia utente , in modo da non dover più preoccuparsi di impostare le proprietà ViewModel utilizzando il Dispatcher .

Quindi il nostro primo passo è quello di avere tutti i nostri ViewModels ereditati da una class come questa:

(tratto da questa risposta ):

 public class PropertyChangedBase:INotifyPropertyChanged { public event PropertyChangedEventHandler PropertyChanged; protected virtual void OnPropertyChanged(string propertyName) { //Raise the PropertyChanged event on the UI Thread, with the relevant propertyName parameter: Application.Current.Dispatcher.BeginInvoke((Action) (() => { PropertyChangedEventHandler handler = PropertyChanged; if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName)); })); } } 

Una volta che abbiamo UserWindow la nostra notifica di modifica delle proprietà al thread dell’interfaccia utente , possiamo procedere alla creazione di un ViewModel pertinente adatto, in questo caso, alle UserWindow e alle aspettative DataBinding:

 public class UserViewModel: PropertyChangedBase { private bool _showUserWindow; public bool ShowUserWindow { get {return _showUserWindow; } set { _showUserWindow = value; OnPropertyChanged("ShowUserWindow"); //This is important!!! } } } 

Infine, è necessario impostare DataContext della finestra su un’istanza del corrispondente ViewModel. Un modo semplice per farlo è nel costruttore di Window:

 public UserWindow() //Window's Constructor { InitializeComponent(); //this is required. DataContext = new UserViewModel(); //here we set the DataContext } 

Come puoi vedere in questo esempio, non c’è letteralmente bisogno di manipolare le proprietà dell’elemento UI nel codice procedurale. Questo è utile non solo perché risolve i problemi di affinità del thread (perché ora puoi impostare la proprietà ShowUserWindow da qualsiasi thread), ma anche perché rende ViewModels e la logica completamente disaccoppiati dall’interfaccia utente e quindi testabili e più scalabili.

Questo stesso concetto si applica a TUTTO in WPF.

Un dettaglio che ho bisogno di menzionare è che sto facendo uso di una tecnica di combinazione di MarkupExtension e IValueConverter per ridurre il boilerplate XAML coinvolto nell’uso di Converters.

Puoi leggere ulteriori informazioni al riguardo nel link e anche nella pagina MSDN DataBinding linkata sopra.

Fammi sapere se hai bisogno di ulteriori dettagli.