如何在 wpf 中创建自定义窗口镶边?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/6792275/
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 22:59:57  来源:igfitidea点击:

How to create custom window chrome in wpf?

wpfxamlwindow

提问by Carl Weis

How can I create a basic custom window chrome for a WPF window, that doesn't include the close button and still a moveable and resizeable window?

如何为 WPF 窗口创建基本的自定义窗口镶边,其中不包括关闭按钮并且仍然是一个可移动和可调整大小的窗口?

回答by Rachel

You set your Window's WindowStyle="None", then build your own window interface. You need to build in your own Min/Max/Close/Drag event handlers, but Resizing is still maintained.

您设置 Window 的WindowStyle="None",然后构建自己的窗口界面。您需要构建自己的 Min/Max/Close/Drag 事件处理程序,但 Resizing 仍然保持不变。

For example:

例如:

<Window 
    WindowState="Maximized" 
    WindowStyle="None"
    WindowStartupLocation="CenterScreen"
    MaxWidth="{Binding Source={x:Static SystemParameters.WorkArea}, Path=Width}"
    MaxHeight="{Binding Source={x:Static SystemParameters.WorkArea}, Path=Height}"
>

    <DockPanel x:Name="RootWindow">
        <DockPanel x:Name="TitleBar" DockPanel.Dock="Top">
            <Button x:Name="CloseButton" Content="X"
                    Click="CloseButton_Click"                 
                    DockPanel.Dock="Right" />
            <Button x:Name="MaxButton" Content="Restore" 
                    Click="MaximizeButton_Click"
                    DockPanel.Dock="Right" />
            <Button x:Name="MinButton" Content="Min"
                    Click="MinimizeButton_Click"
                    DockPanel.Dock="Right" />

            <TextBlock HorizontalAlignment="Center">Application Name</TextBlock>
        </DockPanel>

        <ContentControl Content="{Binding CurrentPage}" />
    </DockPanel>

</Window>

And here's some example code-behind for common window functionality

这是常见窗口功能的一些示例代码隐藏

/// <summary>
/// TitleBar_MouseDown - Drag if single-click, resize if double-click
/// </summary>
private void TitleBar_MouseDown(object sender, MouseButtonEventArgs e)
{
    if(e.ChangedButton == MouseButton.Left)
        if (e.ClickCount == 2)
        {
            AdjustWindowSize();
        }
        else
        {
            Application.Current.MainWindow.DragMove();
        }
 }

/// <summary>
/// CloseButton_Clicked
/// </summary>
private void CloseButton_Click(object sender, RoutedEventArgs e)
{
   Application.Current.Shutdown();
}

/// <summary>
/// MaximizedButton_Clicked
/// </summary>
private void MaximizeButton_Click(object sender, RoutedEventArgs e)
{
    AdjustWindowSize();
}

/// <summary>
/// Minimized Button_Clicked
/// </summary>
private void MinimizeButton_Click(object sender, RoutedEventArgs e)
{
    this.WindowState = WindowState.Minimized;
}

/// <summary>
/// Adjusts the WindowSize to correct parameters when Maximize button is clicked
/// </summary>
private void AdjustWindowSize()
{
    if (this.WindowState == WindowState.Maximized)
    {
        this.WindowState = WindowState.Normal;
        MaximizeButton.Content = "1";
    }
    else
    {
        this.WindowState = WindowState.Maximized;
        MaximizeButton.Content = "2";
    }

}

回答by dss539

.NET 4.5added a new class that greatly simplifies this.

.NET 4.5添加了一个新类,大大简化了这一点。

The WindowChrome classenables you to extend Windows Presentation Foundation (WPF) content into the non-client area of a window that is typically reserved for the operating system's window manager.

WindowChrome类可让您的Windows Presentation Foundation(WPF)的内容延伸到这通常是保留给操作系统的窗口管理器窗口的非客户区。

You can find a tutorial here.

您可以在此处找到教程

And here's a short example usage.

这是一个简短的示例用法

回答by Simon Stanford

I've just used the example below for .net 4.5 and it works very well. Interestingly, it uses a code behind for a resource dictionary for the click events. All you have to do is reference the resource dictionary in your app.xamlfile and then assign the Window the Style CustomWindowStyle. This was shamelessly stolen from http://www.eidias.com/blog/2014/1/27/restyle-your-window.

我刚刚将下面的示例用于 .net 4.5,它运行良好。有趣的是,它为点击事件的资源字典使用了一个代码。您所要做的就是引用app.xaml文件中的资源字典,然后为 Window 分配 Style CustomWindowStyle。这是从http://www.eidias.com/blog/2014/1/27/restyle-your-window无耻地窃取的。

<ResourceDictionary x:Class="WpfApp7.WindowStyle"
                    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">


    <Style x:Key="CustomWindowStyle" TargetType="{x:Type Window}">
        <Setter Property="WindowChrome.WindowChrome">
            <Setter.Value>
                <WindowChrome CaptionHeight="30"
                              CornerRadius="4"
                              GlassFrameThickness="0"
                              NonClientFrameEdges="None"
                              ResizeBorderThickness="5"
                              UseAeroCaptionButtons="False" />
            </Setter.Value>
        </Setter>

        <Setter Property="BorderBrush" Value="Black" />
        <Setter Property="Background" Value="Gray" />

        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type Window}">
                    <Grid>
                        <Border Background="{TemplateBinding Background}"
                                BorderBrush="{TemplateBinding BorderBrush}"
                                BorderThickness="5,30,5,5">
                            <AdornerDecorator>
                                <ContentPresenter />
                            </AdornerDecorator>
                        </Border>

                        <Grid Height="30"
                            VerticalAlignment="Top">

                            <Grid.ColumnDefinitions>
                                <ColumnDefinition Width="Auto"/>
                                <ColumnDefinition Width="*"/>
                                <ColumnDefinition Width="Auto"/>
                            </Grid.ColumnDefinitions>

                            <StackPanel Orientation="Horizontal" Margin="5,0">
                                <Button Content="A" Margin="0,0,5,0" VerticalAlignment="Center" Click="Button_Click" WindowChrome.IsHitTestVisibleInChrome="True"/>
                                <Button Content="B" Margin="0,0,5,0" VerticalAlignment="Center" Click="Button_Click" WindowChrome.IsHitTestVisibleInChrome="True"/>
                                <Button Content="C" Margin="0,0,5,0" VerticalAlignment="Center" Click="Button_Click" WindowChrome.IsHitTestVisibleInChrome="True"/>
                                <Button Content="D" Margin="0,0,5,0" VerticalAlignment="Center" Click="Button_Click" WindowChrome.IsHitTestVisibleInChrome="True"/>
                            </StackPanel>


                            <TextBlock Margin="5,0,0,0"
                                       VerticalAlignment="Center"
                                       HorizontalAlignment="Center"
                                       FontSize="16"
                                       Foreground="White"
                                       Text="{TemplateBinding Title}" 
                                       Grid.Column="1"/>


                            <StackPanel Orientation="Horizontal"
                                        Grid.Column="2">
                                <Button x:Name="btnClose"
                                    Width="15"
                                    Margin="5"
                                    Click="CloseClick"
                                    Content="X"
                                    WindowChrome.IsHitTestVisibleInChrome="True" />


                                <Button x:Name="btnRestore"
                                    Width="15"
                                    Margin="5"
                                    Click="MaximizeRestoreClick"
                                    Content="#"
                                    WindowChrome.IsHitTestVisibleInChrome="True" />

                                <Button x:Name="btnMinimize"
                                    Width="15"
                                    Margin="5"
                                    VerticalContentAlignment="Bottom"
                                    Click="MinimizeClick"
                                    Content="_"
                                    WindowChrome.IsHitTestVisibleInChrome="True" />
                            </StackPanel>
                        </Grid>

                    </Grid>

                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
</ResourceDictionary>

And for the code behind:

而对于背后的代码:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;

namespace WpfApp7
{
    public partial class WindowStyle : ResourceDictionary
    {
        public WindowStyle()
        {
            InitializeComponent();
        }

        private void CloseClick(object sender, RoutedEventArgs e)
        {
            var window = (Window)((FrameworkElement)sender).TemplatedParent;
            window.Close();
        }

        private void MaximizeRestoreClick(object sender, RoutedEventArgs e)
        {
            var window = (Window)((FrameworkElement)sender).TemplatedParent;
            if (window.WindowState == System.Windows.WindowState.Normal)
            {
                window.WindowState = System.Windows.WindowState.Maximized;
            }
            else
            {
                window.WindowState = System.Windows.WindowState.Normal;
            }
        }

