Обои рабочего стола не видны через прозрачную панель приложений

На самом деле я хочу сделать боковое приложение, которое резервирует место на рабочем столе для себя. AppBar сам по себе не проблема. Однако хотелось бы, чтобы он был прозрачным.

Пример: Пример прозрачной панели приложений

На изображении выше вы можете видеть, как stardock резервирует место на рабочем столе, но мое фоновое изображение все еще полностью видно, а само приложение прозрачно.

Когда я запускаю свой appBar, фон автоматически становится черным, и мой фон масштабируется вместе с ним. И, видимо, я тоже не могу сделать скриншот, поэтому не могу привести пример того, как это выглядит.

Вот код для моего AppBar: (Примечание: код установлен со смещением 1920 пикселей, поэтому он будет прикреплен к моему второму монитору (у меня не было времени для абстрактного кодирования).

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>

Does anyone have an Idea on how to make it like stardock? Also I have no understanding of how window handles work and I don't understand most of the code I used. If anyone can explain (or give some reading material) what the window handles do and how to work with them that would be great.

В награду я создам ClassLibrary и загружу ее для всех.

Заранее спасибо.


person RoXaS    schedule 10.04.2014    source источник


Ответы (2)


ИЗМЕНИТЬ 1

Есть несколько вопросов о том, что такое дескриптор окна:

Что такое дескриптор Windows?

Разница между HANDLE и HWND в Windows API?

Теперь, если мы посмотрим на 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;

Другими словами, это пустой указатель, он указывает на что-то, для чего тип неизвестен, это преднамеренно.

Вот некоторое объяснение: Что такое указатель void в C++?

Вывод

HWND или дескриптор окна указывает на этот класс: Windows

В среде .NET у вас нет прямого доступа к этому типу, вы либо используете Form, либо Window, которые сами используют этот тип. Теперь, когда вам нужно выполнить некоторые действия более низкого уровня, как вы делаете, вы выполняете их на Window, поэтому вам нужно указать, на каком HWND вы хотите выполнить это действие. Теперь в .NET вы обычно представляете это как IntPtr.


EDIT На самом деле есть фон, но по некоторым причинам приложение WPF черное.

Однако мое предложение остается в силе.


в моей панели приложений фон становится черным

Это имеет смысл, поскольку вы резервируете пространство, за которым ничего нет, поэтому оно черное.

Возможное решение:

  • Возьмите текущие обои
  • Получите область, которая находится на уровне панели приложений.
  • Сделайте этот регион фоном панели приложений.
  • Сделайте оставшуюся область текущим фоном Windows
  • Восстановите исходный фон, когда панель приложений закрывается

Кроме того, мы не можем точно сказать, как Stardock поступил, если они не скажут вам, как.

Если в MSDN об этом ничего нет и/или что вы не можете использовать из C#, то, вероятно, они так и сделали.

person aybe    schedule 10.04.2014
comment
Спасибо за ваш ответ, я думал об этом, но на самом деле мое фоновое изображение сжимается до нового оставшегося разрешения. так что все равно не подойдет. Он изменяется с 1920x1080 до 1620x1080 (учитывая, что моя панель приложений имеет ширину 300 пикселей), если вы понимаете, о чем я. - person RoXaS; 11.04.2014

Это оказалось ответом. Я не умнее, но это сработало.

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

person RoXaS    schedule 11.04.2014
comment
Точно, я собирался опубликовать это, но вы нашли это раньше :D - person aybe; 11.04.2014
comment
@Aybe Можете ли вы случайно объяснить мне, что происходит в коде, потому что я пытался сделать красивую абстрактную dll, но я не могу понять, как это работает и что происходит. Или, может быть, дайте мне ссылку. Если вы сформулируете это в ответе, я приму это как ответ. Спасибо за вашу помощь. - person RoXaS; 15.04.2014
comment
Отредактируйте свой вопрос и объясните, что вы не понимаете, и я посмотрю. - person aybe; 15.04.2014
comment
Вы не понимаете, что такое дескриптор окна, это все, что вам нужно знать? - person aybe; 15.04.2014
comment
Я дал некоторые пояснения, надеюсь, теперь они вам понятны :D Что касается вашей библиотеки, если вы планируете ее опубликовать, я настоятельно рекомендую вам создать пакет Nuget; людям будет проще его использовать, и вы также будете знать, сколько людей загрузили его, что, кстати, круто. Вы также должны разместить свой исходный код на GitHub, чтобы люди могли в конечном итоге улучшить его. Вот пример библиотеки, которую я недавно опубликовал: nuget.org/packages/SonicApi и github.com/aybe/SonicApi. - person aybe; 15.04.2014