Search code examples
c#wpfxamldata-bindingcustom-controls

How do I pass Properties from a CustomControl to it's UserControls in C# WPF?


I currently have a problem with some DependencyProperties in my code. I made my own Control which is supposed to be used in many instances of my app. I was able to bind a boolean DependencyProperty and use it's value in the control itself. Now I need to bind a Style to it's children.

Here is how I want to bind the property:

<Window>
    <Window.Resources>
        <Style x:Key="style" TargetType="Button">
            <Setter Property="Background" Value="Red" />
        </Style>
    </Window.Resources>
    
    <Grid>
        <local:MyControl ButtonStyle="{StaticResource style}"/>
    </Grid>
</Window>

The value will be set in MyControl.cs this way:

public class MyControl : Control
{
    public static readonly DependencyProperty ButtonStyleProperty= DependencyProperty.Register(
        "ButtonStyle", typeof(Style), typeof(MyControl), new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.Inherits));

    static MyControl()
    {
        DefaultStyleKeyProperty.OverrideMetadata(typeof(MyControl), new FrameworkPropertyMetadata(typeof(MyControl)));
    }

    public Style ButtonStyle
    {
        get => (Style)GetValue(ButtonStyleProperty);
        set => SetValue(ButtonStyleProperty, value);
    }
}

The Generic.xaml of my custom control owns an UserControl which needs the ButtonStyle. I'm trying to pass it in the Generic.xaml like this:

<controller:ControllerView ButtonStyle="{Binding ButtonStyle}"/>

Here is the code-behind for the ControllerView. For some reason the setter is never accessed.

public partial class ControllerView : UserControl
{
    public static readonly DependencyProperty ButtonStyleProperty = DependencyProperty.Register(
        "ButtonStyle", typeof(Style), typeof(ControllerView), new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault));

    public ControllerView()
    {
        InitializeComponent();
    }

    public Style ButtonStyle
    {
        get => (Style)GetValue(ButtonStyleProperty);
        set => SetValue(ButtonStyleProperty, value);
    }
}

And it's supposed to be used in the ControllerView.xaml like this:

<Button Name="Button" Style="{Binding ButtonStyle}"
        Content="{Binding Text, Mode=OneTime}" Command="{Binding Command, Mode=OneTime}"
        Margin="1" FontSize="24">

If anyone could tell me why it doesn't work this way and maybe suggest a solution to my code or can tell me how one would normally do this, that'd be great.


Solution

  • A Binding will use the DataContext as source by default, from the documentation:

    By default, bindings inherit the data context specified by the DataContext property, if one has been set.

    Dependency properties are not part of the DataContext. They are defined on controls. You can use a RelativeSource binding or in some cases an ElementName to refer to another control for binding.

    If I understood your decription correctly, MyControl contains a ControllerView and its ButtonStyle property should be bound to the ButtonStyle property of the parent MyControl.

    <controller:ControllerView ButtonStyle="{Binding ButtonStyle, RelativeSource={RelativeSource AncestorType={x:Type local:MyControl}}}"/>