簡體   English   中英

為什么最大化/最小化事件導致關閉按鈕在禁用后重新啟用?

[英]Why are Maximize/Minimize events causing the Close Button to be re-enabled after disabling it?

我使用P / Invoke調用GetSystemMenu和EnableMenuItem(win32api)來禁用關閉功能。 但是,在最小化或最大化我的Windows窗體應用程序后,將重新啟用該按鈕。

顯然,最小化或最大化會導致行為,但如何? 我不知道在哪里可以防止這種行為。

我應該阻止最大化和最小化行為,還是IP /調用呼叫的方式有什么特別的錯誤? 加載應用程序(主窗體)后,我會通過單擊按鈕調用靜態方法。

class PInvoke
{
    // P/Invoke signatures
    [DllImport("user32.dll")]
    static extern IntPtr GetSystemMenu(IntPtr hWnd, bool bRevert);
    [DllImport("user32.dll")]
    static extern bool EnableMenuItem(IntPtr hMenu, uint uIDEnableItem, uint uEnable);

    // SysCommand (WM_SYSCOMMAND) constant
    internal const UInt32 SC_CLOSE = 0xF060;

    // Constants used with Add/Check/EnableMenuItem
    internal const UInt32 MF_BYCOMMAND = 0x00000000;
    internal const UInt32 MF_ENABLED = 0x00000000;
    internal const UInt32 MF_GRAYED = 0x00000001;
    internal const UInt32 MF_DISABLED = 0x00000002;

    /// <summary>
    /// Sets the state of the Close (X) button and the System Menu close functionality.
    /// </summary>
    /// <param name="window">Window or Form</param>
    /// <param name="bEnabled">Enabled state</param>
    public static void EnableCloseButton(IWin32Window window, bool bEnabled)
    {
        IntPtr hSystemMenu = GetSystemMenu(window.Handle, false);

        EnableMenuItem(hSystemMenu, SC_CLOSE, MF_BYCOMMAND | (bEnabled ? MF_ENABLED : MF_GRAYED));
    }
}

每個窗口都有一個窗口類,它定義該類的所有窗口的樣式。 您可以使用CS_NOCLOSE類樣式刪除該類窗口的關閉按鈕。 有關如何設置此類標志的詳細信息,請參見此處此處

如果這不能滿足您的需求,我不會為了可用性而禁用最小化/最大化,但您可以監聽最小化/最大化事件並重新運行代碼以禁用關閉按鈕。 最后,可以處理close事件,而不是關閉。 然后你知道你的窗口肯定不會被關閉,即使關閉按鈕無意中被啟用了。

接受的答案確實提出了問題的可能解決方法(以及我多次使用過的問題),但它根本沒有回答最初提出的問題:

在使用GetSystemMenuEnableMenuItem API函數禁用關閉按鈕之后,最大化或最小化窗體的原因是什么/為什么導致關閉按鈕重新啟用?

在發現這種看似無法解釋的行為之后,我在完全沒有結果的Google搜索過程中得出了這個問題。 找不到實際解釋行為的答案,
我被迫采取了一些自己的挖掘方式。

作為參考,請注意原始問題中顯示的完全相同的代碼在本機Win32應用程序中正常工作。 重新啟用“關閉”菜單項似乎僅限於WinForms應用程序。

研究System.Windows.Forms.Form類的源代碼揭示了一個有趣的實現細節: .NET Framework設計者顯然決定每次窗體的WindowState更改時調整窗體的系統菜單 ,其中包括最大化和最小化由系統。

具體來說,名稱為AdjustSystemMenu方法有兩種,它們負責響應這些事件而改變系統菜單(並弄亂你自己可能做過的任何自定義)。 如果您有興趣檢查代碼(我已經放棄了這里為了Mono等項目的參與者而放棄了這些代碼),請獲取.NET Reflector的免費副本。

我不完全確定為什么做出這個決定,但至少我現在已經解釋了。

我有同樣的要求。 嘗試了幾種方法來禁用關閉菜單選項然后刪除並嘗試重新創建它(正確)后,我發現了來自Microsoft http://support.microsoft.com/kb/184686的這個黑客攻擊。

奇跡般有效。 它仍然是一個黑客,但它的工作原理。

