如何创建一个没有边框且只能通过手柄调整大小的WPF窗口?

105
如果在WPF的Window上设置了ResizeMode="CanResizeWithGrip",则会在右下角显示一个调整大小手柄,如下所示:

如果同时设置了WindowStyle="None",则标题栏消失,但灰色斜面边缘仍然存在,直到您设置ResizeMode="NoResize"。不幸的是,使用这组设置后,调整窗口大小的手柄也会消失。
我通过自定义Style覆盖了WindowControlTemplate。我想要自己指定窗口的边框,并且我不需要用户能够从四个方向调整窗口大小,但我确实需要一个调整大小的手柄。
有人能详细说明一种简单的方法来满足所有这些标准吗?
  1. 除了在ControlTemplate中指定的边框之外,不要在Window上设置边框。
  2. 确保在右下角有可用的调整大小手柄。
  3. 不要有标题栏。

3
请注意,Allowtransperency会导致内存泄漏。因此请避免使用它。 请参考http://social.msdn.microsoft.com/Forums/en/wpf/thread/fc76b788-c4a6-45f4-896e-5646dffd2155。 - Dipesh Bhatt
1
@DipeshBhatt 我在你提供的链接中找不到任何解释。也许你想发这个链接:https://social.msdn.microsoft.com/Forums/vstudio/en-US/69bae823-dbe9-4a38-b964-e87b71195bb6/window-on-seperate-thread-memory-leak-when-allowtransparencytrue-progress-window?forum=wpf - itsho
1
尽管我已将窗口样式设置为None,但仍然面临顶部灰边的问题。ResizeMode="NoResize"解决了我的问题。 - Surendra Shrestha
5个回答

197
如果您在Window上设置了AllowsTransparency属性(即使未设置任何透明度值),边框将消失,您只能通过手柄调整大小。
<Window
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Width="640" Height="480" 
    WindowStyle="None"
    AllowsTransparency="True"
    ResizeMode="CanResizeWithGrip">

    <!-- Content -->

</Window>

结果看起来是这样的:


纯属巧合,我知道这个 - 我今天下午也在玩同样的控制集合。 :) - ZombieSheep
2
哇,我没想到这个功能如此方便,只需5分钟就能制作自己的便签纸,谢谢 :) - Tomáš Kafka
4
AllowTransparency存在一些缺陷,例如Windows无法再托管子窗口控件,例如WebBrowser,通常会强制软件渲染,还会导致内存泄漏问题。请参见下面我的解决方法。 - Wobbles
你只需要使用WindowStyle="None"来去掉边框;AllowsTransparency只是需要它,但不会影响边框。 - Grault
2
@Grault 这样可以去掉窗口标题栏,但是表单周围仍然有一个实心边框。AllowsTransparency 可以去掉边框。 - Wobbles
简单的事情更有效。 - user5447154

107
我想创建一个没有边框的窗口,可以使用WindowStyle="None",但是测试时发现在顶部出现了一个白色条,经过一些研究发现它是一个"调整大小的边框",这里是图片(我用黄色标出来): The Challenge 在互联网上进行了一些研究,并且找到了许多非 XAML 的解决方案,但所有的解决方法都是使用 C# 的代码实现的,代码很长。最终我在这里间接地找到了解决方案:Maximum custom window loses drop shadow effect
<WindowChrome.WindowChrome>
    <WindowChrome 
        CaptionHeight="0"
        ResizeBorderThickness="5" />
</WindowChrome.WindowChrome>

注意:您需要使用.NET 4.5框架,或者如果您使用的是旧版本,请使用WPFShell,只需引用shell并使用Shell:WindowChrome.WindowChrome即可。

我使用了窗口的WindowChrome属性,如果您使用它,白色的“调整大小边框”将消失,但您需要定义一些属性才能正常工作。

CaptionHeight:这是标题区域(标题栏)的高度,允许像普通标题栏一样进行Aero快照、双击行为。将其设置为0(零)以使按钮起作用。

ResizeBorderThickness:这是窗口边缘处的厚度,您可以在那里调整窗口大小。我将其设为5,因为我喜欢这个数字,而且因为如果你把它设为0,调整窗口大小就很困难。

使用这个简短的代码后,结果如下所示:

解决方案

