Treeview Checkboxes not updating after bound property changed (SL4)
我的问题很简单。我有一个绑定到对象 ObservableCollection 的树视图,这些对象都有自己的 ObservableCollection。根据用户在我的页面上选择的其他标准,我想动态设置选中哪些复选框。不幸的是,在我更改了绑定到 IsChecked 的相应 bool 属性后,我的复选框无法更新其 IsChecked 状态。第一次展开任何节点时,复选框将处于正确状态,但之后它们会停止更新。我怀疑这意味着对象在第一次实际显示之前不会被创建/评估。
数据的结构是 Silverlight -> ViewModel -> ObservableCollection of StoreGroups LocalStoreGroups -> StoreGroup 有 ObservableCollection of Store Stores
通过调试,我注意到 this.PropertyChanged 没有附加任何处理程序,我想知道这是否是问题所在?
树视图控件:
1 | <controls:TreeView ItemsSource="{Binding LocalStoreGroups}" ItemTemplate="{StaticResource TreeviewStoreGroupTemplate}" /> |
在我的项目中,我使用带有以下 HeirarchalDataTemplates 的树视图:
1 2 3 4 5 6 7 8 | <UserControl.Resources> <sdk:HierarchicalDataTemplate x:Key="TreeviewStoreTemplate"> <CheckBox IsChecked="{Binding IsSelected, Mode=TwoWay}" Content="{Binding DTO.Name}" /> </sdk:HierarchicalDataTemplate> <sdk:HierarchicalDataTemplate x:Key="TreeviewStoreGroupTemplate" ItemsSource="{Binding Stores}" ItemTemplate="{StaticResource TreeviewStoreTemplate}"> <CheckBox IsChecked="{Binding IsSelected, Mode=TwoWay}" Content="{Binding DTO.Name}" /> </sdk:HierarchicalDataTemplate> </UserControl.Resources> |
IsSelected 属性的代码(StoreGroup 对象和 Store 对象都有这个属性:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | private bool _IsSelected; public bool IsSelected { get { return _IsSelected; } set { _IsSelected = value; OnPropertyChanged("IsSelected"); } } protected void OnPropertyChanged(PropertyChangedEventArgs e) { PropertyChangedEventHandler temp = this.PropertyChanged; if (null != temp) temp(this, e); } |
更改 IsSelected 的代码
1 2 3 4 5 6 7 8 9 10 | foreach (Store s in LocalStoreGroups.SelectMany(sg => sg.Stores)) { s.IsSelected = false; } foreach (StoreLink link in links) { Store targetStore = (from s in LocalStoreGroups.SelectMany(sg => sg.Stores) where s.DTO.ID == link.DTO.StoreID select s).FirstOrDefault(); targetStore.IsSelected = true; } |
看起来您正在更新属性以响应加载事件。当您更新属性时,您很可能不在 UI 线程上。除非更改发生在 UI 线程上,否则它不会更新显示。
对于作为集合的绑定属性和属性(而不是可观察集合中的子属性),只有 OnPropertyChanged 需要位于 UI 线程上。属性可以提前更改,但 UI 在调用 OnPropertyChanged 之前不会更改绑定。
我们所有的 ViewModel 都从我们创建的 ViewModelBase 派生,该 ViewModelBase 实现了如下所示的助手 SendPropertyChanged(因此我们不必担心跨线程)。
我们所有的通知属性都调用它而不是直接调用 OnPropertyChanged。
它还公开了一个通常有用的 OnUiThread 方法,因此您可以在 UI 线程上执行任意代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | protected delegate void OnUiThreadDelegate(); public event PropertyChangedEventHandler PropertyChanged; public void SendPropertyChanged(string propertyName) { if (this.PropertyChanged != null) { this.OnUiThread(() => this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName))); } } protected void OnUiThread(OnUiThreadDelegate onUiThreadDelegate) { if (Deployment.Current.Dispatcher.CheckAccess()) { onUiThreadDelegate(); } else { Deployment.Current.Dispatcher.BeginInvoke(onUiThreadDelegate); } } |
无论如何,这里的赠品应该是没有人订阅 PropertyChanged 事件。事实证明,虽然我实现了 PropertyChanged 事件,但我忘记了实际上给类 INotifyPropertyChanged 接口。