如何从 ViewModel 更改样式属性?

Nic*_* HL 1 c# wpf mvvm caliburn.micro

如何从 ViewModel 修改 Style 属性?我正在使用 WPF 和 MVVM 以及 Caliburn Micro。我有两种不同的按钮样式,我想在用户单击按钮时从 ViewModel 更改它。

我正在尝试在我的 xaml 中使用它:

<Button 
      Style="{Binding Path=StyleButton, Mode=OneWay}" 
      Grid.Row="0"
      Grid.Column="1"
      Content="News" 
      x:Name="LoadFirstPage" />
Run Code Online (Sandbox Code Playgroud)

tha*_*guy 5

将视图相关类型放入Style视图模型中违反了 MVVM 原则。相反,公开一个表示按钮状态的属性,该按钮确定是否应显示一种样式或另一种样式。

public class MyViewModel : INotifyPropertyChanged
{
   private bool _isPageLoaded;
   public bool IsPageLoaded
   {
      get => _isPageLoaded;
      set
      {
         if (_isPageLoaded == value)
            return;

         _isPageLoaded = value;
         OnPropertyChanged();
      }
   }

   public event PropertyChangedEventHandler PropertyChanged;

   protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
   {
      PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
   }

   // ...other properties and methods.
}
Run Code Online (Sandbox Code Playgroud)

请务必给它一个有意义的名称。它不必是 type bool,但它适合这里。还要确保实现INotifyPropertyChanged反映用户界面中属性的更改。

在单击按钮时调用的命令或方法中将此属性设置为正确的值。从这里开始,您的场景有多种选择。

风格触发解决方案

您可以使用触发器为两种状态创建一个通用样式,而不是完全交换样式,该触发器根据存储在属性中的状态以不同的方式设置属性。

<Style x:Key="MyButtonStyle" TargetType="{x:Type Button}" BasedOn="{StaticResource {x:Type Button}}">
   <!-- Properties set for IsPageLoaded=False -->
   <Setter Property="Foreground" Value="Red"/>
   <Style.Triggers>
      <DataTrigger Binding="{Binding IsPageLoaded}" Value="True">
         <!-- Properties set for IsPageLoaded=True -->
         <Setter Property="Foreground" Value="Blue"/>
      </DataTrigger>
   </Style.Triggers>
</Style>
Run Code Online (Sandbox Code Playgroud)
<Button Style="{StaticResource MyButtonStyle}" 
        Grid.Row="0"
        Grid.Column="1"
        Content="News" 
        x:Name="LoadFirstPage"/>
Run Code Online (Sandbox Code Playgroud)

价值转换器解决方案

您可以创建一个值转换器,返回与属性状态对应的样式。

public class ConditionToStyleConverter : IValueConverter
{
   public Style SuccessStyle { get; set; }

   public Style FailureStyle { get; set; }

   public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
   {
      return (bool)value ? SuccessStyle : FailureStyle;
   }

   public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
   {
      return new InvalidOperationException();
   }
}
Run Code Online (Sandbox Code Playgroud)

以下是您放入范围内的资源字典中的两种样式和转换器的示例。

<Style x:Key="MyFirstButtonStyle" TargetType="{x:Type Button}" BasedOn="{StaticResource {x:Type Button}}">
   <Setter Property="Foreground" Value="Red"/>
</Style>

<Style x:Key="MySecondButtonStyle" TargetType="{x:Type Button}" BasedOn="{StaticResource {x:Type Button}}">
   <Setter Property="Foreground" Value="Blue"/>
</Style>

<local:ConditionToStyleConverter x:Key="ConditionToStyleConverter"
                                 SuccessStyle="{StaticResource MyFirstButtonStyle}"
                                 FailureStyle="{StaticResource MySecondButtonStyle}"/>
Run Code Online (Sandbox Code Playgroud)

您可以像这样在按钮上使用转换器。

<Button Style="{Binding IsPageLoaded, Converter={StaticResource ConditionToStyleConverter}}"
        Grid.Row="0"
        Grid.Column="1"
        Content="News" 
        x:Name="LoadFirstPage"/>
Run Code Online (Sandbox Code Playgroud)

在此示例中,转换器是一个简单的转换器IValueConverter,它公开样式的属性,因此对于不同的样式,您必须创建其他实例。还可以创建一个可以直接绑定样式的多值转换器,但对于初学者的场景来说,这个解决方案应该足够了。