【问题标题】:How to create custom window chrome in wpf?如何在 wpf 中创建自定义窗口镶边?
【发布时间】:2011-10-11 03:50:10
【问题描述】:

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

【问题讨论】:

标签: wpf xaml window


【解决方案1】:

您设置您的窗口WindowStyle="None",然后构建您自己的窗口界面。您需要构建自己的 Min/Max/Close/Drag 事件处理程序,但仍保留调整大小。

例如:

<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>

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

/// <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;
        MaxButton.Content = "1";
    }
    else
    {
        this.WindowState = WindowState.Maximized;
        MaxButton.Content = "2";
    }

}

【讨论】:

  • 其实,使用这个-vs-主窗口-vs-应用程序有点忙(因为容易出错)。 Alex 有更可靠的implementation,尽管它也缺少一些小的功能部分。
  • 您可能还希望在 Titlebar 停靠面板上使用 DockPanel.Dock="Top"
  • 只需使用Shell:WindowChrome.CaptionHeight,您不需要任何标题栏单击拖动废话。没有理由重新发明轮子。还保留其他默认窗口行为,例如双击最大化、摇动以独奏。
  • 最好使用WindowChrome.CaptionHeight来拥有所有的标题栏功能。 @RandomEngy 与按钮处的WindowChrome.IsHitTestVisibleInChrome="True" 组合将使它们在标题栏区域中可点击。
  • 明确地说,WindowChrome.IsHitTestVisibleInChrome 附加属性位于各个按钮上。当你把它放在窗户上时不起作用。
【解决方案2】:

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

WindowChrome class 使您能够将 Windows Presentation Foundation (WPF) 内容扩展到通常为操作系统的窗口管理器保留的窗口的非客户区。

您可以找到tutorial here

这是short example usage

【讨论】:

  • 教程充满了死链接和弃用的信息,简短的示例包括一个不再有效的程序集参考。
  • 感谢您告诉我。如果我发现更好的东西,我会发布它。
  • 我知道有点晚了,但我在这里找到了一个很好的例子:engy.us/blog/2020/01/01/…
  • @SomeCode.NET 确实很好的例子!节省大量时间,谢谢!
【解决方案3】:

我刚刚将下面的示例用于 .net 4.5,它运行良好。有趣的是,它为点击事件的资源字典使用了代码。您所要做的就是在app.xaml 文件中引用资源字典,然后为窗口分配样式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>

对于后面的代码:

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!");
        }
    }
}

【讨论】:

    【解决方案4】:

    这是一个简单的解决方案,看起来与默认的 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>
    

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

    【讨论】:

      【解决方案5】:

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

      • 设置 WindowStyle="None" 来做你自己的 UI。
      • 使用WindowChrome.CaptionHeight 获得标准的标题栏拖动/双击/摇动行为,并在您的按钮上设置WindowChrome.IsHitTestVisibleInChrome="True" 以使其可点击。
      • 为您的按钮实现点击处理程序。
      • 挂钩到 Window.StateChanged 事件以处理最大化/恢复更改并更新您的 UI。你不能假设每个人都在使用你的标题栏按钮来最大化和恢复。这可以通过键盘快捷键(Win+Up/Win+Down)或双击标题栏来实现。
      • 当您最大化时,窗口的 7px 会从所有侧面被切断。为了解决这个问题,我们需要挂钩 WM_GETMINMAXINFO 以提供正确的最大化位置。
      • 您需要重新实现窗口边框以提供不同背景的对比度。
      • 使用 渲染标题栏图标,以便它们在不同的 DPI 下看起来不错。
      • 根据窗口是否处于活动状态更改标题栏的外观,以便向用户指示哪个窗口具有焦点。

      完整的文章有点长; I go over them in detail with code examples in this blog post.

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2014-11-29
        • 1970-01-01
        • 1970-01-01
        • 2010-09-07
        • 2012-08-09
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多