子类化控件的ParentForm WndProc

本文关键字:ParentForm WndProc 控件 子类 | 更新日期: 2023-09-27 18:17:54

我正在将一个定制的MenuStrip控件集成到另一个。net应用程序中。菜单条工作良好,除了焦点问题。如果承载MenuStrip的表单没有聚焦,则用户需要单击两次——一次激活表单,另一次选择菜单项。主机表单是独立的。net应用程序的一部分,我不能修改它。我只能修改控件的源代码。

我在WinForm级别找到了一个解决方案,它覆盖了Form WndProc方法,除非我没有访问WinForm的源代码,也不能重新编译主机应用程序和表单。

是否有一种方法来子类化控件的ParentForm,以便它自动激活ParentForm,如果它没有聚焦?

子类化控件的ParentForm WndProc

我可以使用ContainerControl。ParentForm以获取对主机表单的引用,但是在生产环境中,ParentForm返回null,我仍然需要找到解决方案。我把NewWndProc处理程序在try/catch只是以防它抛出任何异常,虽然我不确定什么(如果有的话)它可以抛出。我可能不得不使用Win32函数而不使用表单。focus和Form.Activate() . net方法。总之,下面是代码:

public class FocusFormWrapper
{
    #region DllImport
    public const int GWL_WNDPROC = (-4);
    public const UInt32 WM_CLOSE = 0x0010;
    public const UInt32 WM_PARENTNOTIFY = 0x0210; 
    public delegate IntPtr WndProcDelegate(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam);
    [DllImport("user32.dll")]
    [return: MarshalAs(UnmanagedType.Bool)]
    public static extern bool IsWindow(IntPtr hWnd);
    [DllImport("user32.dll", SetLastError = true)]
    public static extern IntPtr GetWindowLong(IntPtr hWnd, int nIndex);
    [DllImport("user32.dll", SetLastError = true)]
    public static extern int SetWindowLong(IntPtr hWnd, int nIndex, IntPtr newWndProc);
    [DllImport("user32.dll", SetLastError = true)]
    public static extern IntPtr CallWindowProc(IntPtr lpPrevWndFunc, IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);
    [DllImport("user32.dll", SetLastError = true)]
    private static extern IntPtr FindWindow(string _ClassName, string _WindowName);
    [DllImport("user32.dll", SetLastError = true)]
    public static extern int SendMessage(IntPtr hWnd, uint Msg, int wParam, int lParam);
    [DllImport("user32.dll", SetLastError = true)]
    private static extern bool SetForegroundWindow(IntPtr hWnd);
    #endregion DllImport
    private Form myForm = null;
    private IntPtr hWndTarget = IntPtr.Zero;
    private IntPtr oldWndProc = IntPtr.Zero;
    private WndProcDelegate newWndProc;
    private FocusFormWrapper()
    {
    }
    public FocusFormWrapper(Form sourceForm)
    {
        if (sourceForm == null)
            throw new ArgumentNullException("sourceForm");
        if (!IsWindow(sourceForm.Handle))
            throw new ArgumentException("sourceForm IsWindow failed");
        myForm = sourceForm;
        hWndTarget = myForm.Handle;
        AddSubclass();
    }
    ~FocusFormWrapper()
    {
        RemoveSubclass();
        myForm = null;
        newWndProc = null;
    }
    private int AddSubclass()
    {
        int result = -1;
        if (myForm != null && newWndProc == null)
        {
            newWndProc = new WndProcDelegate(NewWndProc);
            oldWndProc = GetWindowLong(hWndTarget, GWL_WNDPROC);
            result = SetWindowLong(hWndTarget, GWL_WNDPROC, Marshal.GetFunctionPointerForDelegate(newWndProc));
        }
        return result;
    }
    public int RemoveSubclass()
    {
        int result = -1;
        if (myForm != null && newWndProc != null)
        {
            result = SetWindowLong(hWndTarget, GWL_WNDPROC, oldWndProc);
            newWndProc = null;
        }
        return result;
    }
    public IntPtr NewWndProc(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam)
    {
        try
        {
            if (msg == WM_PARENTNOTIFY && !myForm.Focused)
            {
                // Make this form auto-grab the focus when menu/controls are clicked 
                myForm.Activate();
            }
            if (msg == WM_CLOSE)
            {
                RemoveSubclass();
            }
        }
        catch
        {
        }
        return CallWindowProc(oldWndProc, hWnd, msg, wParam, lParam);
    }
}