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>
这基本上是一个以路径作为内容的按钮,我在其中定义了新属性 IconFill
、IconStroke
和 IconData
.
这里是 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
}
我定义了这种风格:
<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>
这基本上是一个以路径作为内容的按钮,我在其中定义了新属性 IconFill
、IconStroke
和 IconData
.
这里是 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
}