Michael
Michael

Reputation: 436

How to hide a WPF Window icon and use ResizeMode=NoResize?

I recently implemented the solutions from this question to hide a WPF Window's icon. I found that when that solution is used in conjunction with ResizeMode=NoResize, the app's title bar context menu fails to disable Min/Max/Resize options.

The odd thing is the context menu doesn't incorrectly enable certain options, it just leaves the state of the context menu from before the icon was hidden. I found this using a simple test app that can make the necessary calls to hide the icon, and can update the Window's ResizeMode on the fly.

Icon Shown, ResizeMode=CanResize

Title bar buttons and context menu are correct.

enter image description here

Icon Hidden, ResizeMode=CanResize

Still correct

enter image description here

Icon Hidden, ResizeMode=NoResize

Title bar buttons are correctly hidden, but the context menu retains it's previous state. If I switch to CanMinimize then to NoResize, the context menu would only have "Minimize" enabled.

enter image description here

This becomes a problem when a resize-able WPF Window launches another Window which is set to NoResize (and you are hiding the icon).

Question

Are there additional Windows API functions that can force the Context Menu to reevaluate its state? What about the NoResize option might be causing this weird behavior? As this only affects the NoResize option, could there be a workaround at the WPF level?

EDIT - My goal is to avoid using WindowStyle=ToolWindow or the WS_EX_TOOLWINDOW extended window style. I've found a few problems with that window style in the past, one is desribed on this question. One of my goals with this whole approach was to emulate the look of ToolWindow without actually having to use it.

Upvotes: 2

Views: 2187

Answers (2)

Irbis377
Irbis377

Reputation: 1

