sixth_way
sixth_way

Reputation: 41

WPF pass ElementName as parameter into a Style

I have 3 TextBlocks whose visibility property depends on a validation rule on 3 different elements (TextBoxes). How can I refactor this code so that the ElementName is abstracted away and I just have one style defined instead of 3? Is it possible to pass in ElementName as a parameter at the TextBlocks which this style is applied to?

    <Style x:Key="textBlock_stockPriceWarning" TargetType="TextBlock">
        <Style.Triggers>
            <DataTrigger Binding="{Binding Path=(Validation.HasError), ElementName=stockPriceTextBox}" Value="True">
                <Setter Property="Visibility" Value="Visible"/>
            </DataTrigger>
            <DataTrigger Binding="{Binding Path=(Validation.HasError), ElementName=stockPriceTextBox}" Value="False">
                <Setter Property="Visibility" Value="Collapsed"/>
            </DataTrigger>
        </Style.Triggers>
    </Style>
    <Style x:Key="textBlock_taxRateWarning" TargetType="TextBlock">
        <Style.Triggers>
            <DataTrigger Binding="{Binding Path=(Validation.HasError), ElementName=ordinaryTaxRateBox}" Value="True">
                <Setter Property="Visibility" Value="Visible"/>
            </DataTrigger>
            <DataTrigger Binding="{Binding Path=(Validation.HasError), ElementName=ordinaryTaxRateBox}" Value="False">
                <Setter Property="Visibility" Value="Collapsed"/>
            </DataTrigger>
        </Style.Triggers>
    </Style>
    <Style x:Key="textBlock_capGainsTaxRateWarning" TargetType="TextBlock">
        <Style.Triggers>
            <DataTrigger Binding="{Binding Path=(Validation.HasError), ElementName=capitalGainsTaxRateBox}" Value="True">
                <Setter Property="Visibility" Value="Visible"/>
            </DataTrigger>
            <DataTrigger Binding="{Binding Path=(Validation.HasError), ElementName=capitalGainsTaxRateBox}" Value="False">
                <Setter Property="Visibility" Value="Collapsed"/>
            </DataTrigger>
        </Style.Triggers>
    </Style>

**Update: Per Mark's advice below, I tried using the following ControlTemplate and attached property definition:

    <ControlTemplate x:Key="LabelWarning" TargetType="Label">
        <Border BorderBrush="AliceBlue"/>
        <ControlTemplate.Triggers>
            <DataTrigger Binding="{Binding Path=(Validation.HasError), 
                ElementName={Binding Path=(views:ElementNameHelper.ElementAlias), RelativeSource={RelativeSource Mode=TemplatedParent}}}" Value="True">
                <Setter Property="Visibility" Value="Visible"/>
            </DataTrigger>
            <DataTrigger Binding="{Binding Path=(Validation.HasError), 
                ElementName={Binding Path=(views:ElementNameHelper.ElementAlias), RelativeSource={RelativeSource Mode=TemplatedParent}}}" Value="False">
                <Setter Property="Visibility" Value="Collapsed"/>
            </DataTrigger>
        </ControlTemplate.Triggers>
    </ControlTemplate>

public static class ElementNameHelper
{
    public static readonly DependencyProperty ElementAliasProperty =
        DependencyProperty.RegisterAttached("ElementAlias",
            typeof(string), typeof(ElementNameHelper));

    public static string GetElementAlias(DependencyObject obj)
    {
        return obj.GetValue(ElementAliasProperty).ToString();
    }

    public static void SetElementAlias(DependencyObject target, string value)
    {
        target.SetValue(ElementAliasProperty, value);
    }
}

And invoking it like this:

        <Label Content="Stock price should be a positive number." 
               FontSize="12" HorizontalAlignment="Center"
               VerticalAlignment="Center" Foreground="Red"
                Margin="1" 
               views:ElementNameHelper.ElementAlias="stockPriceTextBox"
               Template="{StaticResource LabelWarning}"/>

But then the XAML compiler tells me that: A 'Binding' cannot be set on the 'ElementName' property of type 'Binding'. A 'Binding' can only be set on a DependencyProperty of a DependencyObject. Do I have this setup correctly and is it just the case that you simply can't use this approach on ElementName? Or am I missing something with the attached property definition?

Upvotes: 1

Views: 517

Answers (1)

Mark Feldman
Mark Feldman

Reputation: 16119

Usual way to do this is with an attached property.

If you just want a cheap-n-easy solution then you can use Tag, which is described in the documentation as "an arbitrary object value that can be used to store custom information". An attached property is the better way to do it though.

Upvotes: 1

Related Questions