這是我原來的(松散)C#轉換

        [System.Runtime.InteropServices.DllImport("user32.dll")]
    static extern IntPtr GetSystemMenu(IntPtr hWnd, bool bRevert);

    [System.Runtime.InteropServices.DllImport("user32.dll")]
    static extern int GetMenuItemCount(IntPtr hMenu);

    [System.Runtime.InteropServices.DllImport("user32.dll")]
    static extern bool DrawMenuBar(IntPtr hWnd);


    public static void EnableCloseButton(Form frm, bool enabled) {
        IntPtr hMenu;
        int n;
        hMenu = GetSystemMenu(frm.Handle, false);
        if (hMenu != IntPtr.Zero) {
            n = GetMenuItemCount(hMenu);
            if (n > 0) {
                if (enabled) {
                    EnableClose(frm);
                }
                else {
                    DisableClose(frm);
                }
                SendMessage(frm.Handle, WM_NCACTIVATE, (IntPtr)1, (IntPtr)0);
                DrawMenuBar(frm.Handle);
                Application.DoEvents();
            }
        }
    }

    [StructLayout(LayoutKind.Sequential)]
    public struct MENUITEMINFO {
        public uint cbSize;
        public uint fMask;
        public uint fType;
        public uint fState;
        public int wID;
        public int hSubMenu;
        public int hbmpChecked;
        public int hbmpUnchecked;
        public int dwItemData;
        public string dwTypeData;
        public uint cch;
        //    public int hbmpItem;
    }

    internal const UInt32 SC_CLOSE = 0xF060;

    //SetMenuItemInfo fMask constants.
    const UInt32 MIIM_STATE = 0x1;
    const UInt32 MIIM_ID = 0x2;

    //'SetMenuItemInfo fState constants.
    const UInt32 MFS_ENABLED = 0x0;
    const UInt32 MFS_GRAYED = 0x3;
    const UInt32 MFS_CHECKED = 0x8;

    internal const int MFS_DEFAULT = 0x1000;

    [DllImport("user32.dll")]
    static extern bool SetMenuItemInfo(IntPtr hMenu, int uItem, bool fByPosition, [In] ref MENUITEMINFO lpmii);

    [DllImport("user32.dll")]
    static extern bool GetMenuItemInfo(IntPtr hMenu, int uItem, bool fByPosition, ref MENUITEMINFO lpmii);

    [DllImport("user32.dll", CharSet = CharSet.Auto)]
    static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg, IntPtr wParam, IntPtr lParam);

    private const UInt32 WM_NCACTIVATE = 0x0086;

    private static void DisableClose(Form frm) {
        IntPtr hMenu;
        int n;
        hMenu = GetSystemMenu(frm.Handle, false);
        if (hMenu != IntPtr.Zero) {
            MENUITEMINFO mif = new MENUITEMINFO();
            mif.cbSize = (uint)Marshal.SizeOf(typeof(MENUITEMINFO));
            mif.fMask = MIIM_ID | MIIM_STATE;
            mif.fType = 0;
            mif.dwTypeData = null;
            bool a = GetMenuItemInfo(hMenu, (int)SC_CLOSE, false, ref mif);
            mif.fState = MFS_GRAYED;
            SetMenuItemInfo(hMenu, (int)SC_CLOSE, false, ref mif);
            SendMessage(frm.Handle, WM_NCACTIVATE, (IntPtr)1, (IntPtr)0);

            mif.wID = -10;
            mif.fState = MFS_GRAYED;
            SetMenuItemInfo(hMenu, (int)SC_CLOSE, false, ref mif);
        }
    }

    private static void EnableClose(Form frm) {
        IntPtr hMenu;
        int n;
        hMenu = GetSystemMenu(frm.Handle, false);
        if (hMenu != IntPtr.Zero) {
            MENUITEMINFO mif = new MENUITEMINFO();
            mif.cbSize = (uint)Marshal.SizeOf(typeof(MENUITEMINFO));
            mif.fMask = MIIM_ID | MIIM_STATE;
            mif.fType = 0;
            mif.dwTypeData = null;
            bool a = GetMenuItemInfo(hMenu, -10, false, ref mif);
            mif.wID = (int)SC_CLOSE;
            SetMenuItemInfo(hMenu, -10, false, ref mif);
            SendMessage(frm.Handle, WM_NCACTIVATE, (IntPtr)1, (IntPtr)0);

            mif.fState = MFS_ENABLED;
            SetMenuItemInfo(hMenu, (int)SC_CLOSE, false, ref mif);
            SendMessage(frm.Handle, WM_NCACTIVATE, (IntPtr)1, (IntPtr)0);
        }
    }

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM