在无边框 WPF 窗口上启用 Vista 玻璃效果

发布于 2024-08-02 23:22:14 字数 925 浏览 4 评论 0原文

我编写了一个附加属性,可以在窗口上设置该属性,以将玻璃框架延伸到客户区(使用 DwmExtendFrameIntoClientArea API)。在大多数情况下它工作得很好。现在我希望我的窗口是无边框的,所以我在窗口上设置了以下属性:

    WindowStyle="None"
    ResizeMode="NoResize"
    Background="Transparent"
    u:WinUtil.EnableGlass="True"
    ShowInTaskbar="False"

但是使用这些属性,玻璃根本不显示:我的窗口只有透明背景。如果我将 ResizeMode 设置为 CanResize,则会显示玻璃,但我不希望窗口可调整大小。

我怀疑这是因为玻璃效果是通过将非客户端框架延伸到客户端区域来获得的:使用 WindowStyle = NoneResizeMode = NoResize,有不是非客户端框架,因此无需扩展。当我启用调整大小时,它会在窗口周围创建一个框架,因此可以扩展该框架。

我想应该可以通过设置适当的 WS_* 位来创建一个具有细边框、没有标题栏且无法调整大小的窗口,但我不知道到底是哪些

我的问题是:

  1. 所以 应该设置或取消设置样式位以获得所需的外观和行为?
  2. 如何初始化窗口的样式位? Window 类似乎没有类似 Windows 窗体 CreateParams 属性的内容...在创建句柄后设置这些位可以吗?
  3. 我发现 HwndSource 类可以作为问题 2 的答案,但如果您不是 Win32 专家,那么使用起来似乎有点复杂...这对我的问题来说是一个明智的解决方案吗?

欢迎任何建议

I wrote an attached property that I can set on a window to extend the glass frame into the client area (using the DwmExtendFrameIntoClientArea API). It works fine in most cases. Now I want my window to be borderless, so I set the following attributes on my window :

    WindowStyle="None"
    ResizeMode="NoResize"
    Background="Transparent"
    u:WinUtil.EnableGlass="True"
    ShowInTaskbar="False"

But with these attributes, the glass doesn't show up at all : my window just has a transparent background. If I set ResizeMode to CanResize, the glass is shown, but I don't want the window to be resizable.

I suspect it is due to the fact that the glass effect is obtained by extending the non-client frame into the client area : with WindowStyle = None and ResizeMode = NoResize, there is no non-client frame, so there's nothing to extend. When I enable resizing, it creates a frame around the window, so the frame can be extended.

I guess it should be possible to create a window that has a thin border, no title bar, and can't be resized, by setting the appropriate WS_* bits, but I don't know which ones exactly

So my questions are :

  1. Which style bits should be set or unset to have the desired appearance and behavior ?
  2. How can I initialize the window's style bits ? The Window class doesn't seem to have anything like Windows Forms CreateParams property... Is it OK to set these bits after the handle has been created ?
  3. I found the HwndSource class that could be an answer to question 2, but it seems a bit complex to use if you're not a Win32 expert... Would it be a sensible solution to my problem ?

Any advice is welcome

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

泅人 2024-08-09 23:22:14

您是否尝试过使用DwmEnableBlurBehindWindow?这使您能够使窗口客户区的特定部分透明。

Have you tried using DwmEnableBlurBehindWindow? This enables you to make a specific part of a window's client area transparent.

月光色 2024-08-09 23:22:14

我有一个窗口,我只想给它一个玻璃边框(没有标题栏且不可调整大小),但遇到了与您相同的问题。仅通过设置窗口的样式无法实现此目的。我的解决方案是设置 ResizeMode="CanResize" 和 WindowStyle="None",然后处理 WM_NCHITTEST 事件,将可调整大小的边框点击转换为不可调整大小的边框点击。还需要修改窗口的样式以禁用最大化和最小化(使用 Windows 快捷方式)和系统菜单:

private void Window_SourceInitialized(object sender, EventArgs e)
{
    System.Windows.Interop.HwndSource source = (System.Windows.Interop.HwndSource)PresentationSource.FromVisual(this);
    source.AddHook(new System.Windows.Interop.HwndSourceHook(HwndSourceHook));

    IntPtr hWnd = new System.Windows.Interop.WindowInteropHelper(this).Handle;
    IntPtr flags = GetWindowLongPtr(hWnd, -16 /*GWL_STYLE*/);
    SetWindowLongPtr(hWnd, -16 /*GWL_STYLE*/, new IntPtr(flags.ToInt64() & ~(0x00010000L /*WS_MAXIMIZEBOX*/ | 0x00020000L /*WS_MINIMIZEBOX*/ | 0x00080000L /*WS_SYSMENU*/)));
}

