如何防止表单窃取焦点同时仍能接收鼠标点击?

6
我正试图在WinForms中创建一个自定义的AutoCompleteTextBox控件。基本TextBox提供的自动完成仅使用(string).StartsWith而不是(string).Contains提供结果。
我将自动完成搜索结果显示在一个ListBox中,该ListBox停靠在单独的Form中。我可以通过以下方式防止Form最初窃取焦点:
protected override bool ShowWithoutActivation
{
   get { return true; }
}

我可以通过覆盖WndProc方法,完全防止 Form 获得焦点:

protected override void WndProc( ref Message m )
{
    base.WndProc( ref m );
    switch ( m.Msg )
    {
        case WM_MOUSEACTIVATE:
            m.Result = ( IntPtr ) MA_NOACTIVATEANDEAT;
            break;

        default:
            break;
    }

当我这样做时,Form 中包含的 ListBox 将接收到 MouseMoved 事件,但不会接收到 MouseClicked 事件。
MA_NOACTIVATEANDEAT 更改为只有 MA_NOACTIVATE 将把鼠标事件传递给 ListBox,但是这会导致在 ListBox 上的点击窃取了焦点从而转移到 'floating' Form 中的 ListBox
是否有任何方法可以防止 'floating' Form 从 'main' Form 中窃取焦点,同时仍然能够在 ListBox 中获取 MouseClick 事件?

我认为当在列表框中单击后,您将被迫把焦点放回主窗体...或者您可以使用鼠标移动事件来确定鼠标悬停的是哪个列表框项。HitTest(如我所知)将提供一个ListBoxItem。 - IAbstract
@lAbstract 回到主窗体的问题在于,下次想要显示浮动窗体时,主窗体会出现在浮动窗体前面。然后在浮动窗体上调用 BringToFront 会使其获得焦点 - 再次从主窗体中夺取焦点。 - Anthony
啊,明白了:所以,如果您能够监视主窗体的鼠标移动,那么您能否在主窗体上获取HitTestInfo?如果您能够做到这一点,那么您实际上可以从浮动窗体接收所选项目...我想。;) - IAbstract
这假设主窗体负责这些事情。这是一个自定义控件类,将在多个其他窗体中使用 - 因此从重用的角度来看有点不可行。 - Anthony
可能是重复的问题:如何创建一个悬停的C# Winforms控件 - Hans Passant
3个回答

0
将您的浮动表单转换为以下的Usercontrol: 在其上拉出一个ListBox并挂钩到Click事件以模拟您的焦点场景。
using System.Text;
using System.Windows.Forms;
using System.Runtime.InteropServices;

namespace WindowsFormsApplication3
{
    public partial class InactiveForm : UserControl
    {
        private const int WS_EX_TOOLWINDOW = 0x00000080;
        private const int WS_EX_NOACTIVATE = 0x08000000;
        private const int WS_EX_TOPMOST = 0x00000008;


        [DllImport("user32")]
        public static extern int SetParent
         (IntPtr hWndChild, IntPtr hWndNewParent);

        [DllImport("user32")]
        public static extern int ShowWindow
         (IntPtr hWnd, int nCmdShow);


        protected override CreateParams CreateParams
        {
            get
            {

                CreateParams p = base.CreateParams;
                p.ExStyle |= (WS_EX_NOACTIVATE | WS_EX_TOOLWINDOW | WS_EX_TOPMOST);
                p.Parent = IntPtr.Zero;
                return p;
            }
        }

        public InactiveForm()
        {
            InitializeComponent();
        }

        public new void Show()
        {
            if (this.Handle == IntPtr.Zero) base.CreateControl();

            SetParent(base.Handle, IntPtr.Zero);
            ShowWindow(base.Handle, 1);
        }


        private void OnListBoxClicked(object sender, EventArgs e)
        {
            MessageBox.Show("Clicked List Box on floating control");

        }
    }
}

在主窗体中的代码(带有按钮及其单击处理程序):

这将调用带有ListBox的浮动控件。

namespace WindowsFormsApplication3
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            InactiveForm f = new InactiveForm();
             f.Show();
        }
    }
}

当您将UserControl(无边框窗体)显示为浮动窗体时,您会注意到即使您单击(选择)其任何子元素,它也不会接收焦点。在这种情况下,子元素是用户控件上的ListBox。

请参考此处此处


0

我创建了类似的东西,我的解决方案与lAbstract建议的类似。浮动表单具有一个事件DoReturnFocus,自定义文本框(在本例中为AutoCompleteTextBox控件)订阅该事件,并简单地将焦点设置回自身。为了防止主表单出现在浮动表单前面,我将主表单设置为浮动表单的Owner,因为拥有的表单永远不会显示在其所有者表单后面。

更多信息请参见Form.Owner Property


0
除了Angshuman Agarwal之前的回答: 如果您希望主窗体在用户控件显示为浮动窗口时不被停用,可以修改一些代码到您的用户控件中:
private Control _mControl;

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

public new void Show(Control c)
{
    if (c == null) throw new ArgumentNullException();
    _mControl = c;
    if (this.Handle == IntPtr.Zero) base.CreateControl();
    SetParent(base.Handle, IntPtr.Zero);
    ShowWindow(base.Handle, 1);
}

protected override void WndProc(ref Message m)
{
    if (m.Msg == 0x86)  //WM_NCACTIVATE
    {
        if (m.WParam != IntPtr.Zero) //activate
        {
            SendMessage(_mControl.Handle, 0x86, (IntPtr)1, IntPtr.Zero);
        }
        this.DefWndProc(ref m);
        return;
    }
    base.WndProc(ref m);
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接