让 WPF 应用程序看起来像 Metro 风格,即使在 Windows 7 中? (窗口 Chrome / 主题 / 主题)

Posted

技术标签:

【中文标题】让 WPF 应用程序看起来像 Metro 风格,即使在 Windows 7 中? (窗口 Chrome / 主题 / 主题)【英文标题】:Making WPF applications look Metro-styled, even in Windows 7? (Window Chrome / Theming / Theme) 【发布时间】:2012-11-15 13:14:30 【问题描述】:

我喜欢新 Office 套件和 Visual Studio 上的窗口镶边:

当然,我仍在为 Windows 7 开发应用程序,但我想知道是否有一种快速简便的方法(阅读:WPF 样式或 Windows 库)来模拟这种样式。我过去做过一些窗口镶边样式,但让它看起来和表现得恰到好处真的很棘手。

有谁知道是否有现有的模板或库可以为我的 WPF 应用程序添加“现代 UI”外观和感觉?

【问题讨论】:

本指南/ NuGet 包可能会有所帮助:MahaApps Metro 它包含一组样式和控件,用于创建具有 Metro 外观的 WPF 应用程序。 要求我们推荐或查找书籍、工具、软件库、教程或其他场外资源的问题对于 Stack Overflow 来说是题外话,因为它们往往会吸引固执己见答案和垃圾邮件。相反,请描述问题以及迄今为止为解决该问题所做的工作。 【参考方案1】:

我最终选择的解决方案是MahApps.Metro (github),它(现在在两个软件上使用它之后)我认为是一个优秀的 UI 工具包(感谢 Oliver Vogel 的建议) .

它只需要很少的努力就可以为应用程序设置皮肤,并且适应了标准的 Windows 8 控件。它非常强大。

Nuget 上有一个版本:

您可以使用 GUI 通过 Nuget 安装 MahApps.Metro(右键单击 您的项目,管理 Nuget 引用,搜索“MahApps.Metro”)或 通过控制台:

PM> 安装包 MahApps.Metro

它也是免费——即使用于商业用途。

2013 年 10 月 29 日更新:

我发现 Github 版本的 MahApps.Metro 包含当前 nuget 版本中不可用的控件和样式,包括:

数据网格:

清洁窗口:

弹出窗口:

瓷砖:

github 存储库非常活跃,有相当多的用户贡献。我建议检查一下。

【讨论】:

非常好的更新!我还尝试了 MahApps.Metro,用于 WPF 和 Elysium 的 Modern UI。我发现 Elysium 使用起来非常复杂,并且在他们的网站/文档上令人困惑。现代 UI 和 MahApps.Metro 重量轻且易于使用,但是 MahApps。 Metro 在 WPF 表单控件上更具竞争力。 不使用 MahApps 来创建自己的组件定制是否非常困难?【参考方案2】:

我所做的是创建自己的窗口和样式。因为我喜欢控制一切,我不希望一些外部库只是为了使用它的 Window。我看了已经提到的MahApps.Metro on GitHub

也非常好Modern UI on GitHub。 (仅限 .NET4.5)

还有一个是Elysium,但我真的没试过这个。

当我看到它是如何完成的时,我所做的风格真的很简单。现在我有了自己的窗口,我可以用 xaml 做任何我想做的事情......对我来说,这是我自己做的主要原因。我也为你做了一个 :) 我应该说如果不探索 Modern UI 我将无法做到这一点,这对我很有帮助。我试图让它看起来像 VS2012 Window。看起来像这样。

这是代码(请注意它的目标是 .NET4.5)

