WPF数据绑定是否将更改调度到UI线程?

21

我刚刚注意到,当我在后台工作线程中更改我的ViewModel(MVVM)中的绑定属性时,我没有收到任何异常,并且视图正确更新。这是否意味着我可以放心地依赖于wpf数据绑定将ViewModel中的所有更改传递到UI线程?我想我曾经在某个地方读到过,应该确保在ViewModel中(on the UI thread)触发INotifyPropertyChanged.PropertyChanged。这在3.5版中是否已更改或其他什么情况?

2个回答

16

对于标量是可以的,对于集合则不行。对于集合,你需要使用专门的集合来进行组合处理,或者手动通过 Dispatcher 将其转换为 UI 线程。

您可能已经了解到INotifyCollectionChanged.CollectionChanged必须在UI线程上触发,因为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>

是的,除非路径中存在集合,否则沿着属性路径的任何更改都将被正确地编组。 - Kent Boogaart
我第一次读到属性更改应该在UI线程上进行的建议是在这里:http://blogs.msdn.com/dancre/archive/2006/07/23/676300.aspx。 - bitbonk
这是一个将“CollectionChanged”事件调度到UI线程的集合示例:http://tomlev2.wordpress.com/2009/04/17/wpf-binding-to-an-asynchronous-collection/ - Thomas Levesque
任何内置的集合类型都可以处理线程封送吗?(例如ObservableCollection - Merlyn Morgan-Graham
1
@ThomasLevesque,您更改了网站的正确链接:https://thomaslevesque.com/2009/04/17/wpf-binding-to-an-asynchronous-collection/。 - Piotr Golacki
显示剩余3条评论

1

我认为,在2.0和之前的.NET版本中,因线程亲和性而执行上述示例会导致InvalidOperationException异常(由bitbonk张贴的链接是2006年的)。

现在,使用3.5,WPF似乎会将后台线程属性更改代理到调度程序。

所以,简言之,这取决于您正在针对的.NET版本。希望这能消除任何困惑。

我的一个Lab49同事在2007年在此处进行了博客记录:

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


但是2.0没有任何WPF,你是指3.0吗? - bitbonk

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接