Ich habe gerade festgestellt, dass beim Ändern der gebundenen Eigenschaften in meinem ViewModel
(MVVM) von einem Hintergrund-Worker-Thread ich keine Ausnahmen bekomme und die Ansicht korrekt aktualisiert wird. Bedeutet das, dass ich sicher wpf databinding verwenden kann, das alle Änderungen im ViewModel
zum UI-Thread überträgt? Ich denke, ich habe irgendwo gelesen, dass man sicherstellen sollte (in der ViewModel
), dass INotifyPropertyChanged.PropertyChanged
auf dem UI-Thread ausgelöst wird. Hat sich das in 3.5 geändert?Ändert sich der WPF-Databinding-Marshall zum UI-Thread?
Antwort
Ja für Skalare, Nein für Sammlungen. Für Sammlungen benötigen Sie eine spezielle Sammlung, die für Sie marshallt, oder manuell über die Dispatcher
zum UI-Thread marschiert.
Sie haben möglicherweise gelesen, dass INotifyCollectionChanged.CollectionChanged
auf dem UI-Thread ausgelöst werden muss, da es von INotifyPropertyChanged.PropertyChanged
einfach nicht wahr ist. Im Folgenden finden Sie ein sehr einfaches Beispiel, das WPF marshalls Eigenschaftenänderungen für Sie prüft.
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:
<Window x:Class="WpfApplication1.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="300" Width="300">
<StackPanel>
<TextBox Text="{Binding Name}"/>
<TextBox Text="{Binding Address.Line1}"/>
</StackPanel>
</Window>
Ich glaube, dass mit 2.0 und früheren Inkarnationen von .NET würden Sie eine InvalidOperationException aufgrund erhalten haben Thread-Affinität bei der Ausführung des oben genannten Beispiels (Link von Bitbonk gebucht ist 2006 datiert).
Jetzt, mit 3.5, scheint WPF Hintergrund Thread-Eigenschaftsänderungen auf den Dispatcher für Sie zu marshallen.
Also, kurz gesagt, hängt davon ab, welche Version von .NET Sie anvisieren. Hoffentlich löscht das jede Verwirrung.
Einer meiner Kollegen Lab49'ers gebloggt es hier im Jahr 2007:
Aber 2.0 hatte kein WPF, meinst du 3.0? – bitbonk
Was komplexe skalare Typen? Was passiert, wenn ich in meinem ViewModel eine Eigenschaft vom Typ "MyCompositeObject" habe? – bitbonk
Ja, Änderungen an einer beliebigen Stelle entlang eines Eigenschaftspfads werden korrekt marshalliert, sofern sich in diesem Pfad keine Sammlung befindet. –
Das erste Mal, dass ich gelesen habe, dass propertychanges auf dem UI-Thread sein sollte, war hier: http://blogs.msdn.com/dancre/archive/2006/07/23/676300.aspx – bitbonk