【问题标题】:Control created in code behind does not have it's default Style在后面的代码中创建的控件没有它的默认样式
【发布时间】:2026-01-20 02:35:01
【问题描述】:

我正在尝试在后面的窗口代码中实例化一个控件(用于框架导航)。不幸的是,它没有应用默认样式。

我希望当一个控件第一次渲染时,渲染器会将控件的样式/模板设置为任一(显式提供的 -> 资源中提供的默认选项 -> null)。

我对如何应用默认样式的心理模型是否错误?真的是这样吗

class Whatever
{
    int value = 5;
}

是真正的语法糖

class Whatever
{
    public Whatever()
    {
        this.value = 5;
    }

    int value;
}

因此样式是在编译时设置的?

这可能是我如何访问控件的样式和模板的问题(不太可能,因为我可以在我的主窗口上控制它的类型并且它具有默认样式)?

MainWindow.xaml

<Window>
    <Window.Resources>
        <ResourceDictionary>
            </ResourceDictionary.MergedDictionaries>
                <ResourceDictionary Source="DataLogPage/Themes.xaml" />
            </ResourceDictionary.MergedDictionaries>
        </ResourceDictionary>
    </Window.Resources>
    <!-- Data here... -->
</Window>

DatalogPage/Themes.xaml

<ResourceDictionary>
    <ResourceDictionary.MergedDictionaries>
        <ResourceDictionary Source="Themes/Styles/DefaultStyle.xaml" />
    </ResourceDictionary.MergedDictionaries>
</ResourceDictionary>

DatalogPage/Themes/Styles/DefaultStyle.xaml

<ResourceDictionary>
    <ResourceDictionary.MergedDictionaries>
        <ResourceDictionary Source="../Templates/DefaultTemplate.xaml" />
    </ResourceDictionary.MergedDictionaries>
    <Style TargetType="this:DataLog">
        <Setter Property="Template"
                Value="{StaticResource DefaultTemplateForDataLog}" />
    </Style>
</ResourceDictionary>

DatalogPage/Themes/Templates/DefaultTemplate.xaml

<ResourceDictionary>
    <ControlTemplate x:Key="DefaultTemplateForDataLog"
                     TargetType="this:DataLog">
        <!-- Data here... -->
    </ControlTemplate>
</ResourceDictionary>

MainWindow.xaml.cs(我在后面的代码中创建控件)

private void currentContext_ContextChangeRequested()
{
    //Other bits of logic for context switching

    //User wants to go to the Data Log Page
    this.MainFrame.Navigate(new DataLogPage.DataLog());
    return;
}

重申:

问题: 在后面的代码中创建的控件没有它的默认样式。

关于为什么会这样的可能想法:

1) 我的默认样式如何应用的用户模型是错误的,我必须明确设置它。
2)我可能引用的样式不正确。

如果我必须明确设置样式/模板,是否有两全其美的方法,我可以在 MainWindow 的 xaml 中制作一些我可以像这样以编程方式引用的东西:new DataLogPage.DataLog(){Style = this.DataLogStyle};

【问题讨论】:

    标签: wpf wpf-controls


    【解决方案1】:

    App.xaml

    如果您真的想要共享内容,您可以将它们注入到共享的应用程序资源中,或者您可以将它们合并到 app.xaml 中

    <Application x:Class="BlaBla"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             StartupUri="MainWindow.xaml">
       <Application.Resources>
         <ResourceDictionary>
           <ResourceDictionary.MergedDictionaries>
              <ResourceDictionary Source="A.xaml"/>
              <ResourceDictionary Source="B.xaml"/>
           </ResourceDictionary.MergedDictionaries>
        </ResourceDictionary>
      </Application.Resources>
    </Application>
    

    Using a global ResourceDictionary in a UserControl Library

    WPF - Global Style?

    模板和自定义控件

    仅适用于自定义控件 (**)

    首先,我永远不会在窗口中以这种方式开始合并或使用字典(如果你采用 cc 的方式,你宁愿将这些字典合并到下面描述的通用文件中,但请注意它们必须在范围内)。 您有一个名为 Generic 的文件夹,其中必须有一个名为 Themes.xaml 的文件。我个人合并了很多字典here,还做了一些“体力劳动”。通常我将我的客户控制称为 Foo 的主题 ThemeName.generic.xaml,但这只是我的偏好。 :)。

    自定义控件应该派生自控件,并且它必须具有以下静态构造函数,以便应用它的模板。

    public class Whatever : Control // from scratch or something you want to extend
       static Whatever()
       {
           DefaultStyleKeyProperty.OverrideMetadata(typeof (Whatever), new FrameworkPropertyMetadata(typeof (Whatever)));
       }
    }
    

    <Style TargetType="{x:Type local:Whatever}">
        <!-- Do whatever here ;) -->
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type local:Whatever}">
                    <Border Background="{TemplateBinding Background}"
                            BorderBrush="{TemplateBinding BorderBrush}"
                            BorderThickness="{TemplateBinding BorderThickness}">
                    </Border>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
    

    重写 OnApplyTemplate 以查看是否应用了模板。 (*)

    样式

    应用于样式。

    要覆盖现有的控件模板,您可以在 theme.xaml 中执行以下操作。这将使您的所有按钮控件默认具有此样式。

     <Style TargetType="{x:Type Button}">
        <Setter Property="Background" Value="Green"/> 
     </Style>
    

    我通常只是将样式直接放在用户控件的资源中,但如果它们不属于自定义控件,则主要是数据、itemtemplates 等,无论如何这是如何做到的:

    <UserControl.Resources>
        <!-- usuallly  just datatemplates, itemtemplates etc.. -->
        <Style x:Key="SomeStyle" TargetType="{x:Type Button}">
            <!--whatever -->
            <Setter Property="Background" Value="Black"/>
        </Style>
    </UserControl.Resources>
    <Button Style="{StaticResource SomeStyle}"></Button>
    

    顺便说一句:您可以动态加载大多数内容,甚至可以从其他程序集中加载。资源,模板作品。但这是另一个主题:)

    希望对你有所帮助,

    干杯

    斯蒂安

    【讨论】:

    • 将合并字典移动到 App.xaml 指向的 ResourceDictionary 有效。这样做有什么我需要注意的吗?另外,我看不出使用 DefaultStyleKeyProperty.OverrideMetadata 的区别(无论哪种方式都应用了样式)。
    • @Tory 是的,确实有效。我没有完全完成我的帖子,因为我的电话响了,但我在那里找到了你的链接:) 我只在创建所谓的自定义控件时使用的覆盖。如果您只想应用样式,您可以这样做,并且您还使用行为(我喜欢它们)和附加属性来扩展控件的功能。
    • @Tory 我在那里更新了答案,在那里添加了 app.xaml 和合并字典的示例。
    • 是的,我(在这种情况下)将其应用于自定义控件。而且我已经知道使用&lt;Style TargetType="{x:Type Whatever}" \&gt;(它在样式定义中,我只是使用xmlnamespace:datatype而不是x:type datatype),正如您提供的链接问题所讨论的那样。
    最近更新 更多