Question

I have a 3rd party SplitButton control that exposes some DropDownContent and a boolean IsOpen dp to control whether the drop down content is shown or not.

In the case the DropDownContent is a StackPanel with several Buttons, each of which is bound to a command in the view model. In addition to executing that command, clicking the button needs to close the open DropDown content, which I am doing with the AttachedBehavior below.

But my binding, which simple needs to get a reference to the ancestor SplitButton control doesn't work. In the binding, you will note I am trying to Find the first Ancestor control of type SplitButton. I do see however that the debug info says ancestor level 1, so I changed the level to as high as 4, but still with an error.

Can someone see what the fix is?

binding error

System.Windows.Data Error: 4 : Cannot find source for binding with reference 'RelativeSource FindAncestor, AncestorType='Xceed.Wpf.Toolkit.SplitButton', AncestorLevel='1''. BindingExpression:(no path); DataItem=null; target element is 'CloseDropDownContentBehavior' (HashCode=8896066); target property is 'DropDownButtonElement' (type 'SplitButton')

xaml

<DataTemplate x:Key="AddNewPartyTemplate">
    <StackPanel HorizontalAlignment="Right" Margin="10">

        <toolkit:SplitButton x:Name="theSplitButton" Content="{resx:Resx Subject_AddNewWithChoices}">
            <toolkit:SplitButton.DropDownContent>
                <StackPanel x:Name="theStackPanel">
                    <Button Content="{resx:Resx Person}" Command="{Binding AddNewPersonCommand}" 
                        >
                        <i:Interaction.Behaviors>
                            <local:CloseDropDownContentBehavior 
                  ***              DropDownButtonElement="{Binding 
                                    RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type toolkit:SplitButton}}}"/>
                        </i:Interaction.Behaviors>
                    </Button>
                    ...

                </StackPanel>
            </toolkit:SplitButton.DropDownContent>
        </toolkit:SplitButton>
    </StackPanel>
</DataTemplate>

attached behavior

public class CloseDropDownContentBehavior : Behavior<ButtonBase>
{
    private ButtonBase _button;

    protected override void OnAttached()
    {
        _button = AssociatedObject;
        _button.Click += OnPartyButtonClick;
    }

    protected override void OnDetaching()
    {
        _button.Click -= OnPartyButtonClick;
    }

    // **** the point of it all
    void OnPartyButtonClick(object sender, RoutedEventArgs e) { DropDownButtonElement.IsOpen = false; }

    public static readonly DependencyProperty DropDownButtonElementProperty =
        DependencyProperty.Register("DropDownButtonElement",
        typeof(SplitButton), typeof(CloseDropDownContentBehavior), new UIPropertyMetadata(null, OnDropDownElementChanged));

    public DropDownButton DropDownButtonElement
    {
        get { return (DropDownButton)GetValue(DropDownButtonElementProperty); }
        set { SetValue(DropDownButtonElementProperty, value); }
    }

    private static void OnDropDownElementChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) {

    }
}
Was it helpful?

Solution

Guessing it's because Interaction.Behaviors isn't part of the visual tree, so the binding won't find the ancestor. Have you tried simply:

DropDownElement="{Binding ElementName=theSplitButton}"

Update from comments: the solution in this case is to simply use x:Reference:

DropDownElement="{x:Reference theSplitButton}"

OTHER TIPS

i dont know the SplitButton.DropDownContent but if its behave like a context menu the following answer might help: WPF context menu whose items are defined as data templates

this trick is to bind with RelativeSource Self or Type ContextMenu and then set the Path to PlacementTarget.DataContext.YourProperty

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top