wpfdata-bindingbindingstaticresourcedynamicresource

WPF: Passing a data parameter into a style theme?


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.


Solution

  • 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>