views:

699

answers:

2

Im trying to make the contents of a List thats a dependency property show up in a WPF context menu.

I have a class with the following dependency property, a list of Foo's (data holding class):

    public List<Foo> FooList
    {
        get { return (List<Foo>)GetValue(FooListProperty); }
        set { SetValue(FooListProperty, value); }
    }
    public static DependencyProperty FooListProperty =
        DependencyProperty.Register("FooList", typeof(List<Foo>),
            typeof(FooButton));

In XAML I set up the following static resource, I assume its needed since the context menu isnt part of the visual tree:

<UserControl.Resources>
    <ResourceDictionary>            
        <CollectionViewSource 
            x:Key="FooListSource"
            Source="{Binding FooList}"/>

        <!-- ... -->

    </ResourceDictionary>
</UserControl.Resources>

Also part of the ResourceDictionary above is a CompositeCollection which is needed to make the items show up in the actual context menu. If the UserControl CanStop property is true, we also show a separator and a stop command. These bindings does also fail, although the MenuItems themselves show up. So If I can figure out why these fail, the List might be easier.

<CompositeCollection x:Key="FooListItems">
    <CollectionContainer 
        Collection="{Binding Source={StaticResource FooListSource}}"/>
    <Separator 
        Visibility="{Binding CanStop,
            Converter={StaticResource VisibleIfTrue}}" />
    <MenuItem 
        Command="{x:Static Buttons:FooButton.Stop}"
        Header="Stop"
        Visibility="{Binding CanStop,
            Converter={StaticResource VisibleIfTrue}}"/>
</CompositeCollection>

And finally the context menu itself, also in the ResourceDictionary:

<ContextMenu 
    x:Key="FooButtonMenu"
    ItemsSource="{Binding Source={StaticResource FooListItems}}" 
    ItemTemplate="{StaticResource FooListTemplate}"
    <ContextMenu.CommandBindings>
        <CommandBinding  
                Command="{x:Static Buttons:FooButton.Stop}"
                Executed="Stop_Executed" />
    </ContextMenu.CommandBindings>
</ContextMenu>

I feel Im posting way to much code but Im not sure I can make this piece any simpler. Only the separator and the hardcoded menuitem shows up. So something must be messed up with the bindings. Bindings are usually not that hard but now when I want to bind something thats not really part of the same tree I feel a bit lost.

Any suggestions are welcome. :)

A: 

I don't see why you've made FooList a dependency property. You're not making it the target of a binding, which is the most common reason to create a dependency property. You haven't implemented a callback, so it can't do change notification (the second most common reason to create a dependency property). You're not using it for value inheritance. So why, then?

It seems to me that what you really want is for FooList to be a normal CLR property of type ObservableCollection<Foo> (or any class that implements INotifyCollectionChanged). That will do all the change notification that you need - at least, that you need for the code you've posted so far.

Robert Rossney
This sounds like a red herring to me. We could argue all day about whether it is appropriate to make FooList a DependencyProperty or a CLR property. The bottom like is that a DependencyProperty can do everything a CLR property can do and much more. Though it may be argued to be a poor choice in certain scenarios (and I'm not sure this is one of them), it is not related to the problem mizipzor is having.
Ray Burns
You're right that it doesn't matter whether it's a dependency property or not. What's actually important is that its type be a collection that implements `INotifyCollectionChanged`.
Robert Rossney
+1  A: 

As you suspected, your problem does seem to be caused by the use of List<Foo> instead of ObservableCollection<Foo>. Since List<Foo> doesn't notify on property changes, the only way to get WPF to recognize you've added or removed an item is to temporarily set the FooList property to something else and then set it back.

There is no need to switch to a CLR property. Just change List<Foo> to ObservableCollection<Foo>.

The reason the bindings in your CompositeCollection aren't working is that CompositeCollection is not a DependencyObject, so it can't inherit a DataContext.

Ray Burns
Is there an alternative to the CompositeCollection I can use that is a DependencyObject? Although the items are only updated once the context menu is shown, and I used to do this the simple way in code-behind, but I want to try to have it databound now.
mizipzor
Simply swapping List for ObservableCollection seems to make it work.
mizipzor