现在,白色边框消失了,而不需要使用ResizeMode="NoResize"AllowsTransparency="True",也在窗口中显示了阴影。

稍后我将解释如何使用简单而短小的代码轻松地使按钮工作(我没有使用按钮的图像),我很新,认为我可以发布到codeproject,因为在这里我找不到发布教程的地方。

也许还有另一种解决方案(我知道有些对于像我这样的新手来说是困难和复杂的解决方案),但这适用于我的个人项目。

这是完整的代码:

<Window x:Class="MainWindow"
    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:local="clr-namespace:Concursos"
    mc:Ignorable="d"
    Title="Concuros" Height="350" Width="525"
    WindowStyle="None"
    WindowState="Normal" 
    ResizeMode="CanResize"
    >
<WindowChrome.WindowChrome>
    <WindowChrome 
        CaptionHeight="0"
        ResizeBorderThickness="5" />
</WindowChrome.WindowChrome>

    <Grid>

    <Rectangle Fill="#D53736" HorizontalAlignment="Stretch" Height="35" VerticalAlignment="Top" PreviewMouseDown="Rectangle_PreviewMouseDown" />
    <Button x:Name="Btnclose" Content="r" HorizontalAlignment="Right" VerticalAlignment="Top" Width="35" Height="35" Style="{StaticResource TempBTNclose}"/>
    <Button x:Name="Btnmax" Content="2" HorizontalAlignment="Right" VerticalAlignment="Top" Margin="0,0,35,0" Width="35" Height="35" Style="{StaticResource TempBTNclose}"/>
    <Button x:Name="Btnmin" Content="0" HorizontalAlignment="Right" VerticalAlignment="Top" Margin="0,0,70,0" Width="35" Height="35" Style="{StaticResource TempBTNclose}"/>

</Grid>

谢谢!


5
为这个答案点赞!到目前为止,这是本帖中最简单/没有任何权衡的答案!应该获得更多的“顶”票。我必须承认,我对它有点怀疑,特别是关于内部发生了什么事情。我甚至检查了 WFP 树,它看起来确实没有添加透明度。即使像“WebBrowser”这样棘手的控件也可以正常显示。当应用程序处于很大压力下并使用透明度时,我遇到了渲染冻结问题...但是,这种解决方案不会出现这种情况。我认为现在是时候淘汰 @Wobbles 的解决方案了! - VeV
1
如果我正确理解了Rectangle_PreviewMouseDown事件的使用,似乎仍需要进行窗口拖动的互操作。 - Wobbles
我在Windows 10(秋季创作者更新)上使用此代码时,在窗口阴影应该出现的地方会出现奇怪的蓝色矩形闪烁,可能是我的显卡问题。 - 1adam12
你能提供静态资源StaticResource TempBTNclose吗?我想让我的无边框窗口按钮看起来和感觉相同。 - Willy
如果您需要在代码中设置WindowChrome,以下是如何操作的示例:w.SetValue(System.Windows.Shell.WindowChrome.WindowChromeProperty, new System.Windows.Shell.WindowChrome() { CaptionHeight = 0, ResizeBorderThickness = new Thickness(5) });其中w是类型为Window的变量。 - Daap
显示剩余11条评论

43

虽然被接受的答案非常正确,但我想指出AllowTransparency有一些缺点。它不允许子窗口控件显示,例如WebBrowser,并且通常会强制进行软件渲染,这可能会对性能产生负面影响。

不过,还有一个更好的解决办法。

当你想创建一个没有边框,可调整大小并能够托管指向URL的WebBrowser控件或Frame控件的窗口时,你就会发现无法实现,因为该控件的内容将为空。

我找到了一种解决方法;在Window中,如果将WindowStyle设置为None,ResizeMode设置为NoResize(请耐心等待,完成后仍然可以调整大小),然后确保已取消选中AllowsTransparency,你将获得一个静态大小的无边框窗口,并且将显示浏览器控件。

