Pages

Tuesday, September 7, 2010

Taskbar with Window Maximized and WindowState to None in WPF

Say you want your WPF  application to have no Title bar, and will maximized to Full Screen, what you first think of. Its the most easiest to do.

  1. Set WindowStyle to Maximized : By this the Window will not show up any title bar. The window style will be transformed to a Box with border in it. 
  2. Set WindowState to None :  By this the window will be maximized to the whole screen and will show only the window and nothing else..

Oh.. Hold on ..Hold on... As I say, it hides the whole screen, it means it even will not show up the Taskbar or any external gadgets applied to your desktop if it is not set to Always on Top attribute to it.

Yes, this is the problem that I faced recently, when people wanted me to show the TaskBar even though the application should be Maximized.

Is it a Bug ? 

If I am not wrong, WPF builds application based on the Screen Resolution. It produces DPI independent pixels. If you specify it to be full screen, it first gets the Resolution of the screen, and draws the pixel based on its own algorithm. Hence, when you specify it to Maximized, it takes up the whole screen, as otherwise some portion of the screen will be hidden outside the range of the boundary.

When you restore a WPF screen, it will also recalculate the work area based on the distance between the resolution boundaries and resize itself accordingly. No do you think Microsoft should really have an alternative state which show up the Taskbar as it does with normal windows ? I think yes.

The Solution

As I needed to do this, I have just tried out few workarounds to this myself.

Following Lester's Blog on this issue, I have to use

private static System.IntPtr WindowProc(
        System.IntPtr hwnd,
        int msg,
        System.IntPtr wParam,
        System.IntPtr lParam,
        ref bool handled)
{
    switch (msg)
    {
        case 0x0024:
            WmGetMinMaxInfo(hwnd, lParam);
            handled = true;
            break;
    }

    return (System.IntPtr)0;
}

private static void WmGetMinMaxInfo(System.IntPtr hwnd, System.IntPtr lParam)
{
            
    MINMAXINFO mmi = (MINMAXINFO)Marshal.PtrToStructure(lParam, typeof(MINMAXINFO));

    // Adjust the maximized size and position to fit the work area of the correct monitor
    int MONITOR_DEFAULTTONEAREST =0x00000002;
    System.IntPtr monitor = MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST);

    if (monitor != System.IntPtr.Zero)
    {
                
        MONITORINFO monitorInfo = new MONITORINFO();
        GetMonitorInfo(monitor, monitorInfo);
        RECT rcWorkArea = monitorInfo.rcWork;
        RECT rcMonitorArea = monitorInfo.rcMonitor;
        mmi.ptMaxPosition.x = Math.Abs(rcWorkArea.left - rcMonitorArea.left);
        mmi.ptMaxPosition.y = Math.Abs(rcWorkArea.top - rcMonitorArea.top);
        mmi.ptMaxSize.x = Math.Abs(rcWorkArea.right - rcWorkArea.left);
        mmi.ptMaxSize.y = Math.Abs(rcWorkArea.bottom - rcWorkArea.top);
    }

    Marshal.StructureToPtr(mmi, lParam, true);
}

The call to API WmGetMinMaxInfo gets you the size of Maximize window for the current desktop.

[DllImport("user32")]
internal static extern bool GetMonitorInfo(IntPtr hMonitor, MONITORINFO   lpmi);
[DllImport("User32")]
internal static extern IntPtr MonitorFromWindow(IntPtr handle, int flags);

The call to GetMonitorInfo gets you a MONITORINFO object and if you see the code carefully it actually position the window in such a way that it absolutely resize itself to the height and width of the rectangular area.

To call this method, I can use SourceInitialized event which will eventually be called whenever the WindowState is modified.

void win_SourceInitialized(object sender, EventArgs e)
{
    System.IntPtr handle = (new WinInterop.WindowInteropHelper(this)).Handle;
    WinInterop.HwndSource.FromHwnd(handle).AddHook(new WinInterop.HwndSourceHook(WindowProc));
}

Sounds good ?
Oh.. Lets give another easier solution that I have used. Its very simple.

Set properties for your window :

WindowStyle="None"
WindowState="Maximized"
ResizeMode="NoResize"

And go to your code and just resize your window based on PrimaryScreen width and height. Also make sure you do set the Left and Top of the window as well.

this.Width = System.Windows.SystemParameters.WorkArea.Width;
this.Height = System.Windows.SystemParameters.WorkArea.Height;
this.Left = 0;
this.Top = 0;
this.WindowState = WindowState.Normal;

Making WindowState.Normal will ensure that the default behaviour of the window is overridden and also makes the Taskbar Reappear.

I have included my sample application :

Have fun.

Download Sample - 38KB

7 comments:

  1. Hi. Thanks for your post. it helped, but only in when the task bar is set to stay on top. when it is on "autohide" it doesn't work. you think that can be also solved? I also had a bug with it on two monitors. I didn't check it now, but I will and if you are interested in the problem I will let you know. until then, I am waiting for an answer about the autohide. thanks a bunch. Gabriel

    ReplyDelete
  2. Same problem. Auto-hide taskbar doesn't work.

    ReplyDelete
  3. Hello Abhishek,

    I like your second solution, but involving Windows Forms Librairies inside a WPF project doesn't sound good to me.

    You can find the working area size in WPF as well using this command :
    System.Windows.SystemParameters.WorkArea

    Good post though!

    Antoine

    ReplyDelete
  4. @Anonymous

    Hey, Thank you friend. I actually overlooked it totally. Yes, I did somehow got referred to Forms dll and you rightly pointed me out.

    I will definitely change the implementation right now.

    Cheers Antoine.
    Thanks

    ReplyDelete
  5. what about
    this.MaxHeight = System.Windows.SystemParameters.PrimaryScreenHeight
    Anirudh

    ReplyDelete
  6. Or use the WPF Shell Integration Library:

    http://code.msdn.microsoft.com/WPFShell

    ReplyDelete

Please make sure that the question you ask is somehow related to the post you choose. Otherwise you post your general question in Forum section.