【问题标题】:Treeview Checkboxes not updating after bound property changed (SL4)绑定属性更改后树视图复选框未更新 (SL4)
【发布时间】:2011-10-23 10:38:30
【问题描述】:

我的问题很简单。我有一个绑定到对象 ObservableCollection 的树视图,并且这些对象都有自己的 ObservableCollection。根据用户在我的页面上选择的其他标准,我想动态设置选中哪些复选框。 不幸的是,在我更改了绑定到 IsChecked 的相应 bool 属性后,我的复选框无法更新其 IsChecked 状态。第一次展开任何节点时,复选框将处于正确状态,但之后它们会停止更新。我怀疑这意味着对象在第一次实际显示之前不会被创建/评估。

数据的结构是Silverlight -> ViewModel -> ObservableCollection of StoreGroups LocalStoreGroups -> StoreGroup 有 ObservableCollection of Store Stores

通过调试,我注意到 this.PropertyChanged 没有附加任何处理程序,我想知道这是否是问题所在?

Treeview 控件:

<controls:TreeView ItemsSource="{Binding LocalStoreGroups}" ItemTemplate="{StaticResource TreeviewStoreGroupTemplate}" />

在我的项目中,我使用带有以下 HeirarchalDataTemplates 的树视图:

<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 对象都有这个属性:

    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 的代码

 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;
        }

【问题讨论】:

  • 当你设置值时,这是否发生在后台线程中?
  • 不,底部的代码(更改 IsSelected 的代码)使用以下方法在 UI 线程上调用:来自 Mvvmlight 的 DispatcherHelper.CheckBeginInvokeOnUI(...)。
  • 我想知道您的 OnPropertyChanged 方法是如何工作的,因为它不接受字符串,尽管您将字符串传递给它
  • 中间有一种我没有复制的方法,它创建了 args,然后将它们传递给我复制的那个。
  • 无论如何,这里的赠品应该是没有人订阅 PropertyChanged 事件。事实证明,虽然我实现了 PropertyChanged 事件,但我忘了给类实际提供 INotifyPropertyChanged 接口。

标签: silverlight checkbox treeview


【解决方案1】:

看起来您正在更新属性以响应加载事件。当您更新属性时,您很可能不在 UI 线程上。除非更改发生在 UI 线程上,否则它不会更新显示。

对于作为集合的绑定属性和属性(而不是可观察集合中的子属性),只有 OnPropertyChanged 需要位于 UI 线程上。属性可以提前更改,但 UI 在调用 OnPropertyChanged 之前不会更改绑定。

我们所有的 ViewModel 都派生自我们创建的 ViewModelBase,该 ViewModelBase 实现了如下所示的助手 SendPropertyChanged(因此我们不必担心跨线程)。

我们所有的通知属性都调用它而不是直接调用 OnPropertyChanged。

它还公开了一个通常有用的 OnUiThread 方法,因此您可以在 UI 线程上执行任意代码:

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);
    }
}

【讨论】:

  • 在 Visual Studio 中使用断点进行调查,我可以确认在主线程上调用了更新代码。另外,我正在使用 MVVMLight 框架,它确实公开了一种方法来执行您的建议(尽管您的方法似乎是一个更整洁的解决方案)。最后,永远不会在试图发出更改信号的对象上抛出属性更改事件,因为 this.PropertyChanged = null。这可能是因为它是可观察集合中对象的子对象吗?
【解决方案2】:

无论如何,这里的赠品应该是没有人订阅 PropertyChanged 事件。事实证明,虽然我实现了 PropertyChanged 事件,但我忘记了实际上给类 INotifyPropertyChanged 接口。

【讨论】:

    猜你喜欢
    • 2020-04-08
    • 1970-01-01
    • 1970-01-01
    • 2018-05-06
    • 1970-01-01
    • 2015-09-26
    • 2015-09-18
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多