Ho appena notato che quando si cambiano le proprietà associate nel mio ViewModel
(MVVM) da un thread di lavoro in background non ottengo eccezioni e la vista viene aggiornata correttamente. Questo significa che posso tranquillamente fare affidamento sul databinding di wpf che esegue il marshalling di tutte le modifiche nel ViewModel
al thread UI? Penso di aver letto da qualche parte che si dovrebbe essere sicuri (nel ViewModel
) che INotifyPropertyChanged.PropertyChanged
sia INotifyPropertyChanged.PropertyChanged
sul thread dell’interfaccia utente. Questo è cambiato in 3.5 o qualcosa del genere?
Sì per gli scalari, no per le raccolte. Per le raccolte, avrai bisogno di una raccolta specializzata che effettua il marshal per te o esegui manualmente il marshalling sul thread dell’interfaccia utente tramite il Dispatcher
.
Potresti aver letto che INotifyCollectionChanged.CollectionChanged
deve essere INotifyCollectionChanged.CollectionChanged
sul thread dell’interfaccia utente, perché semplicemente non è vero di INotifyPropertyChanged.PropertyChanged
. Di seguito è riportato un esempio molto semplice che dimostra le modifiche alle proprietà dei marshall dei WPF.
Window1.xaml.cs :
using System.ComponentModel; using System.Threading; using System.Windows; namespace WpfApplication1 { public partial class Window1 : Window { private CustomerViewModel _customerViewModel; public Window1() { InitializeComponent(); _customerViewModel = new CustomerViewModel(); DataContext = _customerViewModel; var thread = new Thread((ThreadStart)delegate { while (true) { Thread.Sleep(2000); //look ma - no marshalling! _customerViewModel.Name += "Appended"; _customerViewModel.Address.Line1 += "Appended"; } }); thread.Start(); } } public abstract class ViewModel : INotifyPropertyChanged { public event PropertyChangedEventHandler PropertyChanged; protected void OnPropertyChanged(string propertyName) { var handler = PropertyChanged; if (handler != null) { handler(this, new PropertyChangedEventArgs(propertyName)); } } } public class CustomerViewModel : ViewModel { private string _name; private AddressViewModel _address = new AddressViewModel(); public string Name { get { return _name; } set { if (_name != value) { _name = value; OnPropertyChanged("Name"); } } } public AddressViewModel Address { get { return _address; } } } public class AddressViewModel : ViewModel { private string _line1; public string Line1 { get { return _line1; } set { if (_line1 != value) { _line1 = value; OnPropertyChanged("Line1"); } } } } }
Window1.xaml :
Credo che con 2.0 e precedenti versioni di .NET avresti ricevuto un’eccezione InvalidOperationException a causa dell’affinità del thread durante l’esecuzione dell’esempio sopra citato (il collegamento pubblicato da bitbonk è datato 2006).
Ora, con 3.5, WPF sembra effettuare il marshalling delle modifiche alle proprietà del thread in background sul dispatcher.
Quindi, in breve, dipende da quale versione di .NET stai mirando. Spero che questo chiarisca ogni confusione.
Uno dei miei colleghi Lab49’ers ne ha parlato qui nel 2007: