1

I have a user control with its own context menu, however I need to add additional items to that menu.

The approach I took was to have a dependency property called ContextMenuItems:

Public Shared ReadOnly ContextMenuItemsProperty As DependencyProperty = DependencyProperty.Register("ContextMenuItems", GetType(ObservableCollection(Of MenuItem)), GetType(SmartDataControl), New FrameworkPropertyMetadata(New ObservableCollection(Of MenuItem)))
Public Property ContextMenuItems As ObservableCollection(Of MenuItem)
    Get
        Return GetValue(ContextMenuItemsProperty)
    End Get

    Set(ByVal value As ObservableCollection(Of MenuItem))
        SetValue(ContextMenuItemsProperty, value)
    End Set
End Property

I then used a CompositeCollection to combine the static menu items from the control with the list provided by the host:

    <CompositeCollection x:Key="MenuItemsCompositeCollection">
        <MenuItem Header="TEST" />
        <CollectionContainer Collection="{Binding RelativeSource={RelativeSource AncestorType=UserControl}, Path=ContextMenuItems, Converter={StaticResource TestConverter}}" />
        <MenuItem Header="{Binding RelativeSource={RelativeSource AncestorType=UserControl}, Path=ContextMenuItems}" />
    </CompositeCollection>

What I see when I bind to that resource is:

  • TEST
  • (Collection)

The second menu item is bound to the collection to prove I can get to it. I have a test converter that I have added to menu item and it breaks in the converter method, but when I add the converter to the CollectionContainer it doesn't get called.

Finally, I get the following error in the output window:

System.Windows.Data Error: 4 : Cannot find source for binding with reference 'RelativeSource FindAncestor, AncestorType='System.Windows.Controls.UserControl', AncestorLevel='1''. BindingExpression:Path=ContextMenuItems; DataItem=null; target element is 'CollectionContainer' (HashCode=41005040); target property is 'Collection' (type 'IEnumerable')

H.B.
  • 166,899
  • 29
  • 327
  • 400
Darren
  • 4,408
  • 4
  • 39
  • 57

1 Answers1

2

That "proof" of yours does not work because the two objects compared are obviously not equal. You cannot use RelativeSource or ElementName bindings in a collection container because the necessary conditions are not met, i.e. there is no NameScope and since the CollectionContainer is an abtract object which does not appear in the visual tree there also is not parent via which the ancestor could be found.

If you have access to the UserControl you can however use the Binding.Source and a x:Reference to the UserControl's name, to prevent a cyclical dependecy error the CompositeCollection should be defined in the UserControl.Resources and then referenced using StaticResource.

e.g.

<UserControl Name="control">
    <UserControl.Resources>
        <CompositeCollection x:Key="collection">
            <!-- ... -->
            <CollectionContainer Collection="{Binding ContextMenuItems, Source={x:Reference control}, Converter=...}"/>
        </CompositeCollection>
    </UserControl.Resources>
    <!-- ... -->
        <MenuItem ItemsSource="{Binding Source={StaticResource collection}}"/>
</UserControl>
H.B.
  • 166,899
  • 29
  • 327
  • 400
  • That makes a lot of sense. I knew all I was proving was there wasn't a typo in the binding expression but your explination sheds some light on why it would work in one place but not another. Could you post some XAML to show how I would use Binding.Source and x:Reference (the MS docs are not very helpful). Thanks. – Darren Jul 27 '11 at 13:16
  • @Darren: Added an example and rewrote a bit of the first paragraph as i made a mistake, the problem is not really the inheritance from object (as the binding in the MenuItem still works) but rather that the CollectionContainer does not appear in the VisualTree. In general a lot of the workings of bindings are quite obfuscated, e.g. in some cases you have an [artifical inhertance context](http://blogs.msdn.com/b/nickkramer/archive/2006/08/18/705116.aspx) which may be quite magic... – H.B. Jul 27 '11 at 13:31