Alex
Alex

Reputation: 65

WPF ControlTemplate bindings and triggers

I've defined this style :

<Style x:Key="ButtonIcon" TargetType="{x:Type local:ButtonIcon}">
        <Setter Property="Background" Value="Transparent" />
        <Setter Property="FocusVisualStyle" Value="{x:Null}" />
        <Setter Property="Padding" Value="0" />
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type local:ButtonIcon}">
                    <Path x:Name="IconPath" Stretch="Uniform" Height="10" Margin="5"
                          Fill="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=IconFill}" 
                          Stroke="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=IconStroke}" 
                          Data="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=IconData}" />
                </ControlTemplate>
            </Setter.Value>
        </Setter>
        <Style.Triggers>
            <Trigger Property="IsMouseOver" Value="True">
                <Setter Property="Background" Value="Transparent" />
            </Trigger>
            <Trigger Property="IsPressed" Value="True">
                <Setter Property="Background" Value="Transparent" />
                <Setter Property="IconFill" Value="Transparent"/>
            </Trigger>
        </Style.Triggers>        
    </Style>

which is basically a button with a path as content and where I've defined new properties IconFill, IconStroke and IconData.

Here is the ButtonIcon class:

Public Class ButtonIcon
    Inherits Button
    Implements INotifyPropertyChanged

    Public Event PropertyChanged As PropertyChangedEventHandler Implements INotifyPropertyChanged.PropertyChanged

    Public Sub New()
        DefaultStyleKeyProperty.OverrideMetadata(GetType(ButtonIcon), New FrameworkPropertyMetadata(GetType(ButtonIcon)))
    End Sub

    Public Shared ReadOnly Property IconFillProperty As DependencyProperty = DependencyProperty.Register("IconFill", GetType(Brush), GetType(ButtonIcon), New UIPropertyMetadata(Brushes.Transparent))

    Public Property IconFill As Brush
        Get
            Return GetValue(IconFillProperty)
        End Get
        Set(value As Brush)
            SetValue(IconFillProperty, value)
            NotifyPropertyChanged()
        End Set
    End Property

    Public Shared ReadOnly Property IconStrokeProperty As DependencyProperty = DependencyProperty.Register("IconStroke", GetType(Brush), GetType(ButtonIcon), New UIPropertyMetadata(Brushes.Transparent))

    Public Property IconStroke As Brush
        Get
            Return GetValue(IconStrokeProperty)
        End Get
        Set(value As Brush)
            SetValue(IconStrokeProperty, value)
            NotifyPropertyChanged()
        End Set
    End Property

    Public Shared ReadOnly Property IconDataProperty As DependencyProperty = DependencyProperty.Register("IconData", GetType(Geometry), GetType(ButtonIcon), New UIPropertyMetadata())

    Public Property IconData As Geometry
        Get
            Return GetValue(IconDataProperty)
        End Get
        Set(value As Geometry)
            SetValue(IconDataProperty, value)
            NotifyPropertyChanged()
        End Set
    End Property

    Protected Sub NotifyPropertyChanged(<CallerMemberName> Optional propertyName As String = "")
        RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(propertyName))
    End Sub
End Class

and usage:

<local:ButtonIcon x:Name="BtColor1" Style="{DynamicResource ButtonIcon}" 
        IconFill="{Binding MediaColor1.FillBrush}" 
        IconStroke="{Binding MediaColor1.StrokeBrush}" 
        IconData="{DynamicResource Icons.Circle}"  />

Everything is as expected except that when the button is pressed, the Path Fill color doesn't become transparent. As the Path Fill property is directly bind to the button IconFill I don't understand where is the error. I've also tried to implement INotifyPropertyChanged on ButtonIcon class but without success.

Lastly I know that IsPressed trigger is working because if I change background value to some visible color I correctly see that color when I press the button.

Upvotes: 0

Views: 525

Answers (1)

brxndxnpx
brxndxnpx

Reputation: 189

Try moving your triggers into the ControlTemplate and reference the component's name in the setters.

My ButtonIcon.xaml file:

