wpfcustom-controls

Styling A Custom Control


If this question isn't clear, I'll update it. I'm not entirely sure how to ask what I want, so bear with me.

I have created a custom Navigation Control

It has the Generic.xaml file and the code behind C# files.

I don't understand how styling works when I distribute this as an assembly.

For example, I have brushes defined in the Generic.xaml file. The components that make up my control, TreeView, ListBox, Hyperlink, etc, all have default brushes that I defined for them inside the Generic.xaml. As defaults they work fine.

But when I give the compiled assembly to another developer, and they want to change the colors of the control, how do they know what brushes are in the control?

Do I have to explicitly name each control in my control? Given my control, how would you know how to change the color of a part of the control?


Solution

  • For the most convenience, your custom control should expose dedicated dependency properties of type Brush in the likes of Background or Foreground, then the developer can replace colors and brushes that way. To keep the class API clean, you should only expose the most anticipated customized brushes.

    If you don't provide such properties, the developer has to override the default template in order to customize such layout details. That's the usual case. Usually, the developer would use the XAML designer to extract the original ControlTemplate and customize it. Of all options, this can be considered the least convenient solution.

    Another very convenient option is to define static resource keys that the developers can override. The biggest drawback is that these keys must be documented, so that the developer can know about their existence. If you use this kind of customization support as a design convention, your developers would naturally expect the definition of such keys and, for example, check the static class members to know such resource keys.
    The following example shows how to define and override such resource keys using the ComponentResourceKey. This example defines them using C#, but it's also possible to use XAML to define a ´ComponentResourceKey. Note, you can use static resource keys to enable dynamic resource overriding with any resources e.g. Style`, not only colors and brushes.

    MyCustomControl.cs
    First, define the static resource keys:

    public class MyCustomControl : Control
    {
      public static ComponentResourceKey MouseOverColorKey = new ComponentResourceKey(typeof(MyCustomControl), "MouseOverColor");
      public static ComponentResourceKey MouseOverBrushKey = new ComponentResourceKey(typeof(MyCustomControl), "MouseOverBrush");
    
      static MyCustomControl()
      {
        DefaultStyleKeyProperty.OverrideMetadata(typeof(MyCustomControl), new FrameworkPropertyMetadata(typeof(MyCustomControl)));
      }
    }
    

    Generic.xaml
    Next, use the static resource keys to replace the classic string values.
    It's important to reference this resource using DynamicResource in the default style and template. Otherwise, the developer won't be able to override the value (color resource) of the static key.

    <!-- Define the color resources. 
         It's common best practice to define a Color 
         and then a Brush (e.g. SolidColorBrush) that references this Color resource. 
         This way, the developer can define his own brushes (e.g., GradientBrush or simply to set an opacity) 
         but use the original Color resource. -->
    <Color x:Key="{x:Static local:MyCustomControl.MouseOverColorKey}">Red</Color>
    <SolidColorBrush x:Key="{x:Static local:MyCustomControl.MouseOverBrushKey}"
                     Color="{DynamicResource {x:Static local:MyCustomControl.MouseOverColorKey}}" />
    
    <Style TargetType="local:MyCustomControl">
      <Setter Property="Template">
        <Setter.Value>
          <ControlTemplate TargetType="local:MyCustomControl">
    
            <!-- It's important to reference the resource 
                 using the DynamicResource markup extension -->
            <Border Background="{DynamicResource {x:Static local:MyCustomControl.MouseOverBrushKey}}" />
          </ControlTemplate>
        </Setter.Value>
      </Setter>
    </Style>
    

    MainWindow.xaml
    Finally, override the default mouse over color resource using the static MyCustomControl.MouseOverColorKey:

    <Window>
      <Window.Resources>
        <Color x:Key="{x:Static local:MyCustomControl.MouseOverColorKey}">Orange</Color>
      </Window.Resources>
    
      <local:MyCustomControl Height="200" Width="200" />
    </Window>
    

    Or override the brush by using the static MyCustomControl.MouseOverBrushKey:

    <Window>
      <Window.Resources>
      <SolidColorBrush x:Key="{x:Static local:MyCustomControl.MouseOverBrushKey}"
                       Color="Yellow" />
      </Window.Resources>
    
      <local:MyCustomControl Height="200" Width="200" />
    </Window>