        private void MinimizeClick(object sender, RoutedEventArgs e)
        {
            var window = (Window)((FrameworkElement)sender).TemplatedParent;
            window.WindowState = System.Windows.WindowState.Minimized;
        }

        private void Button_Click(object sender, RoutedEventArgs e)
        {
            MessageBox.Show("Hello!");
        }
    }
}

回答by Apfelkuacha

Here is an easy solution which looks very similar to the default Windows 10 buttons, it simply uses the same Font for the symbols:

这是一个简单的解决方案,它看起来与默认的 Windows 10 按钮非常相似,它只是为符号使用相同的字体:

<StackPanel Orientation="Horizontal" VerticalAlignment="Top" WindowChrome.IsHitTestVisibleInChrome="True">
<Button Click="Minimize_Click" Content="&#xE949;" FontFamily="Segoe MDL2 Assets" FontSize="10" Padding="15,15,15,5" Background="Transparent" BorderBrush="Transparent" />
<Button Click="Maximize_Click" FontFamily="Segoe MDL2 Assets" FontSize="10" Padding="15,10" Background="Transparent" BorderBrush="Transparent">
    <Button.Style>
        <Style TargetType="{x:Type Button}">
            <Setter Property="Button.Content" Value="&#xE739;" />
            <Style.Triggers>
                <DataTrigger Binding="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type Window}}, Path=WindowState}" Value="Maximized">
                    <Setter Property="Button.Content" Value="&#xE923;" />
                </DataTrigger>
            </Style.Triggers>
        </Style>
    </Button.Style>
</Button>
<Button Click="Close_Click" Content="&#xE106;" FontFamily="Segoe MDL2 Assets" FontSize="10" Padding="15,10" Background="Transparent" BorderBrush="Transparent" />
</StackPanel>

If you want Support for older Windows Versions (7 and 8) have a look here: https://stackoverflow.com/a/27911618/9758687

如果您希望支持较旧的 Windows 版本(7 和 8),请查看此处:https: //stackoverflow.com/a/27911618/9758687

回答by RandomEngy

Here's an overview of the approach you'll need to take:

以下是您需要采取的方法的概述:

  • Set WindowStyle="None"to do your own UI.
  • Use WindowChrome.CaptionHeightto get standard title bar drag/double click/shake behavior and set WindowChrome.IsHitTestVisibleInChrome="True"on your buttons to make them clickable.
  • Implement click handlers for your buttons.
  • Hook into the Window.StateChanged event to handle maximize/restore changes and update your UI. You can't assume everyone is using your title bar buttons to maximize and restore. This can happen via keyboard shortcuts (Win+Up/Win+Down) or double-clicking the title bar.
  • 7px of your window gets cut off from all sides when you maximize. You need to compensate with extra margin when the window is maximized.
  • You'll need to re-implement a window border to provide contrast over different backgrounds.
  • Use <Path> to render the title bar icons so they look good at different DPIs.
  • Change the look of the title bar depending on whether the window is active, to give the user an indication of which window has focus.
  • 设置WindowStyle="None"做你自己的用户界面。
  • 使用WindowChrome.CaptionHeight获得标准标题栏拖动/双击/抖动行为,并设置WindowChrome.IsHitTestVisibleInChrome="True"你的按钮,使他们点击。
  • 为您的按钮实现点击处理程序。
  • 连接到 Window.StateChanged 事件以处理最大化/恢复更改并更新您的 UI。你不能假设每个人都在使用你的标题栏按钮来最大化和恢复。这可以通过键盘快捷键 (Win+Up/Win+Down) 或双击标题栏来实现。
  • 当您最大化时,您的窗口的 7px 会从四面八方被切断。当窗口最大化时,您需要用额外的边距进行补偿。
  • 您需要重新实现窗口边框以提供不同背景的对比度。
  • 使用 <Path> 来渲染标题栏图标,使它们在不同的 DPI 下看起来都不错。
  • 根据窗口是否处于活动状态更改标题栏的外观,以向用户指示哪个窗口具有焦点。

The full writeup is a little long; I go over them in detail with code examples in this blog post.

完整的文章有点长;我在这篇博文中使用代码示例详细介绍了它们