如何在 app.xaml 中设置默认 WPF 窗口样式?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/431940/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-08 19:57:09  来源:igfitidea点击:

How to set default WPF Window Style in app.xaml?

wpfc#-3.0themesstyles

提问by NoizWaves

I am trying to set the default Style for every window in my WPF Windows application in my app.xaml. So far i have this in app.xaml:

我试图在我的 app.xaml 中为我的 WPF Windows 应用程序中的每个窗口设置默认样式。到目前为止,我在 app.xaml 中有这个:

<Application.Resources>
    <ResourceDictionary>
        <Style x:Key="WindowStyle" TargetType="{x:Type Window}">
            <Setter Property="Background" Value="Blue" />
        </Style>
    </ResourceDictionary>
</Application.Resources>

I can get the window to appear with this style when running the app (but not is VS designer) by specifically telling the window to use this style via:

在运行应用程序时(但不是 VS 设计器),我可以通过以下方式明确告诉窗口使用此样式,从而使窗口以这种样式显示:

Style="{DynamicResource WindowStyle}

This works, but is not ideal. So how do I:

这有效,但并不理想。那么我该怎么做:

  1. Have all windows automatically use the style (so i don't have to specify it on every window)?
  2. Have VS designer show the style?
  1. 让所有窗口自动使用该样式(所以我不必在每个窗口上都指定它)?
  2. 有VS设计师展示风格吗?

Thanks!

谢谢!

回答by Gishu

To add on to what Ray says:

补充一下雷所说的:

For the Styles, you either need to supply a Key/ID or specify a TargetType.

对于样式,您需要提供键/ID 或指定 TargetType。

If a FrameworkElement does not have an explicitly specified Style, it will always look for a Style resource, using its own type as the key
- Programming WPF (Sells, Griffith)

如果 FrameworkElement 没有明确指定的 Style,它将始终寻找 Style 资源,使用其自己的类型作为键
- Programming WPF (Sells, Griffith)

If you supply a TargetType, all instances of that type will have the style applied. However derived types will not... it seems. <Style TargetType="{x:Type Window}">will not work for all your custom derivations/windows. <Style TargetType="{x:Type local:MyWindow}">will apply to only MyWindow. So the options are

如果您提供 TargetType,则该类型的所有实例都将应用该样式。但是派生类型不会......似乎。<Style TargetType="{x:Type Window}">不适用于您的所有自定义派生/窗口。<Style TargetType="{x:Type local:MyWindow}">将仅适用于 MyWindow。所以选项是

  • Use a Keyed Style that you specify as the Style property of everywindow you want to apply the style. The designer will show the styled window.
  • 使用您指定为要应用该样式的每个窗口的 Style 属性的键控样式。设计器将显示样式窗口。

.

.

    <Application.Resources>
        <Style x:Key="MyWindowStyle">
            <Setter Property="Control.Background" Value="PaleGreen"/>
            <Setter Property="Window.Title" Value="Styled Window"/>
        </Style>
    </Application.Resources> ...
    <Window x:Class="MyNS.MyWindow" Style="{StaticResource MyWindowStyleKey}">  ...
  • Or you could derive from a custom BaseWindow class (which has its own quirks), where you set the Style property during the Ctor/Initialization/Load stage once. All Derivations would then automatically have the style applied. But the designer won't take notice of your styleYou need to run your app to see the style being applied.. I'm guessing the designer just runs InitializeComponent (which is auto/designer generated code) so XAML is applied but not custom code-behind.
  • 或者您可以从自定义 BaseWindow 类(它有自己的 quirks)派生,您可以在 Ctor/Initialization/Load 阶段设置 Style 属性一次。然后所有派生都将自动应用该样式。但是设计师不会注意到你的风格你需要运行你的应用程序才能看到正在应用的风格。代码隐藏。

So I'd say explicitly specified styles are the least work. You can anyways change aspects of the Style centrally.

所以我会说明确指定的样式是最少的工作。无论如何,您都可以集中更改样式的各个方面。

回答by Paul1307

Know this is years later, but since the question is still up here...

知道这是多年以后,但由于问题仍然存在......

  1. Create a resource dictionary in your project (Right-click the project...)

    I'll create a new folder under the Project called "Assets" and put "resourceDict.XAML in it.

  2. Add the code to resourceDict.XAML:

    <Style x:Key="WindowStyle" Target Type="Window" >
         <Setter Property="Background" Value="Blue" />
    </Style>
    
  3. In your Project XAML file add the following under Window:

    <Window.Resources>
        <ResourceDictionary>
            <!-- Believe it or not the next line fixes a bug MS acknowledges -->
            <Style TargetType="{x:Type Rectangle}" />
            <ResourceDictionary.MergedDictionaries>
                <ResourceDictionary Source="/Assets/resourceDict.XAML" />
            </ResourceDictionary.MergedDictionaries>
        <ResourceDictionary>
    </Window.Resources>
    

    ref the following web site: Trouble referencing a Resource Dictionary that contains a Merged Dictionary"There is a bug: if all your default styles are nested in merged dictionaries three levels deep (or deeper) the top dictionary does not get flagged so the search skips it. The work around is to put a default Style to something, anything, in the root Dictionary." And it seems to fix things reliably. Go figure...

  4. And finally, under Window, maybe after Title, but before the final Window '>' :

    Style="{DynamicResource windowStyle}"
    
  5. And you'll need to add the code in steps 3 & 4 to every project to which you want the style to apply.

  6. If you wanted to use a gradient background rather than a solid color, add the following code to the resourceDict.XAML:

    <LinearGradientBrush x:Key="windowGradientBackground" StartPoint="0,0"
            EndPoint="0,1" >
    <GradientStop Color= "AliceBlue" Offset="0" />
    <GradientStop Color= "Blue" Offset=".75" />
    </LinearGradientBrush>
    
  7. And modify your Style Setter for the background color to read:

    <Setter Property="Background" Value="{DynamicResource
            windowGradientBackground}" />
    
  1. 在您的项目中创建一个资源字典(右键单击该项目...)

    我将在名为“Assets”的项目下创建一个新文件夹,并将“resourceDict.XAML”放入其中。

  2. 将代码添加到 resourceDict.XAML:

    <Style x:Key="WindowStyle" Target Type="Window" >
         <Setter Property="Background" Value="Blue" />
    </Style>
    
  3. 在您的项目 XAML 文件中,在 Window 下添加以下内容:

    <Window.Resources>
        <ResourceDictionary>
            <!-- Believe it or not the next line fixes a bug MS acknowledges -->
            <Style TargetType="{x:Type Rectangle}" />
            <ResourceDictionary.MergedDictionaries>
                <ResourceDictionary Source="/Assets/resourceDict.XAML" />
            </ResourceDictionary.MergedDictionaries>
        <ResourceDictionary>
    </Window.Resources>
    

    参考以下网站:引用包含合并字典的资源字典时遇到问题“存在一个错误:如果所有默认样式都嵌套在三层深(或更深)的合并字典中,则顶部字典不会被标记,因此搜索会跳过它。解决方法是在根字典中为任何东西添加默认样式。” 它似乎可以可靠地解决问题。去搞清楚...

  4. 最后,在 Window 下,可能在 Title 之后,但在最终 Window '>' 之前:

    Style="{DynamicResource windowStyle}"
    
  5. 并且您需要将第 3 步和第 4 步中的代码添加到要应用该样式的每个项目中。

  6. 如果您想使用渐变背景而不是纯色,请将以下代码添加到 resourceDict.XAML:

    <LinearGradientBrush x:Key="windowGradientBackground" StartPoint="0,0"
            EndPoint="0,1" >
    <GradientStop Color= "AliceBlue" Offset="0" />
    <GradientStop Color= "Blue" Offset=".75" />
    </LinearGradientBrush>
    
  7. 并修改您的样式设置器以读取背景颜色:

    <Setter Property="Background" Value="{DynamicResource
            windowGradientBackground}" />
    

Steps 3 & 4 need to be repeated in each project.XAML file as described above, but hey, you get uniform Windows across the Solution! And the same process could apply to any controls you want to have a uniform look as well, buttons, whatever.

如上所述,需要在每个 project.XAML 文件中重复第 3 步和第 4 步,但是,嘿,您将在整个解决方案中获得统一的 Windows!同样的过程也适用于您想要具有统一外观的任何控件,按钮等。

For anyone coming into this late, hope this helps as I'm sure the original posters got this all figured out years ago.

对于迟到的任何人,希望这会有所帮助,因为我相信原始海报几年前就已经弄清楚了。

Paul

保罗

回答by Ray Booysen

The designer is not working because you're specifying a DynamicResource. Please change this to StaticResource and all will be well.

设计器不工作,因为您正在指定一个 DynamicResource。请将其更改为 StaticResource,一切都会好起来的。

To apply to all windows, you should remove the x:Key from the style. Setting the TargetType implicitly sets the x:Key to whatever is in TargetType. However, in my tests, this is not working, so I am looking into it.

要应用于所有窗口,您应该从样式中删除 x:Key。设置 TargetType 隐式地将 x:Key 设置为 TargetType 中的任何内容。但是,在我的测试中,这不起作用,所以我正在研究它。

If I set the TargetType to x:Type TextBlock, the designer works perfectly, it just seems to be the Window that is showing different behaviour.

