【问题标题】:Binding Style Propety's Value in ResouceDictionary to ViewModel Properties将 ResouceDictionary 中的样式属性值绑定到 ViewModel 属性
【发布时间】:2020-02-11 19:19:12
【问题描述】:

我正在尝试更改我的 WPF 应用程序的样式(FontFamily、FontSize 等)。搜索 SO 不成功。 在应用程序中,我使用 MVVM 模式。为了给我的应用程序一个统一的外观,我使用资源字典来定义不同的样式和皮肤。到目前为止,这有效。我想要实现的是使用样式窗口通过运行时更改单个属性:

    <Window x:Class="StyleResourceDictionariesDemo.View.StyleWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        mc:Ignorable="d"
        Title="StyleWindow" Height="450" Width="800"
        Style="{DynamicResource WindowStyle}">
    <Window.Resources>
        <ResourceDictionary Source="../ResourceDictionaries/Styles/Controls/WindowStyle.xaml"/>
    </Window.Resources>
    <StackPanel Orientation="Vertical">
        <StackPanel Orientation="Horizontal" HorizontalAlignment="Left">
            <TextBlock Height="20" Width="75" Text="FontFamily:" Margin="10" TextAlignment="Right"/>
            <ComboBox x:Name="CbFonts" Width="200" Margin="10,10,7,0"
                      ItemsSource="{Binding Source={x:Static Fonts.SystemFontFamilies}}" SelectedItem="{Binding OwnFontFamily, UpdateSourceTrigger=PropertyChanged}"
                      SelectedValuePath="Source" Height="23" VerticalAlignment="Top">
                <ComboBox.ItemTemplate>
                    <DataTemplate>
                        <TextBlock Text="{Binding}" FontFamily="{Binding}" Height="20"/>
                    </DataTemplate>
                </ComboBox.ItemTemplate>
            </ComboBox>
        </StackPanel>
        <Border Height="2" Width="Auto" Background="Red" HorizontalAlignment="Stretch" VerticalAlignment="Center"/>
        <StackPanel Orientation="Horizontal"  HorizontalAlignment="Left">
            <TextBlock Height="20" Width="50" Text="Size:" Margin="10" TextAlignment="Right"/>
            <Slider Style="{DynamicResource SliderStyle}" Width="100" x:Name="FontSizeSlider" Value="{Binding OwnFontSize, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
                    Minimum="{Binding OwnSmallFontSize}" Maximum="{Binding OwnBigFontSize}" TickPlacement="Both" Margin="10" TickFrequency="{Binding OwnFontSizeSteps}" IsSnapToTickEnabled="True"/>
            <TextBlock Height="20" Width="50" Text="{Binding ElementName=FontSizeSlider, Path=Value, UpdateSourceTrigger=PropertyChanged}" TextAlignment="Right"/>
        </StackPanel>
    </StackPanel>
</Window>

附加以下视图模型:

using System.Windows.Media;

namespace StyleResourceDictionariesDemo.ViewModel
{
  class StyleWindowViewModel : ViewModelBase
  {
    #region Variables
    private double _ownFontSize = 12, _ownSmallFontSize = 8, _ownBigFontSize = 16;
    private FontFamily _ownFontFamily = new FontFamily("Arial");
    #endregion

    #region Properties
    public double OwnFontSizeSteps { get; } = 4;
    public FontFamily OwnFontFamily
    {
      get => _ownFontFamily;
      set
      {
        _ownFontFamily = new FontFamily(value.Source);
        OnPropertyChanged(nameof(OwnFontFamily));
      }
    }


    public double OwnFontSize
    {
      get => _ownFontSize;
      set
      {
        _ownFontSize = value;
        OnPropertyChanged(nameof(OwnFontSize));
      }
    }
    public double OwnSmallFontSize
    {
      get => _ownSmallFontSize;
      set
      {
        _ownSmallFontSize = value;
        OnPropertyChanged(nameof(OwnSmallFontSize));
      }
    }
    public double OwnBigFontSize
    {
      get => _ownBigFontSize;
      set
      {
        _ownBigFontSize = value;
        OnPropertyChanged(nameof(OwnBigFontSize));
      }
    }
    #endregion

    #region Constructor
    public StyleWindowViewModel()
    {

    }
    #endregion


  }
}

此外,我创建了一个 ControlBaseStyle xaml 文件,所有其他样式都从该文件继承:

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                   xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                   xmlns:viewModel="clr-namespace:StyleResourceDictionariesDemo.ViewModel">
    <viewModel:StyleWindowViewModel x:Key="StyleWindowViewModel" />
    <Style TargetType="{x:Type Control}" x:Key="ControlBaseStyle">
        <Style.Setters>
            <Setter Property="FontFamily" Value="{DynamicResource OwnFontFamily}"/>
            <Setter Property="FontStyle" Value="{DynamicResource FontStyleKey}"/>
            <Setter Property="FontWeight" Value="{DynamicResource FontWeightKey}"/>
            <Setter Property="FontSize" Value="{DynamicResource OwnFontSize}"/>
            <Setter Property="Background" Value="{DynamicResource SystemBackground}"/>
            <Setter Property="Foreground" Value="{DynamicResource SystemForeground}"/>
        </Style.Setters>
    </Style>
