DependencyObject that also implements INotifyPropertyChanged?

230 Views Asked by At

For a Silverlight and WPF app, I have a custom control that includes an ObservableCollection as a dependency property. One element of that control, a Border, needs to change color depending on the composition of items in the ObservableCollection.

For example, let's say the collection is of animals, vegetables, and minerals and called ObjectList. If there is at least one animal, I want the border to be red; if there are no animals but at least one vegetable, it's green; otherwise the collection has only minerals, so will appear as blue.

I created a converter that can take the collection and determine the color, so have a binding like:

<Border Background="{Binding ObjectList, 
                     RelativeSource={RelativeSource Self}, 
                     Converter={StaticResource MyColorConverter}}" />

The challenge is that as items get added/removed from ObjectList I need to trigger reevaluation of the background color; however, ObjectList itself doesn't change. I figure I have three options, but am unsure of which may be the best practice:

  1. Create a new collection each time an object is added or removed. That seems heavy-handed, but will result in ObjectList being changed and so trigger the background update.

  2. Call UpdateTarget for the background property in the CollectionChanged callback for ObjectList. Since UpdateTarget isn't available for Silverlight, I just remove and re-add the binding - again a bit heavy handed.

  3. Implement INotifyPropertyChanged on my custom control and call PropertyChanged on the ObjectList within the implementation of CollectionChanged

I like 3 the best, but the fact I have a DependencyObject that also implements INPC seems odd. Is it? Is there a more elegant approach?

1

There are 1 best solutions below

2
On

There is a way of doing this recommended by the MSDN documentation (scroll down to Best Practices for Working with the VisualStateManager; it's written for full .Net but this section is well suitable for Silverlight too). Whenever your VisualStates depend on properties/state of your custom Control it is recommended to have a ChangedHandler for each VisualState-affecting property and call a private UpdateVisualStates method from there. Evaluate your conditions and set the VisualStates programmatically from within this method.

Even if you are not using VisualStates for the color change I recommend you follow this same pattern.

The following code is left incomplete for brevity:

public ObservableCollection ObjectList {...}
public static readonly DependencyProperty ObjectListProperty =
    DependencyProperty.Register(...OnObjectListChanged...);

private static void OnObjectListChanged(...)
{ObjectList.CollectionChanged += OnObjectListCollectionChanged;}

private void OnObjectListCollectionChanged(...){ UpdateVisualStates(); }

private void UpdateVisualStates()
{
    //actually you have to instatiate a SolidColorBrush here
    if (ContainsAtLeastOneAnimal()) { m_border.Background = Colors.Red; }
    else if (ContainsAtLeastOneVegetable()) {m_border.Background = Colors.Green;}
    else { m_border.Background = Colors.Blue; }
}

Feel free to introduce a DependencyProperty BorderColor and bind to it from your xaml if you don't want to have a reference to the border. It's fine. And there is really no problem with having another moving part. That's way better than simulating that the whole ObjectList instance changed.