C# ListBox.MouseEnter/MouseLeave 和滚动条

发布于 2024-11-17 15:09:43 字数 375 浏览 1 评论 0原文

我的表单左侧有一个列表框。用户可以选择“自动隐藏”它,以便它从左侧消失,并且只有当用户将鼠标移到它上面时才重新出现。

如果列表框中的项目很少,则此功能效果很好。

然而,一旦我将足够的项目放入列表框中,现在就有一个滚动条,有趣的事情就开始发生。仅当用户将鼠标移动到滚动条上方的列表框中时,才会触发 MouseEnter 代码。这意味着我必须有比滚动条窥视的宽度更大的宽度,否则他们将永远无法显示它。

此外,如果列表框未获得焦点,用户根本无法滚动。如果他们尝试单击滚动条进行滚动,列表框就会消失。如果他们在列表框内部单击以将其聚焦(以便他们可以使用鼠标滚轮滚动),那么他们将失去选择。

有什么方法可以扩展 MouseEnter 和 MouseLeave 的范围以包含滚动条吗?

I have a ListBox on the left side of my form. The user has the option of "auto-hiding" it so that it disappears off to the left, and only reappears while the user moves their mouse onto it.

If the ListBox has few items in it, this feature works beautifully.

However as soon as I put enough items into the ListBox such that there is now a scrollbar, funny things start to happen. The MouseEnter code is only triggered when the user has moved their mouse into the ListBox past the scrollbar. This means that I must have more then the width of the scroll bar peeking or they will never be able to show it.

Additionally, the user is not able to scroll at all if the ListBox is not in focus. If they try to click the scrollbar to scroll, the ListBox disappears. If they click inside the ListBox to focus it (so that they can scroll with the mouse wheel), then they lose their selection.

Is there any way I can extend the bounds of MouseEnter and MouseLeave to include the scrollbar?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

盗梦空间 2024-11-24 15:09:43

这是设计使然,滚动条不是控件客户区的一部分。它位于非客户区域,类似于表单顶部的标题栏。 Windows 对待它们的方式有所不同,像 WM_MOUSEMOVE 这样的鼠标移动消息是用 WM_NCMOUSEMOVE 来宣布的。 Winforms 没有用于非客户端通知的事件。这是您通常想要的方式,您不想知道滚动条被操纵。功能,而不是错误。除了这种情况。

作为解决方法,您可以从 ListBox 继承并重写 WndProc() 以查看 WM_NCMOUSEMOVE 消息 (m.Msg == 0xa0)。或者一个 200 毫秒的计时器来检查鼠标所在的位置。

This is by design, the scrollbar is not part of the client area of a control. It is in the non-client area, similar to caption bar on top of a form. Windows treats them differently, mouse move messages like WM_MOUSEMOVE get announced with WM_NCMOUSEMOVE instead. Winforms does not have events for non-client notifications. This is the way you'd normally want it, you wouldn't want to know about the scrollbar getting manipulated. Feature, not a bug. Except in this case.

As a workaround, you could inherit from ListBox and override WndProc() to see the WM_NCMOUSEMOVE messages (m.Msg == 0xa0). Or a 200 msec Timer that checks where the mouse is located.

飘过的浮云 2024-11-24 15:09:43

我正在努力解决本质上相同(或非常相似)的问题。最终我想出了以下解决方法,我相信与使用 MouseLeave 相比,它实际上最终在最终用户体验方面提供了卓越的功能。即使 ListBox 在 MouseLeave 事件中没有出现奇怪/意外的行为,我认为这种解决方法无论如何都是一个更好的选择。

我仍然使用MouseEnter来放大ListBox。然而,我发现最好让它保持放大状态,直到用户单击表单上的其他任何位置,而不是使用 MouseLeave 缩小 ListBox 的大小。我在窗体上的其他位置捕获 MouseDown 事件,然后当用户单击窗体上除 ListBox 之外的任何位置时,将 ListBox 缩小回原始大小。

在表单构造函数中,我放置了这个循环:

    foreach (Control control in Controls)
    {
        control.MouseDown += RedirectMouseDown;
    }

然后创建了这个方法:

    private void RedirectMouseDown(object sender, MouseEventArgs e)
    {
        Control control = (Control)sender;
        Point screenPoint = control.PointToScreen(new Point(e.X, e.Y));
        Point formPoint = PointToClient(screenPoint);
        MouseEventArgs args = new MouseEventArgs(e.Button, e.Clicks, formPoint.X, formPoint.Y, e.Delta);            
        OnMouseDown(args);
    }

然后在表单的 MouseDown 事件中:

    if (!listBox1.ClientRectangle.Contains(listBox1.PointToClient(Control.MousePosition)))
    {
        if (listBox1.Size.Width == 500)
        {
            listBox1.Size = new Size(listBox1.Size.Width - 200, listBox1.Size.Height);
        }
    }

I was struggling with essentially the same (or very similar) problem. Eventually I came up with the following workaround, which I believe actually ends up providing superior functionality in terms of the end-user experience as compared to using MouseLeave. Even if the ListBox didn't have the weird/unexpected behavior with the MouseLeave event, I think this workaround turns out to be a better option anyway.

I still use MouseEnter to enlarge the ListBox. However, instead of having the ListBox shrink back in size with MouseLeave, instead I found it better to actually leave it enlarged until the user clicks anywhere else on the form. I capture the MouseDown event everywhere else on the form, and then shrink the ListBox back to original size when the user clicks anywhere on the form except on the ListBox.

In the form contructor I put this loop:

    foreach (Control control in Controls)
    {
        control.MouseDown += RedirectMouseDown;
    }

Then created this method:

    private void RedirectMouseDown(object sender, MouseEventArgs e)
    {
        Control control = (Control)sender;
        Point screenPoint = control.PointToScreen(new Point(e.X, e.Y));
        Point formPoint = PointToClient(screenPoint);
        MouseEventArgs args = new MouseEventArgs(e.Button, e.Clicks, formPoint.X, formPoint.Y, e.Delta);            
        OnMouseDown(args);
    }

Then inside the form's MouseDown event:

    if (!listBox1.ClientRectangle.Contains(listBox1.PointToClient(Control.MousePosition)))
    {
        if (listBox1.Size.Width == 500)
        {
            listBox1.Size = new Size(listBox1.Size.Width - 200, listBox1.Size.Height);
        }
    }
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文