如何从包含的对象中正确触发CollectionChanged通知?

问题描述 投票:1回答:3

这里是对本质上是一个简单问题的冗长解释。我正在使用Telerilk RadDropDownButton,它显示带有复选框的列表项。

                    <Controls:RadDropDownButton AutoOpenDelay="0:0:0.0" x:Name="Urgency" VerticalAlignment="Center" Width="150" Content="{Binding Path=ItemsSource, ElementName=UrgencyList, Mode=TwoWay, Converter={StaticResource ButtonTextConverter}}" HorizontalContentAlignment="Left">
                    <Controls:RadDropDownButton.DropDownContent>
                        <ListBox x:Name="UrgencyList">
                            <ListBox.ItemTemplate>
                                <DataTemplate>
                                    <StackPanel Orientation="Horizontal">
                                        <CheckBox Content="{Binding Name}" ClickMode="Press" IsChecked="{Binding IsChecked, Mode=TwoWay}" Checked="CheckBox_Checked" Unchecked="CheckBox_Unchecked" />
                                    </StackPanel>
                                </DataTemplate>
                            </ListBox.ItemTemplate>
                        </ListBox>
                    </Controls:RadDropDownButton.DropDownContent>
                </Controls:RadDropDownButton>

如您所见,我将Content属性绑定到Converter。我想要的是,如果未选择任何内容,则内容将显示为“全部”,并且如果选中某项,则将显示所选(已选中)项的列表。

    public class ButtonTextConverter : IValueConverter
{
    #region IValueConverter Members

    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        Debug.WriteLine("I'm Binding");
        int numChecked = 0;
        if (value != null)
            numChecked = ((ObservableCollection<UrgencyItem>) value).Count(urgencyItem => urgencyItem.IsChecked);
        return numChecked > 0 ? string.Format("{0} Items Selected", numChecked) : "All";
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        throw new NotImplementedException();
    }

    #endregion
}

我要绑定的类根据需要实现INotifyPropertyChanged。在此部分列出:

    public class UrgencyItem : INotifyPropertyChanged
{
    private int _id;
    private bool _isChecked;
    private string _name;

    public string Name
    {
        get { return _name; }
        set
        {
            _name = value;
            NotifyPropertyChanged("Name");
        }
    }

并且我将ListBox绑定到后面代码中的数据上,如下所示:

