Style.BasedOn属性如何在内部工作?

时间:2016-01-02 05:31:55

标签: c# wpf wpf-controls

背景

我正在开发一个应用程序,它根据CSV文件中提供的数据生成标签。我希望用户能够应用模板来更改这些标签的外观,我还需要用户能够编辑和修改这些模板。

我从WPF中的现有样式类派生这些模板。即使我将此作为“模板”呈现给最终用户,但为了这篇文章,我将它们称为样式以避免与数据模板混淆。

由于Style在使用后或在被其他style.BasedOn属性引用后变为密封,为了允许用户修改这些样式,对于每个修改,我需要基于Current Style生成新样式。我使用BasedOn Property执行此操作。

问题

设置Style.BasedOn属性并且元素使用该样式时,内部实际发生了什么?

我的第一个想法是创建了Setters集合的副本并应用于新样式,但是如下面的代码所示,情况并非如此:

 var styleA = new Style();
            styleA.Setters.Add(new Setter(/* DP and Value */));

 var styleB = new Style();
            styleB.BasedOn = styleA;

 Console.WriteLine(styleA.Setters.Count);
 Console.WriteLine(styleB.Setters.Count);

 // Ouput.
 // 1
 // 0

我的下一个想法是,BasedOn属性包含对应用于它的样式的引用,实际逻辑由FrameworkElement.Style OnPropertyChanged处理程序执行。我查看了参考资料,但是老实说,很快就进入了我的脑海。

对于解决问题的其他方式的任何帮助或建议将不胜感激。

1 个答案:

答案 0 :(得分:1)

正如您建议的另一种方法,这里有一个常用的方法:资源字典

<强> Generic.xaml

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <Style x:Key="DefaultLabelStyle" TargetType="Label">
        <Setter Property="FontSize" Value="20" />
    </Style>
</ResourceDictionary>

<强> StyleBlue.xaml

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <ResourceDictionary.MergedDictionaries>
        <ResourceDictionary Source="Generic.xaml" />
    </ResourceDictionary.MergedDictionaries>
    <Style BasedOn="{StaticResource DefaultLabelStyle}" TargetType="Label">
        <Setter Property="Background" Value="Blue" />
    </Style>
</ResourceDictionary>

<强> StyleRed.xaml

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <ResourceDictionary.MergedDictionaries>
        <ResourceDictionary Source="Generic.xaml" />
    </ResourceDictionary.MergedDictionaries>
    <Style BasedOn="{StaticResource DefaultLabelStyle}" TargetType="Label">
        <Setter Property="Background" Value="Red" />
    </Style>
</ResourceDictionary>

<强>演示

using System;
using System.Windows;

namespace WpfApplication3
{
    public partial class MainWindow
    {
        public MainWindow()
        {
            InitializeComponent();
            Loaded += MainWindow_Loaded;
        }

        private void MainWindow_Loaded(object sender, RoutedEventArgs e)
        {
        }

        private void SetTheme(string theme)
        {
            var mergedDictionaries = Resources.MergedDictionaries;
            mergedDictionaries.Clear();
            var dictionary = new ResourceDictionary {Source = new Uri(theme)};
            mergedDictionaries.Add(dictionary);
        }

        private void ButtonRedTheme_Click(object sender, RoutedEventArgs e)
        {
            SetTheme(@"pack://application:,,,/Themes/StyleRed.xaml");
        }

        private void ButtonBlueTheme_Click(object sender, RoutedEventArgs e)
        {
            SetTheme(@"pack://application:,,,/Themes/StyleBlue.xaml");
        }
    }
}

enter image description here enter image description here

正如您在Style.BasedOn中所看到的,根本没有迹象表明发生了什么,当然 很多

但是,如下所示:通常,您使用标记扩展和WPF XAML引用现有样式。

作为最终用户,您可能根本不需要了解内部工作原理,因为使用此功能有更简单的模式:XAML /资源字典。

有大量关于样式/模板的文档,首先阅读本文:Styling and Templating

对于您的用户,您可以将他们定向到XamlPad以创建这些模板,您可以同时进行实时预览。

  • 精通用户将知道该怎么做并且能够
  • 对于初学者,您可以提供模板的入门包

使用“CSV”和“代码”方法权衡此解决方案的“优点”和“缺点”(仅在您参与时可扩展,IMO注定要失败)。

修改

通过查看源代码,您可以准确了解BasedOn中发生的情况:http://referencesource.microsoft.com/#PresentationFramework/src/Framework/System/Windows/Style.cs,dd312833d0723042