Как правильно отключить CollectionChanged Notification от содержащихся объектов? - PullRequest
0 голосов
/ 14 марта 2011

Вот длинное объяснение того, что по сути является простой проблемой.Я использую 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 с конвертером.Я хочу, чтобы, если ничего не выбрано, чтобы содержимое показывало «Все», а если что-то отмечалось, отображало список # выбранных (отмеченных) элементов.

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

ТАК ЧТО ЗДЕСЬ ПРОБЛЕМА ...

Если флажок установлен, значение Content должно обновляться.Это не так.

Причина, по которой это не так, заключается в том, что, хотя запускается уведомление о том, что IsChecked был изменен, это уведомление в основном идет в никуда.Объект UrgencyItem не знает, что он является частью ObservableCollection.И вещь о ObservableCollection состоит в том, что он отправляет уведомления на привязку только тогда, когда элементы ДОБАВЛЕНЫ / УДАЛЕНЫ в коллекцию.Другими словами, изменение свойства элемента в коллекции не вызывает событие CollectionChanged, поскольку объекты не были добавлены / удалены.

Что мне нужно сделать, так это вызвать событие collectionChanged при изменении свойства коллекции.Раньше я знал, как это сделать, но слишком много времени проводил вдали от Silverlight, и я забыл, как.

Кто-нибудь?

Ответы [ 2 ]

1 голос
/ 14 марта 2011

Короче говоря, я думаю, что ваш диагноз правильный: вы обычно не получаете уведомление CollectionChanged, если объект в ObservableCollection изменяется, даже если этот объект реализует INotifyPropertyChanged. Насколько я знаю, не существует простого способа получить желаемое поведение с помощью встроенных классов Silverlight.

Есть три возможных способа решения этой проблемы, о которых я знаю:

(1) Один из вариантов - создать собственную коллекцию для urgencyList, унаследованную от ObservableCollection, которая реализует это поведение, т.е. она подписывается на уведомление INPC о каждом объекте, добавляемом в коллекцию, и запускает событие CollectionChanged. когда это произойдет.

(2) Второй альтернативой является использование чего-то вроде ReactiveUI framework , у которого есть собственная ReactiveCollection, которая реализует это поведение.

(3) Третий вариант - создать ваш urgencyList через что-то вроде Obtics или Continuous Linq . Коллекции, которые они возвращают, реализуют это поведение автоматически.

0 голосов
/ 25 августа 2011

Это то, что я использую. То, что Кен предложил в 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();
    }
}
...