65.9K
CodeProject 正在变化。 阅读更多。
Home

NotifyParentObservableCollection(第一部分)监控其子属性的更改

starIconstarIconstarIcon
emptyStarIcon
starIcon
emptyStarIcon

3.50/5 (4投票s)

2011年1月20日

Eclipse

3分钟阅读

viewsIcon

46492

downloadIcon

382

ObservableCollection 附加到其子项的 PropertyChanged 事件,并相应地引发 ChildPropertyChanged 事件。 当您需要更新/选择/排列/移动项目时非常有用。

目录

引言

GUI编程中最常见的任务包括从列表中选择多个项目,修改多个项目的属性,重新排列列表中的项目,或者将项目从一个列表复制/移动到另一个列表。

我假设您正在使用 WPF 和 MVVM 模式。

让我们考虑以下简单示例。 您想打开/关闭Hex变量的各个位,像这样

binary.JPG

您在 ViewModel 上创建属性,这些属性包含项目列表或集合,并将它们绑定到视图上的一些ItemsControl。 您创建一个包含CheckBoxItemTemplate,并将它们绑定到项目的属性。 现在怎么办? 您如何实现所需的功能?

您有几种选择

  • 为您的项目创建视图模型类,并将所有逻辑放在那里,因此每次Item.IsSelected属性更改时,您都会更新Hex变量。 哎呀,Hex变量属于父 VM,因此您的项目将必须具有一个成员变量来保存父 VM 或一个委托回调。
  • 制作哑项目来保存数据,制作一堆转换器,并在这些转换器/XAML中实现逻辑,像这样;
  • TextBox.Text = "{Binding CheckBox.IsChecked, Converter=....}"

    当然,您需要一个相当复杂的FlagConverter,它可以组合多个标志值(我的 WPF 库中有很多这样的转换器)。

  • 不要重新发明轮子,使用我创建的实用程序类来简化事情。 使用这些实用程序类,更新/选择/排列/移动项目的任何任务都变得轻而易举。

示例

XAML 本身就非常简单

<ListBox ItemsSource="{Binding Selectors}">
    <ListBox.ItemTemplate>
        <DataTemplate>
            <CheckBox IsChecked="{Binding IsSelected}" 
                      Content="{Binding Caption}"/>
        </DataTemplate>
    </ListBox.ItemTemplate>
</ListBox>

ViewModel 不可能更简单

private NotifyParentObservableCollection<LookupItem> _selectors;
public NotifyParentObservableCollection<LookupItem> Selectors
{
    get { return _selectors; }
    private set
    {
        if (_selectors != null)
        {
            _selectors.ChildPropertyChanged -= OnSelectorsChildPropertyChanged;
        }
        _selectors = value;
        if (_selectors != null)
        {
            _selectors.ChildPropertyChanged += OnSelectorsChildPropertyChanged;
        }
        OnPropertyChanged("Selectors");
    }
}

private void OnSelectorsChildPropertyChanged(object sender, 
                                             PropertyChangedEventArgs e)
{
    var item = sender as LookupItem;
    if (e.PropertyName == "IsSelected")
    {
        if (item.IsSelected)
        {
            Hex |= (int) item.Value;
        }
        else
        {
            Hex &= ~(int) item.Value;
        }
    }
}

Selectors = new NotifyParentObservableCollection<LookupItem>
{
    new LookupItem { Value = 1, Caption = "0x00000001" },
    new LookupItem { Value = 2, Caption = "0x00000010" },
    new LookupItem { Value = 4, Caption = "0x00000100" },
    new LookupItem { Value = 8, Caption = "0x00001000" },
};

瞧,我们完成了任务。 你能超越这种简单性吗?

NotifyParentObsevableCollection 和 LookupItem

public class LookupItem : INotifyPropertyChanged

当您需要更新/选择/排列/移动这些项目时,LookupItem 是一个实用程序类,用作项目的通用 ViewModel。

LookupItem 具有大量对于这些场景最有用的属性,并且它实现了 INotifyPropertyChanged 接口,以便在其任何属性更改时通知其父级 NotifyParentObservableCollection<T>。 如果您需要更多属性,请继承自它并添加您需要的任何内容。

public class NotifyParentObservableCollection<T> : ObservableCollection<T>

NotifyParentObservableCollection<T> 附加到其子项的 PropertyChanged 事件,并相应地引发 ChildPropertyChanged 事件。

NotifyParentObservableCollection<T> 继承自 ObservableCollection<T>,并重写 OnCollectionChanged()ClearItems() 以附加/分离子项的处理程序。

就是这样。 再次,你能超越这种简单性吗?

当我编写代码时,我的目标是尽可能多地消除代码行,但不要超过限制。

我的座右铭:代码行越少,潜在错误就越少。

注意

由于 NotifyParentObservableCollection<T> 强烈附加到其子项的 PropertyChanged 事件,因此您必须在使用完后 Clear() 该集合,以消除潜在的内存泄漏。

当然,如果父集合与其子集合具有相同的生命周期,则您可以安全地忽略此建议,但如果子集合可以比集合更长寿,请不要忘记调用 Clear()

修订历史

  • 2011年1月20日 - 创建了这篇文章。
© . All rights reserved.