Antony Woods
Antony Woods

Reputation: 4462

Variables in XAML Style

I have the following Style:

<Style x:Key="ButtonBase" TargetType="Button">
        <Setter Property="Background" Value="#FF007BFF"/>
        <Setter Property="Foreground" Value="White"/>
        <Setter Property="Template">
            <Setter.Value>
                 <ControlTemplate TargetType="Button">
                      <ControlTemplate.Triggers>
                           <Trigger Property="IsPressed" Value=">
                                <Setter Property="Background" Value="Yellow" />
                                <Setter Property="Foreground" Value="Black" />
                           </Trigger>
                           <Trigger Property="IsPressed" Value="False">
                                <Setter Property="Background" Value="#FF007BFF" />
                                <Setter Property="Foreground" Value="White" />
                           </Trigger>
                      </ControlTemplate.Triggers>
                 </ControlTemplate>
             <Setter.Value>
        </Setter>
</Style>

and an inherited Style:

<Style x:Key="ElementButton" TargetType="Button" BasedOn="{StaticResource ButtonBase}">
        <Setter Property="Margin" Value="10"/>
        <Setter Property="Height" Value="200"/>
</Style>

What I'd like to do is be able to set an arbitrary variable in the base style:

<Setter Variable="HoverColor" Value="Pink"/>

Then I'd be able to use my triggers as such:

<Trigger Property="IsPressed" Value=">
    <Setter Property="Background" Value="{TemplateBinding HoverColor}" />
    <Setter Property="Foreground" Value="Black" />
</Trigger>

And finally, I could then override it in my inherited style:

<Style x:Key="ElementButton" TargetType="Button" BasedOn="{StaticResource ButtonBase}">
    <Setter Property="Margin" Value="10"/>
    <Setter Property="Height" Value="200"/>
    <Setter Variable="HoverColor" Value="Red"/>
</Style>

Is there a way to achieve this? I've already looked at static resources but these can't be overridden. Also, I cannot use anything that requires a code-behind because I don't have one!

Upvotes: 4

Views: 2592

Answers (1)

Jason Frank
Jason Frank

Reputation: 3902

Its a good question and I've fought through this sort of thing as well. There may be some kind of XAML-only approach that could work, but I have a feeling that if there is, it would feel pretty kludgy. I have a couple of suggestions of how achieve what you want.

First, a quick observation. You say you "don't have a code-behind" and that your view is "XAML-only". Well, I've never seen a UserControl View that doesn't have any code-behind file at least, so I'm assuming you mean you just don't want to put any code in there (other than the obligatory InitializeComponent()). Having said that, the approaches that I'll outline won't require code in your code-behind files.

At the end of the day, it sounds like what you really want is define some custom "variables". These suggestions do just that, albeit maybe not in the way that you originally envisioned doing so.

The first approach that would solve your problem is to subclass the control that you are interested in styling and add any custom dependency properties to it. For example, you could subclass Button, to say something like ButtonWithMyVariables. One of those custom dependency properties would be called "HoverColor", of type Color, or perhaps more appropriately, "HoverBrush" of type Brush (if you're wanting to just apply it directly to the background or foreground property). Then your base Style can set HoverColor to whatever it wants, and your inherited Style can override it, or you can override it directly on the element in your XAML. I'm not providing code samples for this approach (right now, unless requested) since this is a more commonly-used approach that I'm guessing you're already familiar with.

The second approach would be to define a custom attached property. I've not seen this approach used as much for dealing strictly with Styling issues, perhaps because for the attached "behavior" to fully do its job in this case, authors have used Style files to react (bind to) and apply visual changes based on the attached property, rather than the code in the attached property changed callback doing something stylistically (but I suppose it could still be done that way). However, this approach feels "lighter weight" to many, since you don't need to subclass any existing controls.

An example of this second approach can be found in the MahApps.Metro library, specifically the TextboxHelper class (which houses attached properties) and the Controls.TextBox.xaml style file (which binds to those attached properties). For example, we see in the control template for the TextBox, this line that makes use of the Watermark attached property:

<TextBlock x:Name="Message" 
           Text="{TemplateBinding Controls:TextboxHelper.Watermark}" Visibility="Collapsed"
                                   Foreground="{TemplateBinding Foreground}" IsHitTestVisible="False" Opacity="0.6" HorizontalAlignment="Left" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
                                   Margin="6,0,0,0"/>

Now you can imagine that you could set that Watermark value in a base style to something:

<Setter Property="Controls:TextboxHelper.Watermark" 
        Value="My helpful watermark for all!"/>

And then override it in an inherited style:

<Setter Property="Controls:TextboxHelper.Watermark" 
        Value="A more specific watermark!"/>

With either approach, we can define any "variable" we want and easily set them in a Style setter, override them in inherited styles, TemplateBind to them within control templates, or Trigger off of them.

Upvotes: 2

Related Questions