【问题标题】:Order of setting the DataContext in the default constructor in WPFWPF默认构造函数中设置DataContext的顺序
【发布时间】:2018-05-09 19:05:58
【问题描述】:

我在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 方法后,属性值的更改不会反映在 UI 中,但使用 INotifyPropertyChanged 的属性除外。到目前为止一切都清楚了。

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

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

2) 在 InitializeComponent 方法

之后设置DataContext

为什么在 InitializeComponent 之后设置 DataContext 属性对 MVVM 来说是一种不好的做法?能不能描述的更详细一点或者给出一个简单的代码示例?

【问题讨论】:

  • “为什么在 InitializeComponent 之后设置 DataContext 属性对 MVVM 来说是一种不好的做法?”它不是。谁说的?只需确保您的所有属性都会触发 PropertyChanged 事件
  • stackoverflow.com/a/11479509/7378940下方的评论中“重要的是要注意,如果使用MVVM,则应在调用InitializeComponent()之前设置DataContext,否则您的ViewModel绑定将无法正确设置。InitializeComponent () 调用所有属性绑定 getter,因此如果首先调用它,您的绑定将不会获得正确的值,直到在每个属性上再次调用 NotifyPropertyChanged。"

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


【解决方案1】:

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

如果您希望在视图模型更改期间可靠更新 UI。

这是什么意思?

“意义”是在您的特定场景中,您做出的假设是无效的。 WPF 组件会经历各种初始化步骤,其中只有一部分会作为InitializeComponent() 方法的一部分发生。

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

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

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

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

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

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

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

【讨论】:

    【解决方案2】:

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

    然而,在调用 InitializeComponent 之前或之后分配 DataContext 并不重要:

    给定一个类似的绑定

    <TextBlock Text="{Binding SomeText}"/>
    

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

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

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2020-10-15
      • 1970-01-01
      • 2011-07-12
      • 2020-12-31
      • 1970-01-01
      • 2016-07-23
      • 2017-12-05
      • 1970-01-01
      相关资源
      最近更新 更多