public class MyWindow : Window


    public MyWindow()
    
        this.CommandBindings.Add(new CommandBinding(SystemCommands.CloseWindowCommand, this.OnCloseWindow));
        this.CommandBindings.Add(new CommandBinding(SystemCommands.MaximizeWindowCommand, this.OnMaximizeWindow, this.OnCanResizeWindow));
        this.CommandBindings.Add(new CommandBinding(SystemCommands.MinimizeWindowCommand, this.OnMinimizeWindow, this.OnCanMinimizeWindow));
        this.CommandBindings.Add(new CommandBinding(SystemCommands.RestoreWindowCommand, this.OnRestoreWindow, this.OnCanResizeWindow));
    

    private void OnCanResizeWindow(object sender, CanExecuteRoutedEventArgs e)
    
        e.CanExecute = this.ResizeMode == ResizeMode.CanResize || this.ResizeMode == ResizeMode.CanResizeWithGrip;
    

    private void OnCanMinimizeWindow(object sender, CanExecuteRoutedEventArgs e)
    
        e.CanExecute = this.ResizeMode != ResizeMode.NoResize;
    

    private void OnCloseWindow(object target, ExecutedRoutedEventArgs e)
    
        SystemCommands.CloseWindow(this);
    

    private void OnMaximizeWindow(object target, ExecutedRoutedEventArgs e)
    
        SystemCommands.MaximizeWindow(this);
    

    private void OnMinimizeWindow(object target, ExecutedRoutedEventArgs e)
    
        SystemCommands.MinimizeWindow(this);
    

    private void OnRestoreWindow(object target, ExecutedRoutedEventArgs e)
    
        SystemCommands.RestoreWindow(this);
    

这里的资源:

<BooleanToVisibilityConverter x:Key="bool2VisibilityConverter" />

<Color x:Key="WindowBackgroundColor">#FF2D2D30</Color>
<Color x:Key="HighlightColor">#FF3F3F41</Color>
<Color x:Key="BlueColor">#FF007ACC</Color>
<Color x:Key="ForegroundColor">#FFF4F4F5</Color>

<SolidColorBrush x:Key="WindowBackgroundColorBrush" Color="StaticResource WindowBackgroundColor"/>
<SolidColorBrush x:Key="HighlightColorBrush" Color="StaticResource HighlightColor"/>
<SolidColorBrush x:Key="BlueColorBrush" Color="StaticResource BlueColor"/>
<SolidColorBrush x:Key="ForegroundColorBrush" Color="StaticResource ForegroundColor"/>

<Style x:Key="WindowButtonStyle" TargetType="x:Type Button">
    <Setter Property="Foreground" Value="DynamicResource ForegroundColorBrush" />
    <Setter Property="Background" Value="Transparent" />
    <Setter Property="HorizontalContentAlignment" Value="Center" />
    <Setter Property="VerticalContentAlignment" Value="Center" />
    <Setter Property="Padding" Value="1" />
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="x:Type Button">
                <Grid Background="TemplateBinding Background">
                    <ContentPresenter x:Name="contentPresenter"
                          HorizontalAlignment="TemplateBinding HorizontalContentAlignment"
                          VerticalAlignment="TemplateBinding VerticalContentAlignment"
                          SnapsToDevicePixels="TemplateBinding SnapsToDevicePixels"
                          Margin="TemplateBinding Padding"
                          RecognizesAccessKey="True" />
                </Grid>
                <ControlTemplate.Triggers>
                    <Trigger Property="IsMouseOver" Value="True">
                        <Setter Property="Background" Value="StaticResource HighlightColorBrush" />
                    </Trigger>
                    <Trigger Property="IsPressed" Value="True">
                        <Setter Property="Background" Value="DynamicResource BlueColorBrush" />
                    </Trigger>
                    <Trigger Property="IsEnabled" Value="false">
                        <Setter TargetName="contentPresenter" Property="Opacity" Value=".5" />
                    </Trigger>
                </ControlTemplate.Triggers>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

