在WPF中的默认构造函数中设置DataContext的顺序

时间:2017-11-26 11:43:01

标签: c# wpf user-interface data-binding datacontext

我尝试在WPF中的默认构造函数中设置DataContext属性的顺序。

<StackPanel>
        <ListBox ItemsSource="{Binding MyItems, PresentationTraceSources.TraceLevel=High}"></ListBox>
        <TextBlock Text="{Binding SomeText}"></TextBlock>
        <TextBlock Text="{Binding SomeNum}"></TextBlock>
        <TextBlock Text="{Binding Path=Person.Name}"></TextBlock>
        <ListBox ItemsSource="{Binding Path=PersonList}">
            <ListBox.ItemTemplate>
                <DataTemplate>
                    <TextBlock Text="{Binding Name}"></TextBlock>
                </DataTemplate>
            </ListBox.ItemTemplate>
        </ListBox>
    </StackPanel>

1)在 InitializeComponent 方法之前设置DataContext

public partial class MainWindow : Window, INotifyPropertyChanged
    {
        private string someText = "Default text";

        public List<string> MyItems { get; set; }
        public List<Person> PersonList { get; set; }

        public Person Person { get; set; }
        public int SomeNum { get; set; }

        public string SomeText
        {
            get
            {
                return someText;
            }

            set
            {
                someText = value;
                OnPropertyChanged("SomeText");
            }
        }

        public MainWindow()
        {
            this.DataContext = this;

            MyItems = new List<string>();
            PersonList = new List<Person>();
            Person = new Person();

            InitializeComponent();

            /*These changes are not reflected in the UI*/
            SomeNum = 7;
            Person.Name = "Andy";

            /*Changes reflected with a help of INotifyPropertyChanged*/
            SomeText = "Modified Text";

            /* Changes to the Lists are reflected in the UI */
            MyItems.Add("Red");
            MyItems.Add("Blue");
            MyItems.Add("Green");
            MyItems[0] = "Golden";

            PersonList.Add(new Person() { Name = "Xavier" });
            PersonList.Add(new Person() { Name = "Scott" });
            PersonList[0].Name = "Jean";
        }

        public event PropertyChangedEventHandler PropertyChanged;

        protected void OnPropertyChanged(string name)
        {
            PropertyChangedEventHandler handler = PropertyChanged;
            if (handler != null)
            {
                handler(this, new PropertyChangedEventArgs(name));
            }
        }
    }

public class Person
    {
        public string Name { get; set; } = "Default Name";
    }

调用InitializeComponent方法后,除了使用INotifyPropertyChanged的属性之外,属性值不会反映在UI中。到目前为止,一切都很清楚。

但是我注意到列表项的更改也反映在UI中。怎么会?

我一直认为,为了反映从集合中添加/删除,我需要ObservableCollection并在列表对象上实现INotifyPropertyChanged以检测这些对象的修改。这是什么意思?

2)在 InitializeComponent 方法之后设置DataContext

为什么在InitializeComponent之后设置一个DataContext属性是MVVM的一个坏习惯?你能更彻底地描述它还是给出一个简单的代码示例?

2 个答案:

答案 0 :(得分:1)

  

我一直认为,为了反映从集合中添加/删除我需要ObservableCollection<T>并在列表对象上实现INotifyPropertyChanged以检测这些对象的修改。

如果您希望在视图模型更改期间可靠更新UI,则可以这样做。

  

这是什么意思?

“含义”是指在您的特定情况下,您正在做出无效的假设。 WPF组件经历了各种初始化步骤,其中只有一些是InitializeComponent()方法的一部分。

例如,如果您要将值更新的代码移动到Loaded事件的处理程序中,您会发现部分的更新反映在UI中,但不是全部。

如果您将相同的代码移动到使用Dispatcher.InvokeAsync()优先级DispatcherPriority.SystemIdle调用的方法中,您会发现将会观察到 none 的更新,除非对于由INotifyPropertyChanged支持的人。在这种情况下,您将明确等待初始化的每个方面完成,并且初始化代码不再有机会观察更新的值。

一切都与时间有关。在UI结束观察之前设置值的任何代码都可以在没有INotifyPropertyChanged或等效的情况下成功完成。但在这种情况下,你完全受制于当前框架实施的摆布。初始化的不同部分在不同的时间发生,并且这些部分并未全部记录,因此您依赖于未记录的行为。它可能不会改变,但你无法确定。

  

为什么在InitializeComponent之后设置一个DataContext属性对MVVM来说是不好的做法?

不是。不要相信你在互联网上阅读的所有内容,甚至(特别是!)。

如果您想放弃INotifyPropertyChanged的实施,那么在分配DataContext之前初始化所有视图模型数据非常重要。但是,即使您在调用DataContext后分配InitializeComponent,也会观察到该分配(因为DataContext是依赖属性,因此向框架提供了属性更改通知)和UI将从您的视图模型数据中检索所有绑定数据。

重要的是在分配DataContext之前初始化视图模型数据。相对于InitializeComponent()发生的情况并不重要。

答案 1 :(得分:0)

当视图模型属性不触发PropertyChanged事件时,必须在将视图模型实例分配给视图的DataContext之前设置其值。

但是,如果在调用InitializeComponent之前或之后分配DataContext,那么无关紧要:

给出像

这样的绑定
<TextBlock Text="{Binding SomeText}"/>

这两个序列都会导致在视图中显示属性值:

DataContext = new { SomeText = "Hello, World." };
InitializeComponent();

InitializeComponent();
DataContext = new { SomeText = "Hello, World." };