WPF ControlTemplate 绑定和触发器

WPF ControlTemplate bindings and triggers

我定义了这种风格:

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

这基本上是一个以路径作为内容的按钮,我在其中定义了新属性 IconFillIconStrokeIconData.

这里是 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

和用法:

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

除了按下按钮时,路径填充颜色没有变透明外,一切都符合预期。 由于路径填充 属性 直接绑定到按钮 IconFill 我不明白错误在哪里。 我也尝试在 ButtonIcon class 上实施 INotifyPropertyChanged 但没有成功。

最后我知道 IsPressed 触发器正在工作,因为如果我将背景值更改为某种可见颜色,则当我按下按钮时我会正确地看到该颜色。

尝试将触发器移至 ControlTemplate 并在设置器中引用组件的名称。

我的 ButtonIcon.xaml 文件:

<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}" />

用法:

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

组件:

鼠标悬停并按下时的组件:

  • 请注意,我将 Path 包装在 Border 中并更改了触发器颜色,只是为了在尝试时在 UI 上更好地看到它。

这是我用于示例的 ButtonIcon.cs class 文件:

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
}