<Style x:Key="MyWindowStyle" TargetType="local:MyWindow">
    <Setter Property="Foreground" Value="DynamicResource ForegroundColorBrush" />
    <Setter Property="Background" Value="DynamicResource WindowBackgroundBrush"/>
    <Setter Property="ResizeMode" Value="CanResizeWithGrip" />
    <Setter Property="UseLayoutRounding" Value="True" />
    <Setter Property="TextOptions.TextFormattingMode" Value="Display" />
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="local:MyWindow">
                <Border x:Name="WindowBorder" Margin="Binding Source=x:Static SystemParameters.WindowNonClientFrameThickness" Background="StaticResource WindowBackgroundColorBrush">
                    <Grid>
                        <Border BorderThickness="1">
                            <AdornerDecorator>
                                <Grid x:Name="LayoutRoot">
                                    <Grid.RowDefinitions>
                                        <RowDefinition Height="25" />
                                        <RowDefinition Height="*" />
                                        <RowDefinition Height="15" />
                                    </Grid.RowDefinitions>
                                    <ContentPresenter Grid.Row="1" Grid.RowSpan="2" Margin="7"/>
                                    <Rectangle x:Name="HeaderBackground" Height="25" Fill="DynamicResource WindowBackgroundColorBrush" VerticalAlignment="Top" Grid.Row="0"/>
                                    <StackPanel Orientation="Horizontal" HorizontalAlignment="Right" VerticalAlignment="Top" WindowChrome.IsHitTestVisibleInChrome="True" Grid.Row="0">
                                        <Button Command="Binding Source=x:Static SystemCommands.MinimizeWindowCommand" ToolTip="minimize" Style="StaticResource WindowButtonStyle">
                                            <Button.Content>
                                                <Grid Width="30" Height="25" RenderTransform="1,0,0,1,0,1">
                                                    <Path Data="M0,6 L8,6 Z" Width="8" Height="7" VerticalAlignment="Center" HorizontalAlignment="Center"
                                                        Stroke="Binding Foreground, RelativeSource=RelativeSource Mode=FindAncestor, AncestorType=Button" StrokeThickness="2"  />
                                                </Grid>
                                            </Button.Content>
                                        </Button>
                                        <Grid Margin="1,0,1,0">
                                            <Button x:Name="Restore" Command="Binding Source=x:Static SystemCommands.RestoreWindowCommand" ToolTip="restore" Visibility="Collapsed" Style="StaticResource WindowButtonStyle">
                                                <Button.Content>
                                                    <Grid Width="30" Height="25" UseLayoutRounding="True" RenderTransform="1,0,0,1,.5,.5">
                                                        <Path Data="M2,0 L8,0 L8,6 M0,3 L6,3 M0,2 L6,2 L6,8 L0,8 Z" Width="8" Height="8" VerticalAlignment="Center" HorizontalAlignment="Center"
                                                            Stroke="Binding Foreground, RelativeSource=RelativeSource Mode=FindAncestor, AncestorType=Button" StrokeThickness="1"  />
                                                    </Grid>
                                                </Button.Content>
                                            </Button>
                                            <Button x:Name="Maximize" Command="Binding Source=x:Static SystemCommands.MaximizeWindowCommand" ToolTip="maximize" Style="StaticResource WindowButtonStyle">
                                                <Button.Content>
                                                    <Grid Width="31" Height="25">
                                                        <Path Data="M0,1 L9,1 L9,8 L0,8 Z" Width="9" Height="8" VerticalAlignment="Center" HorizontalAlignment="Center"
                                                            Stroke="Binding Foreground, RelativeSource=RelativeSource Mode=FindAncestor, AncestorType=Button" StrokeThickness="2"  />
                                                    </Grid>
                                                </Button.Content>
                                            </Button>
                                        </Grid>
                                        <Button Command="Binding Source=x:Static SystemCommands.CloseWindowCommand" ToolTip="close"  Style="StaticResource WindowButtonStyle">
                                            <Button.Content>
                                                <Grid Width="30" Height="25" RenderTransform="1,0,0,1,0,1">
                                                    <Path Data="M0,0 L8,7 M8,0 L0,7 Z" Width="8" Height="7" VerticalAlignment="Center" HorizontalAlignment="Center"
                                                        Stroke="Binding Foreground, RelativeSource=RelativeSource Mode=FindAncestor, AncestorType=Button" StrokeThickness="1.5"  />
                                                </Grid>
                                            </Button.Content>
                                        </Button>
                                    </StackPanel>
                                    <TextBlock x:Name="WindowTitleTextBlock" Grid.Row="0" Text="TemplateBinding Title" HorizontalAlignment="Left" TextTrimming="CharacterEllipsis" VerticalAlignment="Center"  Margin="8 -1 0 0"  FontSize="16"  Foreground="TemplateBinding Foreground"/>
                                    <Grid Grid.Row="2">
                                        <Path x:Name="ResizeGrip" Visibility="Collapsed" Width="12" Height="12" Margin="1" HorizontalAlignment="Right"
                                        Stroke="StaticResource BlueColorBrush" StrokeThickness="1" Stretch="None" Data="F1 M1,10 L3,10 M5,10 L7,10 M9,10 L11,10 M2,9 L2,11 M6,9 L6,11 M10,9 L10,11 M5,6 L7,6 M9,6 L11,6 M6,5 L6,7 M10,5 L10,7 M9,2 L11,2 M10,1 L10,3" />
                                    </Grid>
                                </Grid>
                            </AdornerDecorator>
                        </Border>
                        <Border BorderBrush="StaticResource BlueColorBrush" BorderThickness="1" Visibility="Binding IsActive, RelativeSource=RelativeSource FindAncestor, AncestorType=x:Type Window, Converter=StaticResource bool2VisibilityConverter" />
                    </Grid>
                </Border>
                <ControlTemplate.Triggers>
                    <Trigger Property="WindowState" Value="Maximized">
                        <Setter TargetName="Maximize" Property="Visibility" Value="Collapsed" />
                        <Setter TargetName="Restore" Property="Visibility" Value="Visible" />
                        <Setter TargetName="LayoutRoot" Property="Margin" Value="7" />
                    </Trigger>
                    <Trigger Property="WindowState" Value="Normal">
                        <Setter TargetName="Maximize" Property="Visibility" Value="Visible" />
                        <Setter TargetName="Restore" Property="Visibility" Value="Collapsed" />
                    </Trigger>
                    <MultiTrigger>
                        <MultiTrigger.Conditions>
                            <Condition Property="ResizeMode" Value="CanResizeWithGrip" />
                            <Condition Property="WindowState" Value="Normal" />
                        </MultiTrigger.Conditions>
                        <Setter TargetName="ResizeGrip" Property="Visibility" Value="Visible" />
                    </MultiTrigger>
                </ControlTemplate.Triggers>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
    <Setter Property="WindowChrome.WindowChrome">
        <Setter.Value>
            <WindowChrome CornerRadius="0" GlassFrameThickness="1" UseAeroCaptionButtons="False" />
        </Setter.Value>
    </Setter>