I know it's an old question but I've also met this problem sometime ago. So if you don't want to use WS_EX_TOOLWINDOW, this can be a good solution for you. You should just gray context menu's items after hiding icon.

    [DllImport("user32.dll")]
    private static extern UInt32 GetWindowLong(IntPtr hwnd, Int32 index);

    [DllImport("user32.dll")]
    private static extern UInt32 SetWindowLong(IntPtr hwnd, Int32 index, UInt32 newStyle);

    [DllImport("user32.dll")]
    private static extern Boolean SetWindowPos(IntPtr hwnd, IntPtr hwndInsertAfter, Int32 x, Int32 y, Int32 width, Int32 height, UInt32 flags);

    [DllImport("user32.dll")]
    private static extern IntPtr SendMessage(IntPtr hwnd, UInt32 msg, IntPtr wParam, IntPtr lParam);

    private const Int32 GWL_EXSTYLE = -20;
    private const Int32 WS_EX_DLGMODALFRAME = 0x0001;
    
    private const Int32 SWP_NOSIZE = 0x0001;
    private const Int32 SWP_NOMOVE = 0x0002;
    private const Int32 SWP_NOZORDER = 0x0004;
    private const Int32 SWP_FRAMECHANGED = 0x0020;
    
    private const UInt32 WM_SETICON = 0x0080;
    private const Int32 ICON_SMALL = 0;
    private const Int32 ICON_BIG = 1;
    
    [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    private static extern IntPtr GetSystemMenu(IntPtr hWnd, Boolean bRevert);
    
    [DllImport("user32.dll", EntryPoint = "EnableMenuItem")] 
    private static extern IntPtr EnableMenuItem(IntPtr hMenu, UInt32 uIDEnableItem, UInt32 uEnable);
    
    private const Int32 SC_MAXIMIZE = 0xF030;
    private const Int32 SC_MINIMIZE = 0xF020;
    private const Int32 SC_SIZE = 0xF000;
    private const Int32 SC_RESTORE = 0xF120;
    
    private const UInt32 MF_BYCOMMAND = 0x00000000;
    private const UInt32 MF_DISABLED = 0x00000002;
    private const UInt32 MF_GRAYED = 0x00000001;

private void RemoveUpperLeftCornerIcon()
{
    IntPtr hwnd = new WindowInteropHelper(this).EnsureHandle();
    UInt32 extendedStyle = GetWindowLong(hwnd, GWL_EXSTYLE); 
   
    //set a new style to be able to hide the icon  
    SetWindowLong(hwnd, GWL_EXSTYLE, extendedStyle | WS_EX_DLGMODALFRAME);
    
    //hide icon
    SendMessage(hwnd, WM_SETICON, (IntPtr)ICON_SMALL, IntPtr.Zero);
    SendMessage(hwnd, WM_SETICON, (IntPtr)ICON_BIG, IntPtr.Zero);
    
    //inform about changes
    SetWindowPos(hwnd, IntPtr.Zero, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED);

    if (this.ResizeMode == ResizeMode.NoResize)
    {
        //gray context menu's items related to size (because WS_EX_DLGMODALFRAME breaks it)
        IntPtr hSystemMenu = GetSystemMenu(hwnd, false);
        EnableMenuItem(hSystemMenu, SC_MAXIMIZE, MF_GRAYED | MF_BYCOMMAND);
        EnableMenuItem(hSystemMenu, SC_MINIMIZE, MF_GRAYED | MF_BYCOMMAND);
        EnableMenuItem(hSystemMenu, SC_SIZE, MF_GRAYED | MF_BYCOMMAND);
        EnableMenuItem(hSystemMenu, SC_RESTORE, MF_GRAYED | MF_BYCOMMAND);
    }
}

protected override void OnSourceInitialized(EventArgs e)
{   
    base.OnSourceInitialized(e);
    RemoveUpperLeftCornerIcon();
}

Upvotes: 0

Il Vic
Il Vic

Reputation: 5666

Using .NET 4.0 and Windows 8.1 Enterprise (I do not know if it works with other .NET versions or a different OS), you just need to use the WS_EX_TOOLWINDOW extended style instead of the WS_EX_DLGMODALFRAME one.

So the code will be:

public partial class MainWindow : Window
{
    [DllImport("user32.dll")]
    static extern int GetWindowLong(IntPtr hwnd, int index);

    [DllImport("user32.dll")]
    static extern int SetWindowLong(IntPtr hwnd, int index, int newStyle);

    [DllImport("user32.dll")]
    static extern bool SetWindowPos(IntPtr hwnd, IntPtr hwndInsertAfter, int x, int y, int width, int height, uint flags);

    [DllImport("user32.dll")]
    static extern IntPtr SendMessage(IntPtr hwnd, uint msg, IntPtr wParam, IntPtr lParam);

    private const int GWL_EXSTYLE = -20;
    private const int WS_EX_DLGMODALFRAME = 0x0001;
    private const int SWP_NOSIZE = 0x0001;
    private const int SWP_NOMOVE = 0x0002;
    private const int SWP_NOZORDER = 0x0004;
    private const int SWP_FRAMECHANGED = 0x0020;
    private const int WM_SETICON = 0x0080;
    private const int WS_EX_TOOLWINDOW = 0x00000080;

    public MainWindow()
    {
        InitializeComponent();
        ResizeMode = System.Windows.ResizeMode.NoResize;
    }

    protected override void OnSourceInitialized(EventArgs e)
    {
        base.OnSourceInitialized(e);

        IntPtr hwnd = new WindowInteropHelper(this).Handle;

        int extendedStyle = GetWindowLong(hwnd, GWL_EXSTYLE);
        SetWindowLong(hwnd, GWL_EXSTYLE, extendedStyle | WS_EX_TOOLWINDOW);

        SendMessage(hwnd, WM_SETICON, new IntPtr(1), IntPtr.Zero);
        SendMessage(hwnd, WM_SETICON, IntPtr.Zero, IntPtr.Zero);

        SetWindowPos(hwnd, IntPtr.Zero, 0, 0, 0, 0, SWP_NOMOVE | 
            SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED);
    }
}

Let's hope it can help you.

Upvotes: 1

Related Questions