如何从 ViewModel 更改样式属性?

Posted

技术标签:

【中文标题】如何从 ViewModel 更改样式属性?【英文标题】:How to change the style property from the ViewModel? 【发布时间】:2021-02-09 06:10:17 【问题描述】:

如何从 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" />

【问题讨论】:

检查我更新的答案。希望对你有帮助 您不会更改视图模型的样式。如果你这样做,你首先不需要视图模型。尽管使用converter,但您可以根据某些视图模型属性的值在视图中设置Style 【参考方案1】:

将视图相关类型(如 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.

一定要给它一个有意义的名字。它不必是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>
<Button Style="StaticResource MyButtonStyle" 
        Grid.Row="0"
        Grid.Column="1"
        Content="News" 
        x:Name="LoadFirstPage"/>

价值转​​换器解决方案

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

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();
   

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

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

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

<Button Style="Binding IsPageLoaded, Converter=StaticResource ConditionToStyleConverter"
        Grid.Row="0"
        Grid.Column="1"
        Content="News" 
        x:Name="LoadFirstPage"/>

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

【讨论】:

我不知道为什么,但是当我使用您的 Style Tigger 解决方案并更改 IsPageLoaded 属性时,我的项目中的内容控件不会加载内容。正是这段代码ActivateItem(new PostsViewModel()); 这不起作用,但你的解决方案很棒,非常感谢。 @NicolasHL 该样式不应该引起任何这样的副作用,但很难说为什么它似乎会影响没有代码的ContentControlContentControl 是如何知道以前不存在的 IsPageLoaded 属性的? 这是在单击按钮时执行的代码部分。 public void LoadFirstPage() ActivateItem(new PostsViewModel()); IsPageLoaded = true; 我不知道代码是否正确,但这是您的解决方案对我的工作方式。 样式触发解决方案对我有用,谢谢

以上是关于如何从 ViewModel 更改样式属性?的主要内容,如果未能解决你的问题,请参考以下文章

如何在ViewModel中访问View中的控件

SwiftUI:如何从 ViewModel 的更改中切换警报演示者

Usercontrol - 从 ViewModel 通知属性更改

MVVM:修改模型,如何正确更新 ViewModel 和 View?

WPF ViewModel与多个View绑定后如何解决的问题

2022-03-17 WPF面试题 如何理解MVVM中的 View 和 ViewModel?