通过透明的AppBar无法看到桌面墙纸

时间:2014-04-10 19:25:54

标签: c# wpf desktop wallpaper appbar

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

实施例: Transparent appbar example

在上图中,您可以看到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并上传它供所有人使用。

提前致谢。

2 个答案:

答案 0 :(得分:1)

编辑1

关于什么是窗口句柄有一些问题:

What is a Windows Handle?

Difference between HANDLE and HWND in Windows API?

现在,如果我们查看Windows Data Types

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;

换句话说,它是一个无效指针,它指的是某种类型未知的东西,这是故意的。

以下是一些解释:What is a void pointer in C++?

结论

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

在.NET框架中,您无法直接访问此类型,您可以使用自己使用该类型的FormWindow。现在,当您必须执行某些较低级别的操作时,可以在Window上执行它们,因此您需要指定要执行此操作的HWND。现在在.NET中,您通常将其表示为IntPtr


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

我的建议仍然适用。


我的appBar背景变黑了

因为你保留了那个没有任何东西的空间,因此它是黑色的。

可能的解决方案:

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

除非他们告诉你如何,否则我们无法告诉Stardock怎么做。

如果MSDN中没有任何内容和/或您无法使用C#,那么可能就是他们的做法

答案 1 :(得分:1)

结果证明这是答案。我并不聪明,但它确实有效。

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