如果我将 TargetType 设置为 x:Type TextBlock,则设计器工作正常,它似乎只是显示不同行为的 Window。

回答by STiLeTT

You can add this code to your App.xaml.cs file:

您可以将此代码添加到 App.xaml.cs 文件中:

        FrameworkElement.StyleProperty.OverrideMetadata(typeof(Window), new FrameworkPropertyMetadata
        {
            DefaultValue = Application.Current.FindResource(typeof(Window))
        });

After this, the style applied to the Windowtype will also apply to all types derived from Window

在此之后,应用于该Window类型的样式也将应用于所有派生自的类型Window

回答by KroaX

I investigated this one for some days now and made it work through the Constructor of my custom Window Class:

我已经调查了几天,并通过我的自定义窗口类的构造函数使其工作:

public class KWindow : Window
{
        public KWindow()
        {
            this.SetResourceReference(StyleProperty, typeof(KWindow));
        }

        static KWindow()
        {
            DefaultStyleKeyProperty.OverrideMetadata(typeof(KWindow), new FrameworkPropertyMetadata(typeof(KWindow)));

        }

        public override void OnApplyTemplate()
        {
            base.OnApplyTemplate();

            // gets called finally
        }
}

Hope it helps someone

希望它可以帮助某人

回答by Arnie

For those this struggling with a solution to the problem: How can I have a custom style be automtically applied to all my Window derived types? Below is the solution I came up with

对于那些努力解决问题的人:如何将自定义样式自动应用于我的所有 Window 派生类型?下面是我想出的解决方案

NOTE: I really didn't want to derive from the Window type or have to insert XAML on each window to force a style update etc. for reasons specific to my project (consumers of my product us my generic reusable style library and create their own layout/windows etc.) so I was really motivatedto figure a solution out that worked that I was willing to live with any side effects

注意:我真的不想从 Window 类型派生或必须在每个窗口上插入 XAML 以强制样式更新等原因特定于我的项目(我的产品的消费者使用我的通用可重用样式库并创建他们自己的布局/窗口等)所以我真的很有动力想出一个有效的解决方案,我愿意忍受任何副作用

Need to iterate through all the instantiated windows and simply force them to use the new custom style you have defined for the Window type. This works great for windows that are already up but when a window or child window is instantiated it won't know to use the new/custom type that has been declared for its base type; the vanilla Window type. So the best I could come up with was to use the LostKeyBoardFocus on the MainWindow for when it loses Focus to a ChildWindow (IOW When a child window has been created) and then invoke this FixupWindowDerivedTypes().

