1

我实际上想制作一个为自己保留桌面空间的侧边栏应用程序。appBar 本身不是问题。但是我希望它是透明的。

例子: 透明应用栏示例

在上图中,您可以看到stardock 如何保留它的桌面空间,但我的背景图像仍然完全可见,并且应用程序本身是透明的。

当我运行我的 appBar 时,背景会自动变为黑色,并且我的背景会随之缩放。显然我也不能截图,所以我不能举例说明它的外观。

这是我的 AppBar 的代码:(注意:代码设置为 1920px 偏移量,因此它可以停靠在我的第二台显示器上(没有时间进行抽象编码。)

using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Windows;
using System.Windows.Interop;
using System.Windows.Threading;

namespace ProjectSideBar
{
public enum ABEdge : int
{
    Left = 0,
    Top,
    Right,
    Bottom,
    None
}

internal static class AppBarFunctions
{
    [StructLayout(LayoutKind.Sequential)]
    private struct RECT
    {
        public int left;
        public int top;
        public int right;
        public int bottom;
    }

    [StructLayout(LayoutKind.Sequential)]
    private struct APPBARDATA
    {
        public int cbSize;
        public IntPtr hWnd;
        public int uCallbackMessage;
        public int uEdge;
        public RECT rc;
        public IntPtr lParam;
    }

    [StructLayout(LayoutKind.Sequential)]
    private struct MONITORINFO
    {
        public int cbSize;
        public RECT rcMonitor;
        public RECT rcWork;
        public int dwFlags;
    }

    private enum ABMsg : int
    {
        ABM_NEW = 0,
        ABM_REMOVE,
        ABM_QUERYPOS,
        ABM_SETPOS,
        ABM_GETSTATE,
        ABM_GETTASKBARPOS,
        ABM_ACTIVATE,
        ABM_GETAUTOHIDEBAR,
        ABM_SETAUTOHIDEBAR,
        ABM_WINDOWPOSCHANGED,
        ABM_SETSTATE
    }
    private enum ABNotify : int
    {
        ABN_STATECHANGE = 0,
        ABN_POSCHANGED,
        ABN_FULLSCREENAPP,
        ABN_WINDOWARRANGE
    }

    [DllImport("SHELL32", CallingConvention = CallingConvention.StdCall)]
    private static extern uint SHAppBarMessage(int dwMessage, ref APPBARDATA pData);

    [DllImport("User32.dll", CharSet = CharSet.Auto)]
    private static extern int RegisterWindowMessage(string msg);

    [DllImport("User32.dll", CharSet = CharSet.Auto)]
    private static extern IntPtr MonitorFromWindow(IntPtr hwnd, uint dwFlags);

    [DllImport("User32.dll", CharSet = CharSet.Auto)]
    private static extern bool GetMonitorInfo(IntPtr hMonitor, ref MONITORINFO mi);


    private const int MONITOR_DEFAULTTONEAREST = 0x2;
    private const int MONITORINFOF_PRIMARY = 0x1;

    private class RegisterInfo
    {
        public int CallbackId { get; set; }
        public bool IsRegistered { get; set; }
        public Window Window { get; set; }
        public ABEdge Edge { get; set; }
        public WindowStyle OriginalStyle { get; set; }
        public Point OriginalPosition { get; set; }
        public Size OriginalSize { get; set; }
        public ResizeMode OriginalResizeMode { get; set; }


        public IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam,
                                IntPtr lParam, ref bool handled)
        {
            if (msg == CallbackId)
            {
                if (wParam.ToInt32() == (int)ABNotify.ABN_POSCHANGED)
                {
                    ABSetPos(Edge, Window);
                    handled = true;
                }
            }
            return IntPtr.Zero;
        }

    }
    private static Dictionary<Window, RegisterInfo> s_RegisteredWindowInfo
        = new Dictionary<Window, RegisterInfo>();
    private static RegisterInfo GetRegisterInfo(Window appbarWindow)
    {
        RegisterInfo reg;
        if (s_RegisteredWindowInfo.ContainsKey(appbarWindow))
        {
            reg = s_RegisteredWindowInfo[appbarWindow];
        }
        else
        {
            reg = new RegisterInfo()
            {
                CallbackId = 0,
                Window = appbarWindow,
                IsRegistered = false,
                Edge = ABEdge.Top,
                OriginalStyle = appbarWindow.WindowStyle,
                OriginalPosition = new Point(appbarWindow.Left, appbarWindow.Top),
                OriginalSize =
                    new Size(appbarWindow.ActualWidth, appbarWindow.ActualHeight),
                OriginalResizeMode = appbarWindow.ResizeMode,
            };
            s_RegisteredWindowInfo.Add(appbarWindow, reg);
        }
        return reg;
    }

    private static void RestoreWindow(Window appbarWindow)
    {
        RegisterInfo info = GetRegisterInfo(appbarWindow);

        appbarWindow.WindowStyle = info.OriginalStyle;
        appbarWindow.ResizeMode = info.OriginalResizeMode;
        appbarWindow.Topmost = false;

        Rect rect = new Rect(info.OriginalPosition.X, info.OriginalPosition.Y,
            info.OriginalSize.Width, info.OriginalSize.Height);
        appbarWindow.Dispatcher.BeginInvoke(DispatcherPriority.ApplicationIdle,
                new ResizeDelegate(DoResize), appbarWindow, rect);

    }

    public static void SetAppBar(Window appbarWindow, ABEdge edge)
    {
        RegisterInfo info = GetRegisterInfo(appbarWindow);

        info.Edge = edge;

        APPBARDATA abd = new APPBARDATA();
        abd.cbSize = Marshal.SizeOf(abd);
        abd.hWnd = new WindowInteropHelper(appbarWindow).Handle;

        if (edge == ABEdge.None)
        {
            if (info.IsRegistered)
            {
                SHAppBarMessage((int)ABMsg.ABM_REMOVE, ref abd);
                info.IsRegistered = false;
            }
            RestoreWindow(appbarWindow);
            return;
        }

        if (!info.IsRegistered)
        {
            info.IsRegistered = true;
            info.CallbackId = RegisterWindowMessage("AppBarMessage");
            abd.uCallbackMessage = info.CallbackId;

            uint ret = SHAppBarMessage((int)ABMsg.ABM_NEW, ref abd);

            HwndSource source = HwndSource.FromHwnd(abd.hWnd);
            source.AddHook(new HwndSourceHook(info.WndProc));
        }

        appbarWindow.WindowStyle = WindowStyle.None;
        appbarWindow.ResizeMode = ResizeMode.NoResize;
        appbarWindow.Topmost = true;

        ABSetPos(info.Edge, appbarWindow);
    }

    private delegate void ResizeDelegate(Window appbarWindow, Rect rect);
    private static void DoResize(Window appbarWindow, Rect rect)
    {
        appbarWindow.Width = rect.Width;
        appbarWindow.Height = rect.Height;
        appbarWindow.Top = rect.Top;
        appbarWindow.Left = rect.Left;
    }

    private static void GetActualScreenData(ABEdge edge, Window appbarWindow, ref int leftOffset, ref int topOffset, ref int actualScreenWidth, ref int actualScreenHeight)
    {
        IntPtr handle = new WindowInteropHelper(appbarWindow).Handle;
        IntPtr monitorHandle = MonitorFromWindow(handle, MONITOR_DEFAULTTONEAREST);

        MONITORINFO mi = new MONITORINFO();
        mi.cbSize = Marshal.SizeOf(mi);

        if (GetMonitorInfo(monitorHandle, ref mi))
        {
            if (mi.dwFlags == MONITORINFOF_PRIMARY)
            {
                return;
            }
            leftOffset = mi.rcWork.left;
            topOffset = mi.rcWork.top;
            actualScreenWidth = mi.rcWork.right - leftOffset;
            actualScreenHeight = mi.rcWork.bottom - mi.rcWork.top;
        }
    }

    private static void ABSetPos(ABEdge edge, Window appbarWindow)
    {
        APPBARDATA barData = new APPBARDATA();
        barData.cbSize = Marshal.SizeOf(barData);
        barData.hWnd = new WindowInteropHelper(appbarWindow).Handle;
        barData.uEdge = (int)edge;

        int leftOffset = 1920;
        int topOffset = 0;
        int actualScreenWidth = (int)SystemParameters.PrimaryScreenWidth;
        int actualScreenHeight = (int)SystemParameters.PrimaryScreenHeight;

        GetActualScreenData(edge, appbarWindow, ref leftOffset, ref topOffset, ref actualScreenWidth, ref actualScreenHeight);

        if (barData.uEdge == (int)ABEdge.Left || barData.uEdge == (int)ABEdge.Right)
        {
            barData.rc.top = topOffset;
            barData.rc.bottom = actualScreenHeight;
            if (barData.uEdge == (int)ABEdge.Left)
            {
                barData.rc.left = leftOffset;
                barData.rc.right = (int)Math.Round(appbarWindow.ActualWidth) + leftOffset;
            }
            else
            {
                barData.rc.right = actualScreenWidth + leftOffset;
                barData.rc.left = barData.rc.right - (int)Math.Round(appbarWindow.ActualWidth);
            }
        }
        else
        {
            barData.rc.left = leftOffset;
            barData.rc.right = actualScreenWidth + leftOffset;
            if (barData.uEdge == (int)ABEdge.Top)
            {
                barData.rc.top = topOffset;
                barData.rc.bottom = (int)Math.Round(appbarWindow.ActualHeight) + topOffset;
            }
            else
            {
                barData.rc.bottom = actualScreenHeight + topOffset;
                barData.rc.top = barData.rc.bottom - (int)Math.Round(appbarWindow.ActualHeight);
            }
        }

        SHAppBarMessage((int)ABMsg.ABM_QUERYPOS, ref barData);
        SHAppBarMessage((int)ABMsg.ABM_SETPOS, ref barData);

        Rect rect = new Rect((double)barData.rc.left, (double)barData.rc.top,
            (double)(barData.rc.right - barData.rc.left), (double)(barData.rc.bottom - barData.rc.top));
        //This is done async, because WPF will send a resize after a new appbar is added.  
        //if we size right away, WPFs resize comes last and overrides us.
        appbarWindow.Dispatcher.BeginInvoke(DispatcherPriority.ApplicationIdle,
            new ResizeDelegate(DoResize), appbarWindow, rect);
    }
}
}

