我在使用C#编写的WPF应用程序中使用多边形和矩形为媒体播放器控件创建了一个模板。我有三个州'按钮。
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>
PlayCommand:
public class PlayCommand : ICommand
{
private MainWindowViewModel _viewModel;
public PlayCommand(MainWindowViewModel viewModel)
{
_viewModel = viewModel;
}
public event EventHandler CanExecuteChanged;
public bool CanExecute(object parameter)
{
return !_viewModel.IsPlaying;
}
public void Execute(object parameter)
{
_viewModel.Play(); // sets IsPlaying to true
}
}
ViewModel实现了INotifyPropertyChanged
public bool IsPlaying
{
get {return _isPlaying; }
private set
{
if (value != _isPlaying)
{
_isPlaying = value;
OnPropertyChanged(new PropertyChangedEventArgs("IsPlaying"));
}
}
}
当我尝试构建触发器以查看IsEnabled来设置笔触和填充时,即使按钮被禁用,它们也似乎永远不会被触发。有什么建议?提前谢谢。
编辑:
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; }
答案 0 :(得分:1)
当视图模型的CanExecuteChanged
属性发生更改时,您应该触发IsPlaying
事件:
public class PlayCommand : ICommand
{
...
public event EventHandler CanExecuteChanged;
public void Execute(object parameter)
{
_viewModel.Play();
CanExecuteChanged?.Invoke(this, EventArgs.Empty);
}
}
或者,您可以在IsPlaying
上使用DataTrigger而不是IsEnabled
上的触发器:
<DataTrigger Binding="{Binding IsPlaying}" Value="False">
<Setter ... />
</DataTrigger>
答案 1 :(得分:1)
VisualState Manager也可用于此类情况。视觉状态为您封装了所有内容,并使您可以根据状态而不是过渡来更改控件。这是(可能很重要的)差异。
TL; DR在底部顺便说一句。
一般情况下,当您使用数据触发器或触发器时,您将获得使用您想要触发不同样式的任何属性和事件的好处。但是你只能访问这些过渡。
相反,各国的工作就是国家。差异非常简单。过渡没有关于国家的信息。如果将鼠标悬停在只读文本框控件上会发生什么?是的,它开始悬停的变化,因为文本框不可编辑,因此悬停不适用于此处。各国可以照顾到这一点,因为它们只有在满足所有条件时才会改变。触发器将在红色虚线上改变,而状态则不会改变。除了获得一致的状态而不是转换之外,您还可以获得更多好处。您还可以访问控件的焦点状态或验证状态,这是依赖属性提供的机制。
要了解您想要哪一个,您可以查看您想要更改的内容以及何时更改。如果您希望鼠标悬停在控件上而导致的结果与仅悬停而不单击之前的结果不同,则转换就是您要查找的内容。如果您只想对控件的特定结果状态进行可视化编码,请尽可能使用VisualStateManager。
TL; DR:触发器和视觉状态不同,行为也不同。您可能想要评估您需要哪一个。视觉状态仅在模板中定义,这可能是使用它们的缺点。您还需要使用故事板,但持续时间为零,它们也会立即应用。 A list of all states for a button can be found here。例如,可以按如下方式创建一个平面按钮:
<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>