背景
我正在开发一个应用程序,它根据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处理程序执行。我查看了参考资料,但是老实说,很快就进入了我的脑海。
对于解决问题的其他方式的任何帮助或建议将不胜感激。
答案 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");
}
}
}
正如您在Style.BasedOn中所看到的,根本没有迹象表明发生了什么,当然 很多 。
但是,如下所示:通常,您使用标记扩展和WPF XAML引用现有样式。
作为最终用户,您可能根本不需要了解内部工作原理,因为使用此功能有更简单的模式:XAML /资源字典。
有大量关于样式/模板的文档,首先阅读本文:Styling and Templating
对于您的用户,您可以将他们定向到XamlPad以创建这些模板,您可以同时进行实时预览。
使用“CSV”和“代码”方法权衡此解决方案的“优点”和“缺点”(仅在您参与时可扩展,IMO注定要失败)。
修改强>
通过查看源代码,您可以准确了解BasedOn
中发生的情况:http://referencesource.microsoft.com/#PresentationFramework/src/Framework/System/Windows/Style.cs,dd312833d0723042