3
votes

Ok, so I'm relatively new to WPF and I'm having a bit of a problem with my usercontrol which contains a custom button.

Everything was working fine until I tried binding the content of the custom button to the usercontrol content.

UserControl Code:

<UserControl
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:OP"
mc:Ignorable="d"
x:Class="OP.GamepadButton"
x:Name="GamePadBtn"
d:DesignWidth="100" d:DesignHeight="100">
<UserControl.Resources>
    <Style x:Key="GamepadButtonStyle" TargetType="{x:Type Button}">
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type Button}">
                    <Grid>
                        <Rectangle x:Name="rectangle" Fill="{TemplateBinding Background}" StrokeThickness="1" Opacity="0.8" Stroke="{TemplateBinding BorderBrush}"/>
                        <ContentPresenter Content="{TemplateBinding Content}" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" 
                        RecognizesAccessKey="True" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" 
                        VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
                    </Grid>
                    <ControlTemplate.Triggers>
                        <Trigger Property="IsFocused" Value="True"/>
                        <Trigger Property="IsDefaulted" Value="True"/>
                        <Trigger Property="IsMouseOver" Value="True"/>
                        <Trigger Property="IsPressed" Value="True"/>
                        <Trigger Property="IsEnabled" Value="False"/>
                    </ControlTemplate.Triggers>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
</UserControl.Resources>

<Grid x:Name="LayoutRoot" Background="Transparent">
    <Grid.RowDefinitions>
        <RowDefinition/>
    </Grid.RowDefinitions>
    <Button Content="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:GamepadButton}}, Path=Content}" 
    Background="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:GamepadButton}}, Path=Background}" 
    BorderBrush="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:GamepadButton}}, Path=BorderBrush}" 
    Grid.Row="1" Style="{DynamicResource GamepadButtonStyle}" />
</Grid>

And I declare the usercontrol in my MainWindow the following way:

<local:GamepadButton Height="60" Width="60" Background="#DAF0FC" BorderBrush="#2c3e50" Content="Button"/>

The result is the following:

Result 1

Also, I noticed now that for some reason this also breaks the BorderBrush binding but not the Background binding.


The code does work if I don't declare any content property for the Usercontrol and set the button Content directly:

UserControl:

<Button Content="Button" 
    Background="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:GamepadButton}}, Path=Background}" 
    BorderBrush="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:GamepadButton}}, Path=BorderBrush}" 
    Grid.Row="1" Style="{DynamicResource GamepadButtonStyle}" />

MainWindow:

<local:GamepadButton Height="60" Width="60" Background="#DAF0FC" BorderBrush="#2c3e50" />

Which gives me the button style I want:

Result 2


So my question is.

How do I make the ContentPresenter for the button bind to the content declared for the usercontrol?

1

1 Answers

2
votes

The problem you are facing is that you can't bind to content from inside that content. Your button inside user control is a current content for control. And at your output window you see this lines

System.Windows.Data Error: 4 : Cannot find source for binding with reference 'RelativeSource FindAncestor, AncestorType='OP.GamepadButton', AncestorLevel='1''. BindingExpression:Path=Tag; DataItem=null; target element is 'Button' (Name=''); target property is 'Content' (type 'Object')
System.Windows.Data Error: 4 : Cannot find source for binding with reference 'RelativeSource FindAncestor, AncestorType='OP.GamepadButton', AncestorLevel='1''. BindingExpression:Path=Background; DataItem=null; target element is 'Button' (Name=''); target property is 'Background' (type 'Brush')
System.Windows.Data Error: 4 : Cannot find source for binding with reference 'RelativeSource FindAncestor, AncestorType='OP.GamepadButton', AncestorLevel='1''. BindingExpression:Path=BorderBrush; DataItem=null; target element is 'Button' (Name=''); target property is 'BorderBrush' (type 'Brush')

If you change that to some another property like Tag then your style will applied correctly.

I think that you can solve your task with using style only, but if you really need new UserControl then you can inherit it directly from button, not UserControl.