2

I created a user control (FilterPicker), that has a certain list as a property. This is a dependency property so that it can be set when I use the user control.

public static readonly DependencyProperty StrategiesProperty = DependencyProperty.Register(
        "Strategies",
        typeof(List<FilterType>),
        typeof(FilterPicker),
        new FrameworkPropertyMetadata
        {
            DefaultValue = new List<FilterType> { FilterType.Despike },
            PropertyChangedCallback = StrategiesChangedCallback,
            BindsTwoWayByDefault = false,
        });

I am then trying to define this list in the .xaml file where I use this control.

<Filtering:FilterPicker Grid.Row="1" Strategy="{Binding Strategy}">
            <Filtering:FilterPicker.Strategies>
                <Filtering1:FilterType>PassThrough</Filtering1:FilterType>
                <Filtering1:FilterType>MovingAverage</Filtering1:FilterType>
                <Filtering1:FilterType>Despike</Filtering1:FilterType>
            </Filtering:FilterPicker.Strategies>
        </Filtering:FilterPicker>

However, it doesn't work. StrategiesChangedCallBack never gets called. If I set it through a binding, it works fine - just not when I try to define it in the xaml. So this works:

<Filtering:FilterPicker Grid.Row="1" Strategy="{Binding Strategy}" Strategies="{Binding AllStrategies}">

But not the previous snippet. Any ideas on what I'm doing wrong?

Tessa
  • 51
  • 1
  • 6
  • 3
    The elements added in XAML are added to the already existing collection, which you have set as default value of the property. The collection instance doesn't change, therefore the PropertyChangedCallback isn't called. Instead of a List, you might use a collection that implements the INotifyCollectionChanged interface (e.g. ObservableCollection) and attach a handler for its CollectionChanged event. – Clemens Oct 31 '16 at 16:27
  • 1
    Besides that, using a default value for a collection type dependency property is potentially dangerous, since all instances of the owning class will by default operate on the same collection instance, i.e. adding elements to the Strategies property of one FilterPicker will affect the property of all other FilterPickers. – Clemens Oct 31 '16 at 16:29
  • See for example [this answer](http://stackoverflow.com/a/9128855/1136211) or [this one](http://stackoverflow.com/a/19558932/1136211) for how to deal with INotifyCollectionChanged. – Clemens Oct 31 '16 at 16:31
  • Your question is too lacking in detail to understand why you have the `StrategiesChangedCallback()` method, so a good, complete answer can't be provided. As noted in the first comment above, the property value itself doesn't change, so of course the method isn't called. If all you want is to react to changes in the collection itself, then you can subscribe to the `INotifyCollectionChanged.CollectionChanged` event. – Peter Duniho Oct 31 '16 at 18:08
  • As far as @Clemens [other comment](https://stackoverflow.com/questions/40346287/setting-a-collection-dependencyproperty-from-xaml-wpf#comment67947243_40346287) regarding "default value for a collection type dependency property" goes, it's wise to be careful here, but not unprecedented. A very good example of this is the `ItemsControl` object, which has both `ItemsSource` and `Items`. If you follow that model -- `ItemsSource` is settable, `Items` is read-only, and the two are mutually exclusive -- you should be fine. – Peter Duniho Oct 31 '16 at 18:08
  • I would only recommend to derive from ItemsControl if FilterPicker is meant to *display* the elements from the Strategies collection. If the Strategies property e.g. only configures the behaviour of the control, ItemsControl as a base class would be the wrong choice. – Clemens Oct 31 '16 at 21:28
  • @Clemens, Peter Duniho: Your combined advice really helped me :). The problem was indeed that I wasn't listening to the collection being changed. When changing it to an ObservableCollection, as predicted I had problems with it operating on the same collection instance. In the end I changed it (back) to an IEnumerable (like ItemsControl.ItemsSource is), which I now set based on a StaticResource defined in the xaml. – Tessa Nov 01 '16 at 09:11
  • @Clemens, Peter Duniho: If either of you would like to post it as an answer, I will accept it. Alternatively I can describe the solution myself and add the final code snippets. – Tessa Nov 01 '16 at 09:11
  • Please have look into [this](https://stackoverflow.com/a/46318579/2507782) also once. – Rahul Sonone Sep 20 '17 at 09:59

1 Answers1

2

From the comments on my original question I was able to piece it together:

  • Indeed the problem was I was only listening to the property being changed, and not to objects in the collection.
  • As predicted I had problems with it operating on the same collection instance.

In the end I changed my DependencyProperty to use an IEnumerable, which I define as a StaticResource in the .xaml that uses the FilterPicker UserControl.

DependencyProperty:

public static readonly DependencyProperty StrategiesProperty = DependencyProperty.Register(
        "Strategies",
        typeof(IEnumerable<FilterType>),
        typeof(FilterPicker),
        new FrameworkPropertyMetadata
        {
            DefaultValue = ImmutableList<FilterType>.Empty, //Custom implementation of IEnumerable
            PropertyChangedCallback = StrategiesChangedCallback,
            BindsTwoWayByDefault = false,
        });

Using it:

<Grid.Resources>
            <x:Array x:Key="FilterTypes" Type="{x:Type Filtering1:FilterType}" >
                <Filtering1:FilterType>PassThrough</Filtering1:FilterType>
                <Filtering1:FilterType>MovingAverage</Filtering1:FilterType>
                <Filtering1:FilterType>Fir</Filtering1:FilterType>
            </x:Array>
        </Grid.Resources>

<Filtering:FilterPicker Grid.Row="1" Strategies="{StaticResource FilterTypes}" />
Tessa
  • 51
  • 1
  • 6