【发布时间】:2012-12-01 10:35:34
【问题描述】:
我有一种情况,两个本质上相同的数据模板在解析隐式样式资源的方式上表现完全不同。这种不一致使得在我正在处理的大型应用程序中处理应用程序范围的样式资源变得困难。
情景。
我在名为 AppStyles.xaml 的独立 xaml 文件中有一个 ResourceDictionary。它为 Button 和 TextBlock 类定义了一种隐式样式。
<!-- AppStyles.xaml -->
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Style TargetType="TextBlock" BasedOn="{StaticResource {x:Type TextBlock}}">
<Setter Property="Padding" Value="10"/>
<Setter Property="Background" Value="Red"/>
</Style>
<Style TargetType="Button" BasedOn="{StaticResource {x:Type Button}}">
<Setter Property="Width" Value="300"/>
</Style>
</ResourceDictionary>
我的 MainWindow.xaml 文件将 AppStyles.xaml 合并到它自己的资源中。 MainWindow.xaml 包含两个 ContentPresenter,每个都绑定到一个简单的 viewmodel 类。第一个 ContentPresenter 使用的数据模板是在 MainWindow.xaml 中内联声明的 UserControl,第二个 ContentPresenter 使用的数据模板也被声明为内联,但引用了在单独文件中定义的 UserControl。两个数据模板使用的 UserControl 的实际声明是相同的。
<!-- MainWindow.xaml -->
<Window x:Class="Demo2.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:Demo2"
Title="MainWindow" Height="350" Width="525"
x:Name="_this">
<Window.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="/AppStyles.xaml"/>
</ResourceDictionary.MergedDictionaries>
<DataTemplate DataType="{x:Type local:TemplateVm1}">
<UserControl>
<StackPanel>
<TextBlock Text="{Binding TextBlockValue}"/>
<Button Content="{Binding ButtonValue}"/>
</StackPanel>
</UserControl>
</DataTemplate>
<DataTemplate DataType="{x:Type local:TemplateVm2}">
<local:UserControl1/>
</DataTemplate>
</ResourceDictionary>
</Window.Resources>
<StackPanel>
<ContentPresenter Content="{Binding ElementName=_this, Path=TemplateVm1}"/>
<ContentPresenter Content="{Binding ElementName=_this, Path=TemplateVm2}"/>
</StackPanel>
</Window>
问题。
问题是两个 ContentPresenter 的呈现方式完全不同!两个 ContentPresenter 都使用 AppStyles.xaml 中的样式呈现 Button,但第一个 ContentPresenter 不应用隐式 TextBlock 样式,而第二个则应用。
我的期望是 TextBlock 样式不会应用于 ContentPresenter 显示的模板,因为 WPF 行为只有从 Control 派生的组件才会在当前模板之外查找以解析资源(并且 TextBlock 不是从 Control 派生的)。
那么这里发生了什么,我怎样才能让它表现得始终如一?
为了示例的完整性,这里是视图模型的实现和后面的 MainWindow 代码以及第二个模板中使用的 UserControl。
// TemplateVm.cs
namespace Demo2
{
public class TemplateVm1
{
public TemplateVm1()
{
TextBlockValue = "TextBlock in ContentPresenter1.";
ButtonValue = "Button in ContentPresenter1";
}
public string TextBlockValue { get; private set; }
public string ButtonValue { get; private set; }
}
public class TemplateVm2
{
public TemplateVm2()
{
TextBlockValue = "TextBlock in ContentPresenter2.";
ButtonValue = "Button in ContentPresenter2";
}
public string TextBlockValue { get; private set; }
public string ButtonValue { get; private set; }
}
}
// MainWindow.xaml.cs
namespace Demo2
{
using System.Windows;
public partial class MainWindow : Window
{
public MainWindow()
{
TemplateVm1 = new TemplateVm1();
TemplateVm2 = new TemplateVm2();
InitializeComponent();
}
public TemplateVm1 TemplateVm1 { get; private set; }
public TemplateVm2 TemplateVm2 { get; private set; }
}
}
<!-- UserControl1.xaml -->
<UserControl x:Class="Demo2.UserControl1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d" d:DesignHeight="300" d:DesignWidth="300">
<StackPanel>
<TextBlock Text="{Binding TextBlockValue}"/>
<Button Content="{Binding ButtonValue}"/>
</StackPanel>
</UserControl>
【问题讨论】:
-
嗯,我也有点困惑为什么会发生这种情况,但是当您将 AppStyles 合并到 app.xaml 中时,您应该会获得一致的行为。如果这不起作用,一种方法是在 UserControl 中合并 xaml,但我强烈建议不要使用该解决方案。您真的确定使用了一种样式而没有使用另一种样式,您可以通过设置背景颜色等更有意义的属性来确认吗?
-
合并到 App.xaml 中的资源与合并到其他地方的资源不同,因为合并到 App.xaml 中的任何内容都绝对适用于所有内容。所以是的,我可以将 AppStyles.xaml 放入 App.xaml,然后将 TextBlock 样式应用于两个模板。但是由于 App.xaml 资源应用方式的不同,TextBlock 样式也将应用于按钮中(因为它们的默认 ControlTemplates 中有 TextBlocks),这是最不可取的,因为我只希望应用我的应用程序样式到应用程序声明的控件。
-
我很确定,在第一个模板的情况下,应用了 Button 的样式,而没有应用 TextBlock 的样式。我已经使用 Width 和 Background 属性对其进行了测试。在第二个模板的情况下,Button 和 TextBlock 都被应用了。