WPF - Freezable in a style of a button not inheriting DataContext

595 views Asked by At

I am modeling an attached command pattern after the AttachedCommandBehavior library here. My button looks like this:

<Button>
    <Button.Style>
        <Style TargetType="{x:Type Button}">
            <Setter Property="vms:Attached.Behaviors">
                <Setter.Value>
                    <vms:Behaviors>
                        <vms:Behavior Event="Click" 
                                      Command="{Binding ClickCommand}" />
                    </vms:Behaviors>
                </Setter.Value>
            </Setter>
        </Style>
    </Button.Style>
</Button>

Everything works great, but when the setter on the Behavior is executed, the Command is null.

Behavior is a Freezable, and Behaviors is a FreezableCollection<Behavior>. It just doesn't seem to be inheriting the DataContext from the Button.

On the other hand, this works correctly:

<Button>
    <vms:Attached.Behaviors>
        <vms:Behavior Event="Click" Command="{Binding ClickCommand}" />
    </vms:Attached.Behaviors>
</Button>

Unfortunately I can't do it this way, because I need to target generated ListViewItems using ItemContainerStyle.

Is there some way to get the DataContext in the Style?

2

There are 2 answers

4
Rick Sladkey On BEST ANSWER

The Attached Command Behavior library is the germ of the idea that became Blend Behaviors. The Blend Behaviors are much more powerful and standardized and so I recommend you switch to using them. But whether you are using Attached Command Behavior or Blend Behaviors, the problem is essential the same: they don't work as expected when trying to set them using a style. I've solved this problem for Blend Behaviors with full support for binding in this StackOverflow answer:

Without testing it, I guess you have to move the ACB behavior to a resource marked with x:Shared="False" in order to get the binding to work.

0
Quanta On

I had the same problem, and using RelativeSource did the trick. I'll show you my before and after code...

Before: (This DIDN'T work)

<DataTemplate x:Key="MenuNodeWithChildrenTemplate">
    <StackPanel Orientation="Horizontal"
            behaviors:EventCommand.CommandToRun="{Binding OpenMenuItem}"
            behaviors:EventCommand.EventName="MouseLeftButtonUp">
        <Label Content="{Binding Title}"/>
        <Label Content="{Binding Description}"/>
    </StackPanel>
</DataTemplate>

After: (This DOES work)

<DataTemplate x:Key="MenuNodeWithChildrenTemplate">
    <StackPanel Orientation="Horizontal"
            behaviors:EventCommand.CommandToRun="{Binding Path=DataContext.OpenMenuItem, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type ItemsControl}}}"
            behaviors:EventCommand.EventName="MouseLeftButtonUp">
        <Label Content="{Binding Title}"/>
        <Label Content="{Binding Description}"/>
    </StackPanel>
</DataTemplate>

You'll obviously have to tweak the parameters of the Relative Source to your specific situation. It seems that, for whatever reason, attached properties don't inherit the data context, so you have to tell if how to.