Изменяет ли атрибут привязки WPF маркер к потоку пользовательского интерфейса?

Я только заметил, что при изменении связанных свойств в моем ViewModel (MVVM) из потока рабочего фона я не получаю никаких исключений, и представление обновляется правильно. Означает ли это, что я могу смело полагаться на привязку данных wpf, сортируя все изменения в ViewModel к потоку пользовательского интерфейса? Я думаю, что я где-то читал, что нужно убедиться (в ViewModel), что INotifyPropertyChanged.PropertyChanged запускается в потоке пользовательского интерфейса. Это изменилось в 3.5 или что-то в этом роде?

Ответы

Ответ 1

Да для скаляров, нет для коллекций. Для коллекций вам понадобится специализированная коллекция, которая будет маршалами для вас, или вручную настройте сам пользовательский интерфейс через Dispatcher.

Возможно, вы прочитали, что INotifyCollectionChanged.CollectionChanged должен запускать поток пользовательского интерфейса, потому что это просто неверно для INotifyPropertyChanged.PropertyChanged. Ниже представлен очень простой пример, который доказывает для вас изменения свойств маршалов 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:

<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>

Ответ 2

Я считаю, что с 2.0 и предыдущими воплощениями .NET вы получили бы InvalidOperationException из-за близости потока при выполнении вышеупомянутого примера (ссылка, опубликованная bitbonk датирована 2006 годом).

Теперь, с 3.5, WPF, похоже, перенаправляет изменения свойств фонового потока на диспетчер для вас.

Итак, короче, зависит, какую версию .NET вы планируете. Надеюсь, это устранит любую путаницу.

Один из моих коллег Lab49'ers рассказал об этом здесь в 2007 году:

http://blog.lab49.com/archives/1166