现在,你可能仍然想能够调整大小吧?那么我们可以使用Interop调用来实现:

    [DllImport("user32.dll", CharSet = CharSet.Auto)]
    private static extern IntPtr SendMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);

    [DllImportAttribute("user32.dll")]
    public static extern bool ReleaseCapture();

    //Attach this to the MouseDown event of your drag control to move the window in place of the title bar
    private void WindowDrag(object sender, MouseButtonEventArgs e) // MouseDown
    {
        ReleaseCapture();
        SendMessage(new WindowInteropHelper(this).Handle,
            0xA1, (IntPtr)0x2, (IntPtr)0);
    }

    //Attach this to the PreviewMousLeftButtonDown event of the grip control in the lower right corner of the form to resize the window
    private void WindowResize(object sender, MouseButtonEventArgs e) //PreviewMousLeftButtonDown
    {
        HwndSource hwndSource = PresentationSource.FromVisual((Visual)sender) as HwndSource;
        SendMessage(hwndSource.Handle, 0x112, (IntPtr)61448, IntPtr.Zero);
    }

瞧,一个没有边框但仍可移动和调整大小且不会失去与WebBrowser等控件兼容性的WPF窗口。


如果我们想从所有方向调整大小,而不仅仅是右下角,但仍然希望没有边框可见,我们应该怎么做? - Daniel
7
以下是其他的wParam值,根据需要使用它们为新的UI对象分配新事件。 private enum ResizeDirection { Left = 61441, Right = 61442, Top = 61443, TopLeft = 61444, TopRight = 61445, Bottom = 61446, BottomLeft = 61447, BottomRight = 61448, } - Wobbles
这对我来说非常好用,但有一个例外,一旦你使用了NoResize,就不能再通过将窗口拖到顶部来捕捉它了。 - CJK
2
@CJK 当然,但毫无疑问,您也可以挂钩Windows消息并处理它。 - Wobbles
我无法拖动窗口。有什么想法为什么?(调整大小非常顺畅) - Li3ro
我在这里发现的唯一问题是:我在窗口周围放置了边框以捕获事件。 运作良好。 我将给定边框的光标设置为在鼠标悬停时显示调整大小光标。 但是,在拖动/调整大小时,光标可能会重置... 但这只是一个小问题。 - dba

6

这里是一个示例:

<Style TargetType="Window" x:Key="DialogWindow">
        <Setter Property="AllowsTransparency" Value="True"/>
        <Setter Property="WindowStyle" Value="None"/>
        <Setter Property="ResizeMode" Value="CanResizeWithGrip"/>
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type Window}">
                    <Border BorderBrush="Black" BorderThickness="3" CornerRadius="10" Height="{TemplateBinding Height}"
                            Width="{TemplateBinding Width}" Background="Gray">
                        <DockPanel>
                            <Grid DockPanel.Dock="Top">
                                <Grid.ColumnDefinitions>
                                    <ColumnDefinition></ColumnDefinition>
                                    <ColumnDefinition Width="50"/>
                                </Grid.ColumnDefinitions>
                                <Label Height="35" Grid.ColumnSpan="2"
                                       x:Name="PART_WindowHeader"                                            
                                       HorizontalAlignment="Stretch" 
                                       VerticalAlignment="Stretch"/>
                                <Button Width="15" Height="15" Content="x" Grid.Column="1" x:Name="PART_CloseButton"/>
                            </Grid>
                            <Border HorizontalAlignment="Stretch" VerticalAlignment="Stretch" 
                                        Background="LightBlue" CornerRadius="0,0,10,10" 
                                        Grid.ColumnSpan="2"
                                        Grid.RowSpan="2">
                                <Grid>
                                    <Grid.ColumnDefinitions>
                                        <ColumnDefinition/>
                                        <ColumnDefinition Width="20"/>
                                    </Grid.ColumnDefinitions>
                                    <Grid.RowDefinitions>
                                        <RowDefinition Height="*"/>
                                        <RowDefinition Height="20"></RowDefinition>
                                    </Grid.RowDefinitions>
                                    <ResizeGrip Width="10" Height="10" Grid.Column="1" VerticalAlignment="Bottom" Grid.Row="1"/>
                                </Grid>
                            </Border>
                        </DockPanel>
                    </Border>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>

25
你应该简要说明代码的工作原理。 - M456

2

我在使用WindowChrome时遇到了困难,无法得到@fernando-aguirre的答案。这是因为我在MainWindow中重写了OnSourceInitialized方法,但没有调用基类方法。

protected override void OnSourceInitialized(EventArgs e)
{
    ViewModel.Initialize(this);
    base.OnSourceInitialized(e); // <== Need to call this!
}

这让我困扰了很长时间。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接