Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ObservableCollection dependency property does not update when item in collection is deleted

I have a attached property of type ObservableCollection on a control. If I add or remove items from the collection, the ui does not update. However if I replace the collection within with a new one the ViewModel the ui does update.

Can someone give me an example of what I need to do within the Dependency object so that it can handle changes within the collection?

Part of the dependency object is listed below:

public class RadCalendarBehavior : DependencyObject
{
private static void OnSpecialDaysChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
  var calendar = d as RadCalendar;
  if (e.NewValue != null)
  {
    calendar.DayTemplateSelector = new SpecialDaySelector((ObservableCollection<DateTime>)e.NewValue, GetSpecialDayTemplate(d));
  }
}

public static ObservableCollection<DateTime> GetSpecialDays(DependencyObject obj)
{
  return (ObservableCollection<DateTime>)obj.GetValue(SpecialDaysProperty);
}

public static void SetSpecialDays(DependencyObject obj, ObservableCollection<DateTime> value)
{
  obj.SetValue(SpecialDaysProperty, value);
}

public static readonly DependencyProperty SpecialDaysProperty =
    DependencyProperty.RegisterAttached("SpecialDays", typeof(ObservableCollection<DateTime>), typeof(RadCalendarBehavior), new UIPropertyMetadata(null, OnSpecialDaysChanged));
}
}

I understand that I need to register that the collection has changed, but I am unsure how to do this within the dependency property

like image 985
GoalMaker Avatar asked Dec 06 '10 00:12

GoalMaker


3 Answers

A change within the collection won't trigger the OnSpecialDaysChanged callback, because the value of the dependency property hasn't changed. If you need to react to detect changes with the collection, you need to handle the event CollectionChanged event manually:

private static void OnSpecialDaysChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
  var calendar = d as RadCalendar;

  if (e.OldValue != null)
  {
    var coll = (INotifyCollectionChanged)e.OldValue;
    // Unsubscribe from CollectionChanged on the old collection
    coll.CollectionChanged -= SpecialDays_CollectionChanged;
  }

  if (e.NewValue != null)
  {
    var coll = (ObservableCollection<DateTime>)e.NewValue;
    calendar.DayTemplateSelector = new SpecialDaySelector(coll, GetSpecialDayTemplate(d));
    // Subscribe to CollectionChanged on the new collection
    coll.CollectionChanged += SpecialDays_CollectionChanged;
  }
}

private static void SpecialDays_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
    // handle CollectionChanged
}
like image 98
Thomas Levesque Avatar answered Nov 13 '22 16:11

Thomas Levesque


This is just to add to the answer by Thomas. In my code I do interact with the DependencyObject's properties by creating a handler object localy like below:

private static void OnSpecialDaysChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
    var action = new NotifyCollectionChangedEventHandler(
            (o, args) =>
                {
                    var calendar = d as RadCalendar;

                    if (calendar!= null)
                    {
                        // play with calendar's properties/methods
                    }
                });

    if (e.OldValue != null)
    {
       var coll = (INotifyCollectionChanged)e.OldValue;
       // Unsubscribe from CollectionChanged on the old collection
       coll.CollectionChanged -= action;
    }

    if (e.NewValue != null)
    {
       var coll = (ObservableCollection<DateTime>)e.NewValue;
       // Subscribe to CollectionChanged on the new collection
       coll.CollectionChanged += action;
    }
}

Hope this is helpful to someone.

like image 29
nowaq Avatar answered Nov 13 '22 15:11

nowaq


If you have a collection-type dependency property keep the following in mind:

If your property is a reference type, the default value specified in dependency property metadata is not a default value per instance; instead it is a default value that applies to all instances of the type. [...]
To correct this problem, you must reset the collection dependency property value to a unique instance, as part of the class constructor call.

(see MSDN Collection-Type Dependency Properties)

To answer Sam's question (I just ran into the same problem):

Make your CollectionChanged-handler non-static and unsubscribe/re-subscribe on instance-level.

private static void OnSpecialDaysChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
  var calendar = d as RadCalendar;

  if (e.OldValue != null)
  {
    var coll = (INotifyCollectionChanged)e.OldValue;
    // Unsubscribe from CollectionChanged on the old collection of the DP-instance (!)
    coll.CollectionChanged -= calendar.SpecialDays_CollectionChanged;
  }

  if (e.NewValue != null)
  {
    var coll = (ObservableCollection<DateTime>)e.NewValue;
    calendar.DayTemplateSelector = new SpecialDaySelector(coll, GetSpecialDayTemplate(d));
    // Subscribe to CollectionChanged on the new collection of the DP-instance (!)
    coll.CollectionChanged += calendar.SpecialDays_CollectionChanged;
  }
}

private void SpecialDays_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
    // handle CollectionChanged on instance-level
}
like image 45
Matumba Avatar answered Nov 13 '22 17:11

Matumba