</ResourceDictionary>

我遇到的问题是 controlbasestyle 无法识别来自 ViewModel 属性的更改,我目前不明白为什么。也许我错过了一些简单的事情,我只是没有看到或者我采取了错误的方法来解决这个问题?

提前致谢。

米尔科

编辑1:

我的目标是在运行时更改样式和皮肤。 结构如下: 最初,所有样式都由皮肤分配不同的属性。 皮肤定义了应用程序中应该保持一致的颜色、字体、大小和其他内容。这些可以通过它们的特定名称(使用 x:key 定义)来调用。 该样式使用当前使用的设置器的名称检索在皮肤上方定义的值(例如,Value=-DynamicResource SystemBackground-用于属性背景)。到目前为止一切顺利。

我们的目标是使用 MVVM 模式来做到这一点。我已经定义了一个样式窗口和一个 ViewModel(上面的代码)。但我无法让风格注意到变化。

现在的第一个问题是,我可以吗?如果可以,我如何在运行时更改 FontFamily(或任何其他属性)的值。 第二个问题是,这种方法在现实生活中是否正确,还是有更好的方法?

示例皮肤:

LightSkin.xaml:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
    <ResourceDictionary.MergedDictionaries>
        <!--Default Style-->
        <ResourceDictionary Source="../00_Default/DefaultStyle.xaml"/>

        <!--Specific Style-->
        <ResourceDictionary Source="LightColor.xaml"/>
        <ResourceDictionary Source="LightSizes.xaml"/>
        <ResourceDictionary Source="LightFont.xaml"/>
    </ResourceDictionary.MergedDictionaries>
</ResourceDictionary>

DefaultStyle.xaml:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<ResourceDictionary.MergedDictionaries>
    <ResourceDictionary Source="../../Styles/Controls/ButtonStyle.xaml"/>
    <ResourceDictionary Source="../../Styles/Controls/LabelStyle.xaml"/>
    <ResourceDictionary Source="../../Styles/Controls/ToggleButtonStyle.xaml"/>
    <ResourceDictionary Source="../../Styles/Controls/WindowStyle.xaml"/>
</ResourceDictionary.MergedDictionaries>

LabelBaseStyle.xaml:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <ResourceDictionary.MergedDictionaries>
        <ResourceDictionary Source="ControlBaseStyle.xaml"/>
    </ResourceDictionary.MergedDictionaries>
    <Style TargetType="{x:Type Label}" x:Key="LabelBaseStyle" BasedOn="{StaticResource ControlBaseStyle}">
        <Style.Setters>
            <Setter Property="Width" Value="300"/>
            <Setter Property="Height" Value="150"/>
            <Setter Property="HorizontalAlignment" Value="Center"/>
            <Setter Property="VerticalAlignment" Value="Top"/>
            <Setter Property="Content" Value="{Binding Content}"/>
        </Style.Setters>
    </Style>
</ResourceDictionary>

每个 (Base-)Style 都直接或间接继承自 ControlBaseStyle.xaml(上面的代码)。

【问题讨论】:

  • 你可以参考我的这个answer。尝试将 BaseStyle 目标设为 FrameworkElement,并将 setter 属性设为 TextBlock.FontSize。这应该有助于字体大小。
  • 感谢您的评论,但我想实现的并不可行,因为我一开始以为会这样。我必须走一条不同的路才能意识到我面前的东西。所以我正在研究另一种解决方案,如何立即实施。

标签: c# wpf xaml mvvm


【解决方案1】:

您应该将ControlBaseStyle 的设置器绑定到视图模型的属性:

<viewModel:StyleWindowViewModel x:Key="StyleWindowViewModel" />
<Style TargetType="{x:Type Control}" x:Key="ControlBaseStyle">
    <Style.Setters>
        <Setter Property="FontFamily" Value="{Binding OwnFontFamily, Source={StaticResource StyleWindowViewModel}}"/>
        ...
    </Style.Setters>
</Style>

当您想要更改样式时,还要确保设置您在ResourceDictionary 中定义的实际StyleWindowViewModel 实例的属性。

【讨论】:

  • 感谢您的回复,我明天会仔细查看,但是快速复制粘贴代码 sn-ps 并没有带来任何进展。但你得到我想要做的。我网站的高级开发人员表示这不是我想做的,或者非常复杂。
  • 有什么复杂的?您如何以及何时设置StyleWindowViewModel 的属性?你能提供一个repo吗?
  • @Mirko:您还没有展示如何修改设置器应该绑定到的视图模型的属性。
  • 抱歉,我的回复迟了 mm8,我目前正在研究另一种方法来解决这个问题。完成后我会分享这个。
猜你喜欢
  • 2021-04-20
  • 2017-08-08
  • 2011-08-26
  • 2016-01-11
  • 2015-01-25
  • 2014-05-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多