1

我有一种情况,两个本质上相同的数据模板在解决隐式样式资源的方式上表现完全不同。这种不一致使得在我正在处理的大型应用程序中处理应用程序范围的样式资源变得困难。

情景。

我在名为 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>
4

1 回答 1

1

如果我没记错的话,WPF 认为ControlTemplates是一个边界,并且不会在模板内应用隐式样式。

但是这个规则有一个例外:任何继承自的东西都Control将应用隐式样式。

由于 aButton继承自Control,因此它应用隐式样式。但是 aTextBlock继承自FrameworkElement, not Control,因此它不会自动应用隐式样式,您必须手动添加它。

您应该会发现,如果您切换TextBlock到 a Label,将应用隐式样式,因为Label继承自Control

作为另一种选择,我认为您可以通过在您的内部TextBlock创建另一个隐式样式来手动应用隐式样式TextBlockUserControl.Resources

<Style TargetType="{x:Type TextBlock}" BasedOn="{StaticResource {x:Type FrameworkElement}}" />
于 2012-12-13T14:30:46.913 回答