这是我的主窗口中的代码:

<Window x:Class="ProjectSideBar.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="MainWindow" Loaded="Window_Loaded" Height="1080" Width="300" ResizeMode="NoResize" ShowInTaskbar="False" Background="{x:Null}">
<Grid>
    <Label Content="My application" HorizontalAlignment="Left" Margin="27,40,0,0" VerticalAlignment="Top" Foreground="White"/>
</Grid>

有没有人知道如何让它像stardock?此外,我不了解窗口句柄的工作原理,也不了解我使用的大部分代码。如果有人可以解释(或提供一些阅读材料)窗口把手的作用以及如何使用它们,那就太好了。

作为奖励,我将制作一个 ClassLibrary 并上传给大家使用。

提前致谢。

4

2 回答 2

1

编辑 1

关于什么是窗口句柄有几个问题:

什么是 Windows 句柄?

Windows API 中 HANDLE 和 HWND 的区别?

现在,如果我们看一下Windows 数据类型

HWND    
A handle to a window.
This type is declared in WinDef.h as follows:
typedef HANDLE HWND;

HANDLE  
A handle to an object.
This type is declared in WinNT.h as follows:
typedef PVOID HANDLE;

PVOID   
A pointer to any type.
This type is declared in WinNT.h as follows:
typedef void *PVOID;