private static IntPtr HwndSourceHook(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
{
    switch (msg)
    {
        case 0x0084 /*WM_NCHITTEST*/:
            IntPtr result = DefWindowProc(hwnd, msg, wParam, lParam);
            if (result.ToInt32() >= 10 /*HTLEFT*/ && result.ToInt32() <= 17 /*HTBOTTOMRIGHT*/ )
            {
                handled = true;
                return new IntPtr(18 /*HTBORDER*/);
            }
            break;
    }
    return IntPtr.Zero;
}

[System.Runtime.InteropServices.DllImport("user32.dll")]
private static extern IntPtr DefWindowProc(IntPtr hWnd, int msg, IntPtr wParam, IntPtr lParam);

[System.Runtime.InteropServices.DllImport("user32.dll", SetLastError = true)]
private static extern IntPtr SetWindowLongPtr(IntPtr hWnd, int nIndex, IntPtr dwNewLong);
[System.Runtime.InteropServices.DllImport("user32.dll", SetLastError = true)]
private static extern IntPtr GetWindowLongPtr(IntPtr hWnd, int nIndex);

这为您提供了 Windows 7 中适合通知区域弹出窗口(例如时钟或音量弹出窗口)的窗口。顺便说一句,您可以通过创建高度 44 的控件并设置其背景来重现弹出按钮底部的阴影:

<Control.Background>
    <LinearGradientBrush StartPoint="0,0" EndPoint="0,1">
        <GradientStop Color="{x:Static SystemColors.GradientActiveCaptionColor}" Offset="0"/>
        <GradientStop Color="{x:Static SystemColors.InactiveBorderColor}" Offset="0.1"/>
    </LinearGradientBrush>
</Control.Background>

I had a Window that I wanted to give just a glass boarder (no title bar and non-resizable) and ran into the same problem as you. You cannot accomplish this just by setting the Window's style. My solution was to set ResizeMode="CanResize" and WindowStyle="None" then handle the WM_NCHITTEST event to convert resizable border hits to non-resizable border hits. It was also necessary to modify the Window's style to disable maximizing and minimizing (using Windows shortcuts) and the system menu:

private void Window_SourceInitialized(object sender, EventArgs e)
{
    System.Windows.Interop.HwndSource source = (System.Windows.Interop.HwndSource)PresentationSource.FromVisual(this);
    source.AddHook(new System.Windows.Interop.HwndSourceHook(HwndSourceHook));

    IntPtr hWnd = new System.Windows.Interop.WindowInteropHelper(this).Handle;
    IntPtr flags = GetWindowLongPtr(hWnd, -16 /*GWL_STYLE*/);
    SetWindowLongPtr(hWnd, -16 /*GWL_STYLE*/, new IntPtr(flags.ToInt64() & ~(0x00010000L /*WS_MAXIMIZEBOX*/ | 0x00020000L /*WS_MINIMIZEBOX*/ | 0x00080000L /*WS_SYSMENU*/)));
}

private static IntPtr HwndSourceHook(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
{
    switch (msg)
    {
        case 0x0084 /*WM_NCHITTEST*/:
            IntPtr result = DefWindowProc(hwnd, msg, wParam, lParam);
            if (result.ToInt32() >= 10 /*HTLEFT*/ && result.ToInt32() <= 17 /*HTBOTTOMRIGHT*/ )
            {
                handled = true;
                return new IntPtr(18 /*HTBORDER*/);
            }
            break;
    }
    return IntPtr.Zero;
}

[System.Runtime.InteropServices.DllImport("user32.dll")]
private static extern IntPtr DefWindowProc(IntPtr hWnd, int msg, IntPtr wParam, IntPtr lParam);

[System.Runtime.InteropServices.DllImport("user32.dll", SetLastError = true)]
private static extern IntPtr SetWindowLongPtr(IntPtr hWnd, int nIndex, IntPtr dwNewLong);
[System.Runtime.InteropServices.DllImport("user32.dll", SetLastError = true)]
private static extern IntPtr GetWindowLongPtr(IntPtr hWnd, int nIndex);

This gives you a Window in Windows 7 suitable for notification area flyouts (e.g. the clock or volume flyouts). BTW, you can reproduce the shading at the bottom of the flyout by creating a control of height 44 and setting it's background:

<Control.Background>
    <LinearGradientBrush StartPoint="0,0" EndPoint="0,1">
        <GradientStop Color="{x:Static SystemColors.GradientActiveCaptionColor}" Offset="0"/>
        <GradientStop Color="{x:Static SystemColors.InactiveBorderColor}" Offset="0.1"/>
    </LinearGradientBrush>
</Control.Background>
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文