关于 silverlight:Treeview 复选框在绑定属性更改后不更新(SL4)

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 接口。