zoukankan      html  css  js  c++  java
  • WPF三种自定义窗体的实现

    目前我所知道的,有三种方式可以实现自定义窗体:WindowStyle="None"、WindowChrome、第三方库ControlzEx;但它们都有各自的优缺点,下面一一展示如何使用。

    一、WindowStyle="None"

    <Window x:Class="CustomWindows.TransparentWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
            xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
            xmlns:pui="clr-namespace:PumbaaUI.Controls;assembly=PumbaaUI"
            xmlns:i="http://schemas.microsoft.com/xaml/behaviors"
            xmlns:ctrlz="clr-namespace:ControlzEx.Behaviors;assembly=ControlzEx"
            mc:Ignorable="d"
            Title="TransparentWindow" Height="450" Width="800"
            WindowStyle="None" AllowsTransparency="True" ResizeMode="CanResizeWithGrip"
            Background="#CCFFFFFF" BorderThickness="1" BorderBrush="{DynamicResource PuiBrushes.Theme}">
        <Window.Template>
            <ControlTemplate TargetType="{x:Type Window}">
                <Grid>
                    <Border x:Name="LayoutRoot" BorderThickness="{TemplateBinding BorderThickness}" BorderBrush="{TemplateBinding BorderBrush}">
                        <DockPanel>
                            <Border x:Name="border_title" DockPanel.Dock="Top" Height="28" Background="{DynamicResource PuiBrushes.Window.Title.Background}">
                                <DockPanel>
                                    <StackPanel DockPanel.Dock="Right"></StackPanel>
                                    <TextBlock Margin="12 0 0 0" VerticalAlignment="Center" Foreground="{DynamicResource PuiBrushes.Window.Title.Foregound}" TextTrimming="WordEllipsis" Text="{TemplateBinding Title}" />
                                </DockPanel>
                            </Border>
                            <Border Background="{TemplateBinding Background}">
                                <AdornerDecorator>
                                    <ContentPresenter />
                                </AdornerDecorator>
                            </Border>
                        </DockPanel>
                    </Border>
                    <ResizeGrip x:Name="WindowResizeGrip" HorizontalAlignment="Right" VerticalAlignment="Bottom" IsTabStop="False" UseLayoutRounding="True" Visibility="Collapsed" />
                </Grid>
                <ControlTemplate.Triggers>
                    <Trigger Property="WindowState" Value="Maximized">
                        <Setter TargetName="LayoutRoot" Property="Margin" Value="{x:Static pui:WindowHelper.ChromeThickness}" />
                    </Trigger>
                    <MultiTrigger>
                        <MultiTrigger.Conditions>
                            <Condition Property="ResizeMode" Value="CanResizeWithGrip" />
                            <Condition Property="WindowState" Value="Normal" />
                        </MultiTrigger.Conditions>
                        <Setter TargetName="WindowResizeGrip" Property="Visibility" Value="Visible" />
                    </MultiTrigger>
                </ControlTemplate.Triggers>
            </ControlTemplate>
        </Window.Template>
        <i:Interaction.Behaviors>
            <ctrlz:GlowWindowBehavior GlowBrush="{DynamicResource PuiBrushes.Theme80}" NonActiveGlowBrush="{DynamicResource PuiBrushes.Theme80}" ResizeBorderThickness="6" />
        </i:Interaction.Behaviors>
    </Window>

    要注意的几个点:

    1、窗口大小无法拖拽;设置ResizeMode="CanResizeWithGrip",并且ControlTemplate的根元素是Panel,ResizeGrip其实可有可无;

    2、窗口最大化时,溢出屏幕;对内容元素设置Margin,不要对根元素设置Margin,不会有效果,Margin的大小可能屏幕有关,计算方式如下

    public static class WindowHelper
        {
            private static Nullable<Thickness> chromeThickness;
            public static Thickness ChromeThickness
            {
                get
                {
                    if (chromeThickness.HasValue)
                        return chromeThickness.Value;
    
                    var w = (SystemParameters.MaximizedPrimaryScreenWidth - SystemParameters.WorkArea.Width) / 2;
                    var h = (SystemParameters.MaximizedPrimaryScreenHeight - SystemParameters.WorkArea.Height) / 2;
    
                    chromeThickness = new Thickness(w, h, w, h);
                    return chromeThickness.Value;
                }
            }
        }

    未必正确,我这里计算的值是8,实际上7才是完全全屏,第三方UI框架ModernUI就是写死的7,不过它使用的是WindowChrome,而我使用WindowChrome的时候8才是正确的,7相当于去掉1单位的边框,而我只需要最大化时将边框设为0即可;

    3、允许窗口透明时,窗口没有发光效果;使用ControlzEx的GlowWindowBehavior可以设置窗口在Active和NoActive下发什么光;

    还有什么缺点:

    1、窗口最大化时覆盖了任务栏,相当于全屏;

    二、WindowChrome

    <Window x:Class="CustomWindows.ChormeWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
            xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
            xmlns:pui="clr-namespace:PumbaaUI.Controls;assembly=PumbaaUI"
            mc:Ignorable="d"
            Title="ChormeWindow" Height="450" Width="800" BorderThickness="1" BorderBrush="{DynamicResource PuiBrushes.Theme}">
        <WindowChrome.WindowChrome>
            <WindowChrome CornerRadius="0" GlassFrameThickness="1" />
        </WindowChrome.WindowChrome>
        <Window.Template>
            <ControlTemplate TargetType="{x:Type Window}">
                <Border>
                    <Border x:Name="LayoutRoot" BorderThickness="{TemplateBinding BorderThickness}" BorderBrush="{TemplateBinding BorderBrush}">
                        <DockPanel>
                            <Border x:Name="border_title" DockPanel.Dock="Top" Height="28" Background="{DynamicResource PuiBrushes.Window.Title.Background}">
                                <DockPanel>
                                    <StackPanel DockPanel.Dock="Right"></StackPanel>
                                    <TextBlock Margin="12 0 0 0" VerticalAlignment="Center" Foreground="{DynamicResource PuiBrushes.Window.Title.Foregound}" TextTrimming="WordEllipsis" Text="{TemplateBinding Title}" />
                                </DockPanel>
                            </Border>
                            <Border Background="{TemplateBinding Background}">
                                <AdornerDecorator>
                                    <ContentPresenter />
                                </AdornerDecorator>
                            </Border>
                        </DockPanel>
                    </Border>
                </Border>
                <ControlTemplate.Triggers>
                    <Trigger Property="WindowState" Value="Maximized">
                        <Setter TargetName="LayoutRoot" Property="Margin" Value="{x:Static pui:WindowHelper.ChromeThickness}" />
                    </Trigger>
                </ControlTemplate.Triggers>
            </ControlTemplate>
        </Window.Template>
    </Window>

    了解WindowChrome,可查看微软官方文档:https://docs.microsoft.com/zh-cn/dotnet/api/system.windows.shell.windowchrome

    要注意的几个点:

    1、GlassFrameThickness设置成负数才会出现默认窗口按钮(不推荐,建议自己添加按钮),设置成0则没有发光效果(默认是黑色的光,如果想用不同颜色的光,推荐使用ControlzEx的GlowWindowBehavior)

    2、需要注意的是CaptionHeight,指的是标题栏的高度,默认不等于0,右键标题栏会有一些窗口操作功能,并且在标题栏区域的按钮将无法响应鼠标事件,除非设置IsHitTestVisibleInChrome="True",不需要标题栏的可以将CaptionHeight设为0;

    3、和WindowStyle="None"一样,最大化时会溢出屏幕,解决方式也一样,不过Margin的计算是正确的;

    还有什么缺点:

    1、如果不设置WindowStyle="None"的话,窗口不能实现透明,设置了WindowStyle="None",如果GlassFrameThickness="0",则修复了最大化全屏的问题,但是窗口内容底部溢出(在本身的溢出上,又溢出了一个任务栏高度),并且在此时如果窗口的任务栏大小发生或设置成自动隐藏,窗口的会认为此时不是最大化状态,且无法恢复到原来的大小;如果GlassFrameThickness!="0",则跟WindowStyle="None"效果一样,就跟没设置WindowChrome一样;

    2、相比ControlzEx的WindowChromeBehavior,不能全屏显示,除非隐藏掉任务栏;

     补充:

    在设置ResizeMode=CanMinimize或NoResize时,最大化将显示为全屏;

    三、ControlzEx

    <Window x:Class="CustomWindows.ControlExWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
            xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
            xmlns:i="http://schemas.microsoft.com/xaml/behaviors"
            xmlns:ctrlz="clr-namespace:ControlzEx.Behaviors;assembly=ControlzEx"
            mc:Ignorable="d"
            Title="ControlExWindow" Height="450" Width="800"
            BorderThickness="1" BorderBrush="{DynamicResource PuiBrushes.Theme}">
        <Window.Template>
            <ControlTemplate TargetType="{x:Type Window}">
                <Border x:Name="LayoutRoot" BorderThickness="{TemplateBinding BorderThickness}" BorderBrush="{TemplateBinding BorderBrush}">
                    <DockPanel>
                        <Border x:Name="border_title" DockPanel.Dock="Top" Height="28" Background="{DynamicResource PuiBrushes.Window.Title.Background}">
                            <DockPanel>
                                <StackPanel DockPanel.Dock="Right"></StackPanel>
                                <TextBlock Margin="12 0 0 0" VerticalAlignment="Center" Foreground="{DynamicResource PuiBrushes.Window.Title.Foregound}" TextTrimming="WordEllipsis" Text="{TemplateBinding Title}" />
                            </DockPanel>
                        </Border>
                        <Border Background="{TemplateBinding Background}">
                            <AdornerDecorator>
                                <ContentPresenter />
                            </AdornerDecorator>
                        </Border>
                    </DockPanel>
                </Border>
            </ControlTemplate>
        </Window.Template>
        <i:Interaction.Behaviors>
            <ctrlz:WindowChromeBehavior IgnoreTaskbarOnMaximize="True" />
            <ctrlz:GlowWindowBehavior GlowBrush="{DynamicResource PuiBrushes.Theme80}" NonActiveGlowBrush="{DynamicResource PuiBrushes.Theme80}" ResizeBorderThickness="6" />
        </i:Interaction.Behaviors>
    </Window>

     了解ControlzEx,请查看https://github.com/ControlzEx/ControlzEx,内容很少,但每一点都可能是困扰你很久的问题;

    要注意的几个点:

    1、关于WindowChromeBehavior,设置IgnoreTaskbarOnMaximize="True",则最大化时覆盖任务栏,可以实现最大化和全屏的切换;

    2、关于GlowWindowBehavior,不设置Brush和ResizeBorderThickness是不会发光的,默认值是Null和0,发光效果实际上是由四条带BlurEffect的Border组成,所以性能上不敢苟同;

    还有什么缺点:

    1、无论是否设置WindowStyle="None",窗口都无法透明;

    总结:

    如果不需要透明窗体,使用ControlzEx是最完美的,最大化的时候也不用处理溢出,可自由地切换最大化和全屏;如果需要使用透明窗体,只能使用WindowStyle="None"的方式了,唯一的缺点就是最大化就是全屏,而一般的应用都不会超过任务栏的。

    另外第三方UI框架MahApps.Metro v2.1.1的窗口是比较完美的,它使用的是ControlzEx,但是效果又与我的demo不太一致,在窗口透明的情况下,窗口溢出且显示诡异,效果如下:

  • 相关阅读:
    PAT 甲级 1101 Quick Sort
    PAT 甲级 1038 Recover the Smallest Number
    #Leetcode# 112. Path Sum
    #Leetcode# 17. Letter Combinations of a Phone Number
    #Leetcode# 235. Lowest Common Ancestor of a Binary Search Tree
    C++结构体重构
    【NOIP2016提高A组模拟9.7】鼎纹
    快速幂总结
    【NOIP2013提高组】货车运输
    【NOIP2015提高组】运输计划
  • 原文地址:https://www.cnblogs.com/pumbaa/p/13306486.html
Copyright © 2011-2022 走看看