换句话说,它是一个空指针,它指向类型未知的东西,这是故意的。

这里有一些解释:什么是 C++ 中的空指针?

结论

HWND 或窗口句柄指向此类:Windows

在 .NET 框架中,您不能直接访问此类型,您可以使用 aForm或 aWindow自己使用该类型。现在,当您必须像正在做的那样做一些较低级别的事情时,您可以在 a 上执行它们Window,因此您需要指定要执行此操作的 HWND。现在在 .NET 中,您通常将其表示为IntPtr


编辑 实际上有一个背景,但由于某些原因,WPF 应用程序是黑色的。

我的建议仍然适用。


我的appBar背景变黑了

这是有道理的,因为您要保留后面没有任何东西的空间,因此它是黑色的。

可能的解决方案:

  • 获取当前壁纸
  • 获取与您的应用栏一样高的区域
  • 使该区域成为您应用栏的背景
  • 使剩余区域成为当前的 Windows 背景
  • 当您的应用栏关闭时恢复原始背景

除了我们无法真正说出 Stardock 是怎么做的,除非他们告诉你怎么做。

如果 MSDN 中没有关于它的内容和/或您不能从 C# 中使用它,那么这可能就是他们的做法

于 2014-04-10T20:08:39.827 回答
1

原来这就是答案。我并不聪明,但它奏效了。

http://www.codeproject.com/Articles/232972/Creating-an-application-like-Google-Desktop-in-WPF

于 2014-04-11T14:38:54.947 回答