14

I have the following Button and Style in WPF and I need to generalize the Binding in the DataTrigger section because I have near 10 similar buttons in the same Window and each button should be binded to a different property (SelectedPositions, SelectedAgencies, ....). Is it possible to implement?

    <Button x:Name="btnPosition"
            Grid.Row="0"
            Grid.Column="0"
            HorizontalAlignment="Left"
            VerticalAlignment="Center"
            Command="{Binding PositionFilterCommand}"
            Content="{l:Translate position}"
            Style="{StaticResource NewButtonStyle}" />

    <Style x:Key="NewButtonStyle" TargetType="{x:Type Button}">
        <Setter Property="Foreground" Value="White" />
        <Setter Property="Height" Value="22" />
        <Setter Property="Width" Value="Auto" />
        <Setter Property="FontFamily" Value="OpenSans" />
        <Setter Property="FontSize" Value="13" />
        <Setter Property="Cursor" Value="Hand" />
        <Setter Property="Margin" Value="10,2,10,0" />
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="Button">
                    <Border CornerRadius="3">
                        <Grid x:Name="gridButton" Background="#54728e">
                            <Grid.ColumnDefinitions>
                                <ColumnDefinition Width="Auto" />
                                <ColumnDefinition Width="*" />
                            </Grid.ColumnDefinitions>
                            <Image x:Name="img"
                                   Grid.Column="0"
                                   Width="24"
                                   Height="24"
                                   Source="Img/tick-white.png"
                                   Visibility="Visible" />
                            <Rectangle x:Name="rect"
                                       Grid.Column="1"
                                       Fill="#54728e"
                                       RadiusX="3"
                                       RadiusY="3" />
                            <ContentPresenter Grid.Column="1"
                                              Margin="5,0,5,0"
                                              HorizontalAlignment="Stretch"
                                              VerticalAlignment="Center" />
                        </Grid>
                    </Border>
                    <ControlTemplate.Triggers>
                        <DataTrigger Binding="{Binding SelectedPositions}" Value="{x:Static sys:String.Empty}">
                            <Setter TargetName="rect" Property="Fill" Value="#8bbcdf" />
                            <Setter TargetName="img" Property="Visibility" Value="Collapsed" />
                            <Setter TargetName="gridButton" Property="Background" Value="#8bbcdf" />
                        </DataTrigger>
                    </ControlTemplate.Triggers>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
Viv
  • 17,170
  • 4
  • 51
  • 71
MauroBahia
  • 143
  • 1
  • 1
  • 6
  • Are all these properties (SelectedPositions, SelectedAgencies, ....) of the same type? If so you can either use an attached property or normal dependency property to the button and check that value in a common Style and on the control have this DP bound to each respective source property. You could also use the `Tag` property to do the same thing, although it's something that's generally frowned upon. – Viv Jul 11 '13 at 16:28
  • 1
    Yes, these properties are of the same type (Strings). Each Button has its own property, content and command. Please, could you provide me an example of what you explained?. Thanks in advance. – MauroBahia Jul 11 '13 at 17:25
  • Yeh sure, added an example of each. Take your pick and look into it further as well for what's the best for your requirement. – Viv Jul 11 '13 at 18:05

1 Answers1

36

could you provide me an example of what you explained?

Sure,

1 - Using Tag

In your Style have your DataTrigger as:

<DataTrigger Binding="{Binding Path=Tag,
                                RelativeSource={RelativeSource Self}}"
              Value="{x:Static sys:String.Empty}">
  ...
</DataTrigger>

as for usage:

<Button x:Name="btnPosition"
            Grid.Row="0"
            Grid.Column="0"
            HorizontalAlignment="Left"
            VerticalAlignment="Center"
            Command="{Binding PositionFilterCommand}"
            Content="{l:Translate position}"
            Tag="{Binding SelectedPositions}"
            Style="{StaticResource NewButtonStyle}" />

2 - Using Attached Property:

"local:" refers to the xaml namespace alias of your application or if you use different namespaces, the namespace where MyCustomPropertyCollection is declared.

code-behind:

public class MyCustomPropertyCollection {
  public static readonly DependencyProperty SomeStringProperty =
    DependencyProperty.RegisterAttached(
      "SomeString",
      typeof(string),
      typeof(MyCustomPropertyCollection),
      new FrameworkPropertyMetadata(string.Empty));

  public static void SetSomeString(UIElement element, string value) {
    element.SetValue(SomeStringProperty, value);
  }

  public static string GetSomeString(UIElement element) {
    return (string)element.GetValue(SomeStringProperty);
  }
}

Style.DataTrigger

<DataTrigger Binding="{Binding Path=(local:MyCustomPropertyCollection.SomeString),
                                RelativeSource={RelativeSource Self}}"
              Value="{x:Static sys:String.Empty}">
  ...
</DataTrigger>

usage:

<Button x:Name="btnPosition"
            Grid.Row="0"
            Grid.Column="0"
            HorizontalAlignment="Left"
            VerticalAlignment="Center"
            Command="{Binding PositionFilterCommand}"
            Content="{l:Translate position}"
            local:MyCustomPropertyCollection.SomeString="{Binding SelectedPositions}"
            Style="{StaticResource NewButtonStyle}" />

3 - Normal Dependency Property

custom Button class:

public class MyButton : Button {
  public static readonly DependencyProperty SomeStringProperty =
    DependencyProperty.Register(
      "SomeString",
      typeof(string),
      typeof(MyButton),
      new FrameworkPropertyMetadata(string.Empty));

  public string SomeString {
    get {
      return (string)GetValue(SomeStringProperty);
    }
    set {
      SetValue(SomeStringProperty, value);
    }
  }
}

Style in xaml not only needs DataTrigger updated but Style definition too.

so switch

<Style x:Key="NewButtonStyle" TargetType="{x:Type Button}">

to

<Style x:Key="NewButtonStyle" TargetType="{x:Type local:MyButton}">

Style.DataTrigger

<DataTrigger Binding="{Binding Path=SomeString,
                                RelativeSource={RelativeSource Self}}"
              Value="{x:Static sys:String.Empty}">
  ...
</DataTrigger>

usage:

<local:MyButton x:Name="btnPosition"
            Grid.Row="0"
            Grid.Column="0"
            HorizontalAlignment="Left"
            VerticalAlignment="Center"
            Command="{Binding PositionFilterCommand}"
            Content="{l:Translate position}"
            SomeString="{Binding SelectedPositions}"
            Style="{StaticResource NewButtonStyle}" />

Tag approach is frowned upon. "Attached Property" is easier to implement but isn't as clear of a indicator of dependencies as a custom class with a normal DP and AP also gets way overused. Take your pick for what you'd prefer.

Viv
  • 17,170
  • 4
  • 51
  • 71
  • What would the `Value="..."` look like if I were trying to trigger on a specific string, rather than `String.Empty`? – phonetagger Jul 24 '15 at 23:35
  • And if it wouldn't be too much to ask, could you take a look at my current question and tell me how to do a `DataTrigger` based on an enum? http://stackoverflow.com/q/31615946/1245420 – phonetagger Jul 24 '15 at 23:39