WPF: Passing a data parameter into a style theme?

155 views Asked by At

I have a style that I re-use for multiple textboxes. Within the style, I define a control template - and within that control template, I have some triggers. I want to be able to pass a Data Property into one of those triggers from the View.

Here is a shortened version of my current style defined in a Resource Dictionary:

<Style x:Key="TextBoxTheme" TargetType="{x:Type TextBox}">
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type TextBox}">
                <Border x:Name="border">
                    <ScrollViewer x:Name="PART_ContentHost" Focusable="false/>
                </Border>
                <ControlTemplate.Triggers>
                    <DataTrigger Binding="{Binding PARAMETER}" Value="true">
                        <Setter Property="BorderBrush" TargetName="border" Value="Red"/>
                    </DataTrigger>
                </ControlTemplate.Triggers>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

There is one DataTrigger "(Binding PARAMETER}" that will determine if the border is set to red or not. Depending on where I use this textbox, I want to bind that to a different variable.

I want to be able to pass that as a parameter from my View. I'm imagining something like this (just an idea of how I think it should work, but this doesn't work):

<TextBox Style="{StaticResource TextBoxTheme, Parameter={Binding PARAMETER}}"/>
<TextBox Style="{StaticResource TextBoxTheme, Parameter={Binding DIFFERENT_PARAMETER}}"/>

Since I can't figure out how to do that yet, I am almost rewriting the same code every time I call it.

Example (Using this style with my 'Name' textbox):

<TextBox Text="{Binding Name}">
<TextBox.Style>
    <Style TargetType="TextBox" BasedOn="{StaticResource TextBoxTheme}">
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type TextBox}">
                    <Border x:Name="border">
                        <ScrollViewer x:Name="PART_ContentHost" Focusable="false"/>
                    </Border>
                    <ControlTemplate.Triggers>
                        <DataTrigger Binding="{Binding DuplicateName}" Value="False">
                            <Setter Property="BorderBrush" TargetName="border" Value="Green"/>
                        </DataTrigger>
                    </ControlTemplate.Triggers>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
</TextBox.Style>

Example (When I want to use this same style with a different binding):

<TextBox Text="{Binding IpAddress}">
<TextBox.Style>
    <Style TargetType="TextBox" BasedOn="{StaticResource TextBoxTheme}">
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type TextBox}">
                    <Border x:Name="border">
                        <ScrollViewer x:Name="PART_ContentHost" Focusable="false"/>
                    </Border>
                    <ControlTemplate.Triggers>
                        <DataTrigger Binding="{Binding DuplicateIP}" Value="False">
                            <Setter Property="BorderBrush" TargetName="border" Value="Green"/>
                        </DataTrigger>
                    </ControlTemplate.Triggers>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
</TextBox.Style>

Is there a better way of doing this? I found an article that shows you how to pass a property into a style (https://thomaslevesque.com/2011/10/01/wpf-creating-parameterized-styles-with-attached-properties/) but I don't see it working with passing a data property.

2

There are 2 answers

1
emoacht On BEST ANSWER

In general, for reusing an existing Style, Style.Triggers are better than ControlTemplate.Triggers because Style.Triggers can be defined at an derived Style.

A simple base Style would be like:

<Style x:Key="TextBoxTheme" TargetType="{x:Type TextBox}" BasedOn="{StaticResource {x:Type TextBox}}">
    <Setter Property="BorderThickness" Value="2"/>
</Style>

Then you can define two derived Styles of different Style.Triggers.

<Style x:Key="TextBoxTheme1" TargetType="{x:Type TextBox}" BasedOn="{StaticResource TextBoxTheme}">
    <Style.Triggers>
        <DataTrigger Binding="{Binding PARAMETER}" Value="True">
            <Setter Property="BorderBrush" Value="Red"/>
        </DataTrigger>
    </Style.Triggers>
</Style>

<Style x:Key="TextBoxTheme2" TargetType="{x:Type TextBox}" BasedOn="{StaticResource TextBoxTheme}">
    <Style.Triggers>
        <DataTrigger Binding="{Binding DuplicateName}" Value="False">
            <Setter Property="BorderBrush" Value="Green"/>
        </DataTrigger>
    </Style.Triggers>
</Style>

Or if you want to specify Style.Triggers for each TextBox, it would be easier to define a Style inside each TextBox.

<TextBox Text="example">
    <TextBox.Style>
        <Style TargetType="{x:Type TextBox}" BasedOn="{StaticResource TextBoxTheme}">
            <Style.Triggers>
                <DataTrigger Binding="{Binding DuplicateIP}" Value="False">
                    <Setter Property="BorderBrush" Value="Green"/>
                </DataTrigger>
            </Style.Triggers>
        </Style>
    </TextBox.Style>
</TextBox>
0
Andy On

If you particularly want this to work with the one style then you could use an attached dependency property.

https://learn.microsoft.com/en-us/dotnet/desktop/wpf/properties/custom-dependency-properties?view=netdesktop-7.0

You can bind that to whatever you like in the control.

<TextBox Style="{StaticResource TextBoxTheme}" 
         local:Attached.AbstractedProperty="{Binding .....}"/>

You then reference that known property in your style

<DataTrigger Binding="{Binding Path=(local:Attached.AbstractedProperty), RelativeSource={RelativeSource Self}}" Value="False">
      <Setter Property="BorderBrush" Value="Green"/>