C# 更改自定义按钮WPF上的笔划和填充
我在一个用C#编写的WPF应用程序中使用多边形和矩形为come media player控件创建了一个模板。我有三个按钮的“状态”C# 更改自定义按钮WPF上的笔划和填充,c#,wpf,xaml,C#,Wpf,Xaml,我在一个用C#编写的WPF应用程序中使用多边形和矩形为come media player控件创建了一个模板。我有三个按钮的“状态” Idle = stroke=Black; Fill=Black MouseOver = stroke=White; Fill=Black MouseDown/Disabled = Stroke=black; fill=White 我有前两个工作,但我不能得到最后一个。到目前为止,我有以下代码: <Button Grid.Column="2" Vertical
Idle = stroke=Black; Fill=Black
MouseOver = stroke=White; Fill=Black
MouseDown/Disabled = Stroke=black; fill=White
我有前两个工作,但我不能得到最后一个。到目前为止,我有以下代码:
<Button Grid.Column="2" VerticalAlignment="Center" HorizontalAlignment="Center" Command="{Binding PlayCommand}" >
<Button.Template>
<ControlTemplate TargetType="Button">
<Canvas Height="24" Width="18">
<Polygon x:Name="play" Points="2,0 18,9 2,18" Fill="Black" Canvas.Top="2"/>
</Canvas>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="play" Property="Stroke" Value="White"/>
</Trigger>
<Trigger Property="IsMouseOver" Value="False">
<Setter TargetName="play" Property="Stroke" Value="Black"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Button.Template>
</Button>
ViewModel实现INotifyPropertyChanged
public bool IsPlaying
{
get {return _isPlaying; }
private set
{
if (value != _isPlaying)
{
_isPlaying = value;
OnPropertyChanged(new PropertyChangedEventArgs("IsPlaying"));
}
}
}
当我试图构建一个触发器来设置笔划和填充时,即使按钮被禁用,它们似乎也不会被触发。有什么建议吗?提前谢谢
编辑:
App.xaml.cs
private void App_OnStartup(object sender, StartupEventArgs e)
{
Configuration config = new Configuration();
MainWindow main = new MainWindow(config);
main.Show();
}
ViewModel构造函数:
public MainWindowViewModel(Configuration config)
{
this.config = config;
_player = new MediaPlayerMock();
_player.StatusChanged += _player_StatusChanged;
PlayCommand = new PlayCommand(this);
StopCommand = new StopCommand(this);
}
public ICommand PlayCommand { get; set; }
public ICommand StopCommand { get; set; }
当视图模型的
isplay
属性更改时,应触发CanExecuteChanged
事件:
public class PlayCommand : ICommand
{
...
public event EventHandler CanExecuteChanged;
public void Execute(object parameter)
{
_viewModel.Play();
CanExecuteChanged?.Invoke(this, EventArgs.Empty);
}
}
或者,您可以在
IsPlaying
上设置数据触发器,而不是在IsEnabled
上设置触发器:
<DataTrigger Binding="{Binding IsPlaying}" Value="False">
<Setter ... />
</DataTrigger>
当视图模型的
显示属性更改时,应触发CanExecuteChanged
事件:
public class PlayCommand : ICommand
{
...
public event EventHandler CanExecuteChanged;
public void Execute(object parameter)
{
_viewModel.Play();
CanExecuteChanged?.Invoke(this, EventArgs.Empty);
}
}
或者,您可以在IsPlaying
上设置数据触发器,而不是在IsEnabled
上设置触发器:
<DataTrigger Binding="{Binding IsPlaying}" Value="False">
<Setter ... />
</DataTrigger>
在这种情况下也可以使用VisualState管理器。可视状态为您封装了所有这些,并使您能够基于状态更改控件,而不是基于转换更改控件。这是一个(可能很重要的)区别
TL;博士在底部顺便说一句
当使用数据触发器或触发器时,通常您将受益于使用要触发不同样式的属性和事件。但您只能访问这些转换
相反,国家在国家上工作。区别很简单。转换没有关于状态的信息。如果将鼠标移到只读文本框控件上会发生什么情况?对,它启动悬停更改,尽管此处不适用悬停,因为文本框不可编辑。国家可以照顾到这一点,因为它们只有在满足所有条件时才会改变。触发器将在红色虚线上更改,而状态不会更改
除了获得一致的状态而不是转换之外,您还可以获得更多的好处。您还可以访问控件的焦点状态或验证状态,这是依赖项属性提供的机制
要想知道你想要哪一个,你可以看看你想要改变什么以及什么时候改变。如果您希望将鼠标悬停在控件上,而不是在前面不单击鼠标的情况下悬停,那么您需要的是转换。如果您只想对控件的特定结果状态进行可视化编码,请尽可能改用VisualStateManager
TL;DR:触发器和视觉状态不同,表现也不同。您可能需要评估您需要哪一个。视觉状态仅在模板中定义,这可能是使用它们的缺点。您还需要使用故事板,但如果持续时间为零,它们也会立即应用。例如,可以按如下方式创建平面按钮:
<ControlTemplate TargetType="Button">
<Grid Background="{TemplateBinding Background}" TextBlock.Foreground="{TemplateBinding Foreground}">
<Rectangle Fill="White" Opacity="0." x:Name="Overlay" />
<Rectangle Fill="Gray" Opacity="0." x:Name="OverlayDark" />
<ContentPresenter Margin="{TemplateBinding Padding}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" />
<VisualStateManager.VisualStateGroups>
<VisualStateGroup Name="CommonStates">
<VisualState Name="Normal">
<VisualState.Storyboard>
<Storyboard>
<DoubleAnimation To="0.0" Duration="0:0:0.1" Storyboard.TargetName="Overlay"
Storyboard.TargetProperty="Opacity" />
</Storyboard>
</VisualState.Storyboard>
</VisualState>
<VisualState Name="Pressed">
<Storyboard>
<DoubleAnimation To="0.5" Duration="0:0:0.1" Storyboard.TargetName="OverlayDark"
Storyboard.TargetProperty="Opacity" />
</Storyboard>
</VisualState>
<VisualState Name="MouseOver">
<VisualState.Storyboard>
<Storyboard>
<DoubleAnimation To="0.5" Duration="0:0:0.1" Storyboard.TargetName="Overlay"
Storyboard.TargetProperty="Opacity" />
</Storyboard>
</VisualState.Storyboard>
</VisualState>
<VisualState Name="Disabled">
<Storyboard>
<DoubleAnimation To="1" Duration="0:0:0.1" Storyboard.TargetName="OverlayDark"
Storyboard.TargetProperty="Opacity" />
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
</Grid>
</ControlTemplate>
在这种情况下也可以使用VisualState管理器。可视状态为您封装了所有这些,并使您能够基于状态更改控件,而不是基于转换更改控件。这是一个(可能很重要的)区别
TL;博士在底部顺便说一句
当使用数据触发器或触发器时,通常您将受益于使用要触发不同样式的属性和事件。但您只能访问这些转换
相反,国家在国家上工作。区别很简单。转换没有关于状态的信息。如果将鼠标移到只读文本框控件上会发生什么情况?对,它启动悬停更改,尽管此处不适用悬停,因为文本框不可编辑。国家可以照顾到这一点,因为它们只有在满足所有条件时才会改变。触发器将在红色虚线上更改,而状态不会更改
除了获得一致的状态而不是转换之外,您还可以获得更多的好处。您还可以访问控件的焦点状态或验证状态,这是依赖项属性提供的机制
要想知道你想要哪一个,你可以看看你想要改变什么以及什么时候改变。如果您希望将鼠标悬停在控件上,而不是在前面不单击鼠标的情况下悬停,那么您需要的是转换。如果您只想对控件的特定结果状态进行可视化编码,请尽可能改用VisualStateManager
TL;DR:触发器和视觉状态不同,表现也不同。您可能需要评估您需要哪一个。视觉状态仅在模板中定义,这可能是使用它们的缺点。您还需要使用故事板,但如果持续时间为零,它们也会立即应用。例如,可以按如下方式创建平面按钮:
<ControlTemplate TargetType="Button">
<Grid Background="{TemplateBinding Background}" TextBlock.Foreground="{TemplateBinding Foreground}">
<Rectangle Fill="White" Opacity="0." x:Name="Overlay" />
<Rectangle Fill="Gray" Opacity="0." x:Name="OverlayDark" />
<ContentPresenter Margin="{TemplateBinding Padding}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" />
<VisualStateManager.VisualStateGroups>
<VisualStateGroup Name="CommonStates">
<VisualState Name="Normal">
<VisualState.Storyboard>
<Storyboard>
<DoubleAnimation To="0.0" Duration="0:0:0.1" Storyboard.TargetName="Overlay"
Storyboard.TargetProperty="Opacity" />
</Storyboard>
</VisualState.Storyboard>
</VisualState>
<VisualState Name="Pressed">
<Storyboard>
<DoubleAnimation To="0.5" Duration="0:0:0.1" Storyboard.TargetName="OverlayDark"
Storyboard.TargetProperty="Opacity" />
</Storyboard>
</VisualState>
<VisualState Name="MouseOver">
<VisualState.Storyboard>
<Storyboard>
<DoubleAnimation To="0.5" Duration="0:0:0.1" Storyboard.TargetName="Overlay"
Storyboard.TargetProperty="Opacity" />
</Storyboard>
</VisualState.Storyboard>
</VisualState>
<VisualState Name="Disabled">
<Storyboard>
<DoubleAnimation To="1" Duration="0:0:0.1" Storyboard.TargetName="OverlayDark"
Storyboard.TargetProperty="Opacity" />
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
</Grid>
</ControlTemplate>