需要遍历所有实例化的窗口,并简单地强制它们使用您为 Window 类型定义的新自定义样式。这对于已经启动的窗口非常有用,但是当一个窗口或子窗口被实例化时,它不会知道使用已经为其基类型声明的新的/自定义类型;香草窗口类型。因此,我能想到的最好方法是在 MainWindow 上使用 LostKeyBoardFocus 以在它失去对 ChildWindow 的焦点时(IOW 当创建子窗口时),然后调用此 FixupWindowDerivedTypes()。

If someone has a better solution for "detecting" when any kind of window derived type is instantiated and thus call the FixupWindowDerivedTypes() that would be great. There may be something useful with handling the WM_WINDOWPOSCHANGING in this area as well.

如果有人有更好的解决方案来“检测”何时实例化任何类型的窗口派生类型,从而调用 FixupWindowDerivedTypes() 那就太好了。在这个区域处理 WM_WINDOWPOSCHANGING 可能也有一些有用的东西。

So this solution is not elegant per say but gets the job done without me having to touch any code or XAML related to my windows.

所以这个解决方案并不优雅,但无需我接触任何与我的窗口相关的代码或 XAML 即可完成工作。

   public static void FixupWindowDerivedTypes()
    {
        foreach (Window window in Application.Current.Windows)
        {
           //May look strange but kindly inform each of your window derived types to actually use the default style for the window type

                    window.SetResourceReference(FrameworkElement.StyleProperty, DefaultStyleKeyRetriever.GetDefaultStyleKey(window));
                }
            }
        }
    }


//Great little post here from Jafa to retrieve a protected property like DefaultStyleKey without using reflection.
http://themechanicalbride.blogspot.com/2008/11/protected-dependency-properties-are-not.html

//Helper class to retrieve a protected property so we can set it
internal class DefaultStyleKeyRetriever : Control
{
    /// <summary>
    /// This method retrieves the default style key of a control.
    /// </summary>
    /// <param name="control">The control to retrieve the default style key 
    /// from.</param>
    /// <returns>The default style key of the control.</returns>
    public static object GetDefaultStyleKey(Control control)
    {
        return control.GetValue(Control.DefaultStyleKeyProperty);
    }
}

回答by Crossman

Considering Gishu's answer I figured out one more workaround. But it could be little bit weird. If you use MVVM pattern you could remove code-behind of your window and x:Class markup in XAML file. So you will get an instance of window or your custom window but not a some instance of 'MainWindow' class that is derived from 'Window' class and marked as partial. I'm making VS-like window so I had to inherit window class and extend it functionality. In that case it will be possible to make new window class as partial that would allow us to make code-behind without inheritance.

考虑到Gishu的回答,我想出了另一种解决方法。但这可能有点奇怪。如果您使用 MVVM 模式,您可以删除窗口的代码隐藏和 XAML 文件中的 x:Class 标记。因此,您将获得一个窗口实例或您的自定义窗口,而不是从“Window”类派生并标记为部分的“MainWindow”类的某个实例。我正在制作类似 VS 的窗口,所以我必须继承窗口类并扩展它的功能。在这种情况下,可以将新窗口类创建为部分窗口类,这将允许我们在没有继承的情况下进行代码隐藏。

回答by Alex Anbu

  1. you will save all the styles in one xaml file(example design.xaml)

  2. and then call that (design.xaml) xaml file in all the pages like this way

  1. 您将所有样式保存在一个 xaml 文件中(示例 design.xaml)

  2. 然后像这样在所有页面中调用该 (design.xaml) xaml 文件

Like:

喜欢:

<ResourceDictionary.MergedDictionaries>
                <ResourceDictionary  Source="Design.xaml"/>                
</ResourceDictionary.MergedDictionaries>