</Style>

【讨论】:

您好,非常感谢您发布的这么棒的代码。顺便问一句,窗户上可以有阴影吗?我唯一想到的是将GlassFrameThickness 更改为1。但是阴影太浓了,太黑了。我怎样才能改变它的重量和不透明度? Add DropShadow to a Custom made Window 不使用 MahApps 来创建自己的组件定制是否非常困难? 太棒了!非常感谢您做出如此出色的贡献,我曾多次尝试做同样的事情,但从未得到如此完美的结果。 假设我想增加顶部蓝色边框的厚度,并删除所有其他边的边框(如你的答案中的极乐世界图片),我需要改变什么?我是 wpf 的新手,因此提出了这个问题【参考方案3】:

基于Kapitán Mlíko's answer 上面的源代码,我会将其更改为使用以下内容:

对于最小化、恢复/最大化和关闭按钮,最好使用 Marlett 字体而不是路径数据点。

<StackPanel Orientation="Horizontal" HorizontalAlignment="Right" VerticalAlignment="Top" WindowChrome.IsHitTestVisibleInChrome="True" Grid.Row="0">
<Button Command="Binding Source=x:Static SystemCommands.MinimizeWindowCommand" ToolTip="minimize" Style="StaticResource WindowButtonStyle">
    <Button.Content>
        <Grid Width="30" Height="25">
            <TextBlock Text="0" FontFamily="Marlett" FontSize="14" VerticalAlignment="Center" HorizontalAlignment="Center" Padding="3.5,0,0,3" />
        </Grid>
    </Button.Content>
