Home > Back-end >  Binding DependencyProperty for property is substyle ComboBox
Binding DependencyProperty for property is substyle ComboBox

Time:10-23

I have a CustomComboBox component that inherits from ComboBox with its style copied to another resource dictionary xaml file.

The question is, can I or how do I bind a property from the sub style for ComboBoxToggleButton used in ComboBoxTemplate used in CustomComboBoxStyle?

The property is: GryphlBrush in
<Path x:Name="arrow" Stroke="{Binding GryphlBrush}" ...

The code example below:

<Style x:Key="ComboBoxToggleButton" TargetType="{x:Type ToggleButton}">
    <!-- ... -->
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type ToggleButton}">
                Border x:Name="templateRoot" SnapsToDevicePixels="true" Background="Transparent" BorderThickness="0" BorderBrush="Transparent" CornerRadius="4">
                    <Border x:Name="splitBorder" Width="{DynamicResource {x:Static SystemParameters.VerticalScrollBarWidthKey}}" SnapsToDevicePixels="true" Margin="0" HorizontalAlignment="Right" BorderThickness="1" BorderBrush="Transparent">
                        <Path x:Name="arrow" Stroke="{Binding GryphlBrush}" VerticalAlignment="Center" Margin="0" HorizontalAlignment="Center" Fill="{StaticResource ComboBox.Static.Glyph}" Data="F1 M 0,0 L 2.667,2.66665 L 5.3334,0 L 5.3334,-1.78168 L 2.6667,0.88501 L0,-1.78168 L0,0 Z"/>
                    </Border>
                </Border>
                <!-- ... -->
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

<ControlTemplate x:Key="ComboBoxTemplate" TargetType="{x:Type ComboBox}">
    <Border BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" CornerRadius="4">
        <Grid x:Name="templateRoot" SnapsToDevicePixels="true">
            <!-- ... -->
            <ToggleButton x:Name="toggleButton" BorderBrush="Transparent" BorderThickness="0" Background="Transparent" Grid.ColumnSpan="2" IsChecked="{Binding IsDropDownOpen, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}" Style="{StaticResource ComboBoxToggleButton}"/>
            <!-- ... -->
        </Grid>
    </Border>
    <!-- ... -->
</ControlTemplate>

<Style x:Key="BorderedComboBoxStyles" TargetType="{x:Type components:BorderedComboBox}">
    <Setter Property="Template" Value="{StaticResource ComboBoxTemplate}"/>
</Style>

In code behind I only define the dependency property, getters and setters:

public class BorderedComboBox : ComboBox
{
    public static readonly DependencyProperty GryphlBrushDP = DependencyProperty.Register(
        "GryphlBrush", typeof(Brush), typeof(BorderedComboBox));
    
    private Brush _gryphlBrush;
    
    public Brush GryphlBrush
    {
        get => _gryphlBrush;
        set => _gryphlBrush = value;
    }
}

CodePudding user response:

You should adhere to the naming conventions for dependency properties and their getters and setters. Furthermore using nameof for the property can prevent errors when renaming.

The name of the identifier field that you use to store the name and characteristics of the dependency property must be the Name you chose for the dependency property as part of the Register call, appended by the literal string Property. [...]

If you fail to follow this naming pattern, designers might not report your property correctly, and certain aspects of property system style application might not behave as expected.

Hence your dependency property definition should look like this:

public class BorderedComboBox : ComboBox
{
   public static readonly DependencyProperty GryphlBrushProperty = DependencyProperty.Register(
      nameof(GryphlBrush), typeof(Brush), typeof(BorderedComboBox));

   private Brush _gryphlBrush;

   public Brush GryphlBrush
   {
      get => _gryphlBrush;
      set => _gryphlBrush = value;
   }
}

If you want to bind the GryphlBrush property, in the ToggleButton control template, you could:

  • Create a custom toggle button and add a dependency property to it and then bind it to the GryphlBrush property on your BorderedComboBox.

  • If the toggle button is only used in this context within the combo box, apply a RelativeSource binding with AncestorType, since the BorderedComboBox is a parent of the toggle button.

    <Style x:Key="ComboBoxToggleButton" TargetType="{x:Type ToggleButton}">
       <!--...-->
       <Setter Property="Template">
          <Setter.Value>
             <ControlTemplate TargetType="{x:Type ToggleButton}">
                <Border x:Name="templateRoot" SnapsToDevicePixels="true" Background="Transparent" BorderThickness="0" BorderBrush="Transparent" CornerRadius="4">
                <Border x:Name="splitBorder" Width="{DynamicResource {x:Static SystemParameters.VerticalScrollBarWidthKey}}" SnapsToDevicePixels="true" Margin="0" HorizontalAlignment="Right" BorderThickness="1" BorderBrush="Transparent">
                   <!--<Path x:Name="arrow" Stroke="{Binding GryphlBrush, RelativeSource={RelativeSource AncestorType={x:Type local:BorderedComboBox}}}" VerticalAlignment="Center" Margin="0" HorizontalAlignment="Center" Fill="{StaticResource ComboBox.Static.Glyph}" Data="F1 M 0,0 L 2.667,2.66665 L 5.3334,0 L 5.3334,-1.78168 L 2.6667,0.88501 L0,-1.78168 L0,0 Z"/>-->
                   <Path x:Name="arrow" Stroke="{Binding GryphlBrush, RelativeSource={RelativeSource AncestorType={x:Type local:BorderedComboBox}}}" VerticalAlignment="Center" Margin="0" HorizontalAlignment="Center" Data="F1 M 0,0 L 2.667,2.66665 L 5.3334,0 L 5.3334,-1.78168 L 2.6667,0.88501 L0,-1.78168 L0,0 Z"/>
                </Border>
                </Border>
                <!--...-->
             </ControlTemplate>
          </Setter.Value>
       </Setter>
    </Style>
    
  • Related