我有一种情况,两个基本相同的数据模板在解析隐式样式资源的方式上表现完全不同。这种不一致使我很难在我正在开发的大型应用程序中处理应用程序范围的样式资源。
情景。
我在一个名为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,每个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的渲染方式完全不同!两个ContentPresenters都使用AppStyles.xaml中的样式呈现Button,但是第一个ContentPresenter不应用隐式TextBlock样式,而第二个则不适用。
我的期望是TextBlock样式不会应用于ContentPresenter显示的模板,因为WPF行为只有从Control派生的组件看起来在当前模板之外才能解析资源(并且TextBlock不是从Control派生的) )。
那么这里发生了什么,我怎样才能让它表现得一致?
为了完整的示例,这里是viewmodels和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>
答案 0 :(得分:1)
如果我没记错的话,WPF认为ControlTemplates
是一个边界,并且不会在模板中应用隐式样式。
但是这条规则有一个例外:从Control
继承的任何内容都将应用隐式样式。
由于Button
继承自Control
,因此它会应用隐式样式。但是TextBlock
继承自FrameworkElement
,而不是Control
,因此它不会自动应用隐式样式,您必须手动添加它。
您应该会发现,如果您将TextBlock
切换为Label
,则隐式样式将会应用,因为Label
继承自Control
作为另一种选择,我认为您可以通过在TextBlock
TextBlock
样式来手动应用隐式UserControl.Resources
样式
<Style TargetType="{x:Type TextBlock}" BasedOn="{StaticResource {x:Type FrameworkElement}}" />