</Button>
<Grid Margin="1,0,1,0">
    <Button x:Name="Restore" Command="Binding Source=x:Static SystemCommands.RestoreWindowCommand" ToolTip="restore" Visibility="Collapsed" Style="StaticResource WindowButtonStyle">
        <Button.Content>
            <Grid Width="30" Height="25" UseLayoutRounding="True">
                <TextBlock Text="2" FontFamily="Marlett" FontSize="14" VerticalAlignment="Center" HorizontalAlignment="Center" Padding="2,0,0,1" />
            </Grid>
        </Button.Content>
    </Button>
    <Button x:Name="Maximize" Command="Binding Source=x:Static SystemCommands.MaximizeWindowCommand" ToolTip="maximize" Style="StaticResource WindowButtonStyle">
        <Button.Content>
            <Grid Width="31" Height="25">
                <TextBlock Text="1" FontFamily="Marlett" FontSize="14" VerticalAlignment="Center" HorizontalAlignment="Center" Padding="2,0,0,1" />
            </Grid>
        </Button.Content>
    </Button>
</Grid>
<Button Command="Binding Source=x:Static SystemCommands.CloseWindowCommand" ToolTip="close"  Style="StaticResource WindowButtonStyle">
    <Button.Content>
        <Grid Width="30" Height="25">
            <TextBlock Text="r" FontFamily="Marlett" FontSize="14" VerticalAlignment="Center" HorizontalAlignment="Center" Padding="0,0,0,1" />
        </Grid>
    </Button.Content>
</Button>

【讨论】:

嗨飞天特立独行。你能解释一下为什么使用 marlett 字体更好吗?我有三种不同的实现,我不确定使用哪一种。第一个是使用路径数据点,第二个是使用 marlett,第三个是重新创建 SVG 格式的按钮。我试图在这个项目中使用 100% 的最佳实践,但不确定哪一个是最佳选择。你能解释一下为什么 marlett 更好吗? 嗨 user1632018 如果您希望在 Winform 或 WPF 中创建自定义 chrome 窗口,您应该查看系统上可用的“Marlett”字体。此字体包含 Windows 中用于最小化、最大化、恢复和关闭按钮的实际字形。使用这种字体可以很容易地在自定义 chrome 窗口中重用这些字形,而不是通常使用的自定义图像。您可以查看 Windows Character Map 中的 Marlett 字体或以下链接了解更多详细信息:microsoft.com/typography/fonts/font.aspx?FMID=1264希望对您有所帮助。【参考方案4】:

我会推荐 Modern UI for WPF 。

它有一个非常活跃的维护者,它很棒而且免费!

我目前正在将一些项目移植到 MUI,第一(同时也是第二)印象真是太棒了!

要查看 MUI 的实际效果,您可以下载基于 MUI 的 XAML Spy。

编辑:在 WPF 中使用 Modern UI 几个月,我很喜欢它!

【讨论】:

【参考方案5】:

如果你愿意付费,我强烈推荐你Telerik Components for WPF。他们提供了很棒的styles/themes 并且有针对Office 2013 和Windows 8 的特定主题(编辑:还有Visual Studio 2013 主题风格)。然而,这里提供的不仅仅是样式,实际上你会得到一大堆非常有用的控件。

下面是它的实际效果(从 Telerik 样本中截取的屏幕截图):

这里是telerik executive dashboard sample(第一个屏幕截图)的链接,这里是CRM Dashboard(第二个屏幕截图)的链接。

他们提供 30 天的试用期,试一试!

【讨论】:

【参考方案6】:

看看这个WPF metro-styled window with optional glowing borders。

这是一个独立的应用程序,它使用 Microsoft.Windows.Shell(包括在内)以外的任何其他库来创建带有可选发光边框的 Metro 风格窗口。

支持 Windows 一直到 XP (.NET4)。

【讨论】:

以上是关于让 WPF 应用程序看起来像 Metro 风格,即使在 Windows 7 中? (窗口 Chrome / 主题 / 主题)的主要内容,如果未能解决你的问题,请参考以下文章

wpf metro风格,怎么自定义gridview的scrollbar,就是想美化一下

WPF 使用MahApps.Metro UI库

WPF 皮肤之MathApps.Metro UI库

如何在我的“Metro Style”应用程序中的 WPF 中进行拖放?

C# WPF开源控件库:MahApps.Metro

使用 powershell 启动 Metro 风格的应用程序