        private void SearchParamsVertical_Loaded(object sender, RoutedEventArgs e)
    {
        urgencyList = new ObservableCollection<UrgencyItem>
                          {
                              new UrgencyItem {ID = 1, IsChecked = false, Name = "Non Emergent"},
                              new UrgencyItem {ID = 2, IsChecked = false, Name = "Emergent"},
                              new UrgencyItem {ID = 3, IsChecked = false, Name = "Stat Emergent"},
                              new UrgencyItem {ID = 4, IsChecked = false, Name = "Stroke Protocol"}
                          };

        urgencyList.CollectionChanged += urgencyList_CollectionChanged;
        UrgencyList.ItemsSource = urgencyList;
    }

这里是问题...

选中复选框后,内容的值应更新。不是。

之所以不是这样,是因为尽管该通知发出了IsChecked已更改的通知,但该通知基本上没有任何进展。 UrgencyItem对象不知道它是ObservableCollection的一部分。关于ObservableCollection的事情是,仅当将项目添加到集合中或从集合中删除项目时,它才向其绑定发送通知。换句话说,更改集合中项目的属性不会触发CollectionChanged事件,因为没有添加/删除对象。

我需要做的是在修改集合的属性时触发collectionChanged事件。我曾经知道如何做到这一点,但是离Silverlight太多了,我已经忘记了如何做。

有人吗?

silverlight observablecollection
3个回答
1
投票

简而言之,我认为您的诊断是正确的:如果ObservableCollection中的对象发生更改,即使该对象实现了INotifyPropertyChanged,通常也不会收到CollectionChanged通知。据我所知,还没有一种直接的方法可以通过内置的Silverlight类获得所需的行为。

我知道有三种可能的解决方法:

((1)一种选择是从servableCollection继承,为urgencyList创建自己的集合,该集合实现此行为,即,它订阅添加到集合中的每个对象的INPC通知,并在出现时触发CollectionChanged事件那会发生。

((2)的第二种选择是使用类似ReactiveUI framework的东西,它具有实现此行为的自己的ReactiveCollection。

(3)第三种选择是通过类似Obtics or Continuous Linq的方式创建您的urgencyList。他们返回的集合会自动实现此行为。


0
投票

这就是我正在使用的。我想肯在nr(1)中提出的建议:

public class Person: INotifyPropertyChanged
{
private string _name;
public string Name
    {
        get { return _name; }
        set { 
            _name = value;
            if(PropertyChanged != null)
                PropertyChanged(this, new PropertyChangedEventArgs("Name"));
        }
    }
public event PropertyChangedEventHandler PropertyChanged;
}

我在PLObservableNotifyList<Person>中有这些对象,我在ItemsSource上将其设置为ItemsControl。当我更新值(使用设置器)后,绑定将自动更新。

public class PLObservableNotifyList<T> :
            ObservableCollection<T> where T : INotifyPropertyChanged
{
    public ItemPropertyChangedEventHandler ItemPropertyChanged;
    public EventHandler CollectionCleared;

    protected override void OnCollectionChanged(
                                NotifyCollectionChangedEventArgs args)
    {
        base.OnCollectionChanged(args);

        if (args.NewItems != null)
            foreach (INotifyPropertyChanged item in args.NewItems)
                item.PropertyChanged += OnItemPropertyChanged;

        if (args.OldItems != null)
            foreach (INotifyPropertyChanged item in args.OldItems)
                item.PropertyChanged -= OnItemPropertyChanged;
    }

    void OnItemPropertyChanged(object sender,
                               PropertyChangedEventArgs args)
    {
        if (ItemPropertyChanged != null)
            ItemPropertyChanged(this,
                new PLItemPropertyChangedEventArgs(sender,
                                                 args.PropertyName));
    }

    protected override void ClearItems()
    {
        foreach (INotifyPropertyChanged item in Items)
            item.PropertyChanged -= OnItemPropertyChanged;

        if (CollectionCleared != null)
            CollectionCleared(this, EventArgs.Empty);

        base.ClearItems();
    }
}

0
投票

您需要我的ObservableComputations库。使用该库,您可以编写代码:

            private Computing<string> _checkedUrgencyItemsText;
            public Computing<string> CheckedUrgencyItemsText = _selectedUrgencyItemsText ?? 
                 Expr.Is(() => UrgencyItems.Filtering(urgencyItem => urgencyItem.IsChecked)
                   .Using(checkedUrgencyItems => 
                        checkedUrgencyItems.Count > 0 
                        ?  string.Format("{0} Items Selected", checkedUrgencyItems.Count) 
                        : "All")).Computing();
                    <Controls:RadDropDownButton Content="{Binding Path=CheckedUrgencyItemsText.Value}" AutoOpenDelay="0:0:0.0" x:Name="Urgency" VerticalAlignment="Center" Width="150"  HorizontalContentAlignment="Left">
                    <Controls:RadDropDownButton.DropDownContent>
                        <ListBox x:Name="UrgencyList">
                            <ListBox.ItemTemplate>
                                <DataTemplate>
                                    <StackPanel Orientation="Horizontal">
                                        <CheckBox Content="{Binding Name}" ClickMode="Press" IsChecked="{Binding IsChecked, Mode=TwoWay}" Checked="CheckBox_Checked" Unchecked="CheckBox_Unchecked" />
                                    </StackPanel>
                                </DataTemplate>
                            </ListBox.ItemTemplate>
                        </ListBox>
                    </Controls:RadDropDownButton.DropDownContent>
                </Controls:RadDropDownButton>
© www.soinside.com 2019 - 2024. All rights reserved.