<Style x:Key="ButtonIcon" TargetType="{x:Type local:ButtonIcon}">
  <Setter Property="Background" Value="Transparent" />
  <Setter Property="FocusVisualStyle" Value="{x:Null}" />
  <Setter Property="Padding" Value="0" />
  <Setter Property="Template">
  <Setter.Value>
      <ControlTemplate TargetType="{x:Type local:ButtonIcon}" >
      <Border Name="Border"
              Background="{TemplateBinding Background}"
              BorderThickness="{TemplateBinding BorderThickness}"
              BorderBrush="{TemplateBinding BorderBrush}"
              CornerRadius="{TemplateBinding CornerRadius}">
              
          <Path Name="IconPath" Stretch="Uniform" Height="10" Margin="5"
                  Fill="{TemplateBinding IconFill}" 
                  Stroke="{TemplateBinding IconStroke}" 
                  Data="{TemplateBinding IconData}" >
          </Path>
              
      </Border>
            
      <ControlTemplate.Triggers>
          <Trigger Property="IsMouseOver" Value="True">
              <Setter TargetName="Border" Property="Background" Value="Transparent" />
          </Trigger>
          <Trigger Property="IsPressed" Value="True">
              <Setter TargetName="Border" Property="Background" Value="Transparent" />
              <Setter TargetName="IconPath" Property="Fill" Value="Red" />
              <Setter TargetName="IconPath" Property="Stroke" Value="Red" />
          </Trigger>
      </ControlTemplate.Triggers>
              
      </ControlTemplate>
  </Setter.Value>
  </Setter>
</Style>

<Style TargetType="{x:Type local:ButtonIcon}" BasedOn="{StaticResource ButtonIcon}" />

Usage:

<local:ButtonIcon Width="100" Height="50" 
                    BorderThickness="1" 
                    Background="#2F2F2F" 
                    CornerRadius="5" 
                    BorderBrush="DimGray"
                    IconFill="#48A999"
                    IconStroke="#48A999"
                    IconData="M9.4 16.6L4.8 12l4.6-4.6L8 6l-6 6 6 6 1.4-1.4zm5.2 0l4.6-4.6-4.6-4.6L16 6l6 6-6 6-1.4-1.4z" 
                    Cursor="Hand">
</local:ButtonIcon>

The component:

Icon Button

The component when moused over and pressed:

Moused Over & Pressed

  • Note, I wrapped the Path in a Border and changed the trigger colors just to see it better on the UI while trying it out.

Here's the ButtonIcon.cs class file I used for the example:

public class ButtonIcon : Button
{
    static ButtonIcon()
    {
        DefaultStyleKeyProperty.OverrideMetadata(typeof(ButtonIcon), new FrameworkPropertyMetadata(typeof(ButtonIcon)));
    }

    public override void OnApplyTemplate()
    {
        base.OnApplyTemplate();
    }

    #region Dependency Properties

    /// <summary>
    /// The button's corner radius.
    /// </summary>
    public static readonly DependencyProperty CornerRadiusProperty =
        DependencyProperty.Register(nameof(CornerRadius), 
            typeof(CornerRadius), typeof(ButtonIcon), new PropertyMetadata(new CornerRadius(0)));
        
    public CornerRadius CornerRadius
    {
        get => (CornerRadius)GetValue(CornerRadiusProperty);
        set => SetValue(CornerRadiusProperty, value);
    }   
        
        
    /// <summary>
    /// The icon's fill color.
    /// </summary>
    public static readonly DependencyProperty IconFillProperty =
        DependencyProperty.Register(nameof(IconFill), 
            typeof(Brush), typeof(ButtonIcon), new PropertyMetadata(Brushes.Black));

    public Brush IconFill
    {
        get => (Brush)GetValue(IconFillProperty);
        set => SetValue(IconFillProperty, value);
    }

    /// <summary>
    /// The icon's stroke color.
    /// </summary>
    public static readonly DependencyProperty IconStrokeProperty =
        DependencyProperty.Register(nameof(IconStroke), 
            typeof(Brush), typeof(ButtonIcon), new PropertyMetadata(Brushes.Black));

    public Brush IconStroke
    {
        get => (Brush)GetValue(IconStrokeProperty);
        set => SetValue(IconStrokeProperty, value);
    }

    /// <summary>
    /// The icon's path data.
    /// </summary>
    public static readonly DependencyProperty IconDataProperty =
        DependencyProperty.Register(nameof(IconData),
            typeof(Geometry), typeof(ButtonIcon), new PropertyMetadata(null));

    public Geometry IconData
    {
        get => (Geometry)GetValue(IconDataProperty);
        set => SetValue(IconDataProperty, value);
    }

    #endregion
}

Upvotes: 1

Related Questions