如何在VB.Net中重新定位滚轮?

时间:2010-04-01 11:29:39

标签: .net vb.net winforms scroll

我在VB.Net中有一个表单,启用了Autoscroll,其中有几个ComboBox。当我单击ComboBox时,我可以使用鼠标滚轮滚动选项,但我无法取消选择ComboBox(通过在父窗体的空白部分单击它)以返回滚动父窗体。这使得导航形式很烦人,因为我本能地单击控件并翻转滚轮,导致ComboBox更改选择而不是移动父窗体。

有没有办法以我期望的直观方式完成这项工作,或者我是否有一些概念上的混淆使得这个问题成为错误的问题?

2 个答案:

答案 0 :(得分:2)

Windows将WM_MOUSESCROLL消息发送到具有焦点的控件。如果控件不使用它,则它将被发送到控件的父级。最终,这将是您的表单,然后使用它来滚动自己。

然而,ComboBox非常乐意使用消息本身。显示下拉列表时,它会滚动下拉列表。它还捕获鼠标,以便它可以看到您在列表外单击。这将关闭列表并将重点放在ComboBox上。然后使用该消息通过文本框部分滚动项目。换句话说,当CB具有焦点时,你永远不会得到滚动形式。

您可以使用IMessageFilter来解决这个问题。我在this thread中的代码显示了一个示例。你必须去医生,代码是为了做别的事。例如,您可以使用窗体的Handle属性值替换m.HWnd字段,以便所有鼠标滚动消息都转到窗体。或者您可以按原样使用代码,它可能更直观。

小心这一点,您的用户可能会被这种非标准行为致命地困惑。也许她真的想要滚动组合。

以下是使其有效的代码。将它放在应用程序的主窗体中,它对您的应用显示的所有其他窗体都有效:

public partial class Form1 : Form, IMessageFilter {
    public Form1() {
        InitializeComponent();
        Application.AddMessageFilter(this);
        this.FormClosed += (o, e) => Application.RemoveMessageFilter(this);
    }

    public bool PreFilterMessage(ref Message m) {
        if (m.Msg == 0x20a) {  // Trap WM_MOUSEWHEEL
            var ctl = Control.FromHandle(m.HWnd);
            if (ctl == null || !(ctl is ComboBox)) ctl = Control.FromHandle(GetParent(m.HWnd));
            // Redirect when message destined for text box of a ComboBox
            if (ctl != null && ctl is ComboBox && !(ctl as ComboBox).DroppedDown) {
                PostMessage(ctl.Parent.Handle, m.Msg, m.WParam, m.LParam);
                return true;
            }
        }
        return false;
    }

    [System.Runtime.InteropServices.DllImport("user32.dll")]
    private static extern IntPtr PostMessage(IntPtr hWnd, int msg, IntPtr wp, IntPtr lp);
    [System.Runtime.InteropServices.DllImport("user32.dll")]
    private static extern IntPtr GetParent(IntPtr hWnd);
}

答案 1 :(得分:0)

一种解决方案是捕获selectedIndexChanged事件,并将焦点切换到表单(我相信它是form1.Focus())。

当用户选择一个值时,通过将焦点切换回窗体并因此滚动操作也可以解决问题。但是,如果用户单击组合框然后决定不更改该值,则不会取消选择它。

您可以将相同的Focus代码放入您的表单Click事件中,但如果他们单击控件(如标签),它将无效。