Question

I actually want to make a sidebar application that reserves desktop space for itself. The appBar itself is not a problem. However I would like it to be transparent.

Example: Transparent appbar example

In the above image you can see how stardock reserves it's desktop space, but my background image is still completely visible and the application itself is transparent.

When I run my appBar the background becomes black automatically and my background scales with it. And Apparently I cant screenshot it either so i can't give an example of how it looks.

Here is the code for my AppBar: (Note: code is set with 1920px offset so it would dock on my second monitor (havent had time for abstract coding.)

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);
    }
}
}

And here is the code from my main window:

<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.

In reward I will make a ClassLibrary and upload it for everyone to use.

Thanks in advance.

Was it helpful?

Solution

EDIT 1

There are a few questions about what is a window handle :

What is a Windows Handle?

Difference between HANDLE and HWND in Windows API?

Now if we look at 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;

In other terms it is a void pointer, it points to something for which the type is not known, this is deliberate.

Here's some explanation : What is a void pointer in C++?

Conclusion

An HWND or handle to a window points to this class : Windows

In the .NET framework you don't directly have access to this type, you either use a Form or a Window which themselves use that type. Now when you have to do some lower-level stuff like you are doing, you perform them on a Window, therefore you need to specify on what HWND you want to perform this action. Now in .NET you generally represent that as an IntPtr.


EDIT Actually there is a background but for some reasons the WPF application is black.

My suggestion still applies though.


my appBar the background becomes black

That makes senses since you are reserving that space there is nothing behind, therefore it is black.

Possible solution :

  • Grab the current wallpaper
  • Get region of it that is as high as your app bar
  • Make that region your app bar's background
  • Make the remaining region the current Windows background
  • Restore the original background when your app bar gets closed

Other than we can't really tell how Stardock did unless they tell you how.

If there's nothing in MSDN about it and/or that you can't use from C# then it's probably how they did it.

OTHER TIPS

This turned out to be the answer. I am not wiser, but it worked.

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

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top