0

I have a user control with a scrollbar (scrollbar appears as a contained user control, which inherits from Panel, is too large). When using the mouse to scroll all is well, but trying to scroll with the mousewheel dont work.

My solution here is to set focus to my child-control in an eventhandler for Scroll. This works. Now the question; Will this result in a lot of unecessary calls to childControl.Focus()? Is there a more neat way of doing this?

Edit: I think I was a bit unclear with my question so Rephrasing the question:

is

private void ChildControl_OnScroll(object sender, ScrollEventArgs scrollEventArgs) 
{
    this.childControl.Focus();
}

a bad way of setting the focus? I.e. will the focus be set mutliple times each time I scroll? or rather, will this cause (tiny) performance issues.

Chris Farmer
  • 24,974
  • 34
  • 121
  • 164
Mystogan
  • 547
  • 4
  • 21

3 Answers3

1

The MouseWheel event is an event that "bubbles". Windows sends it to the control that has the focus, regardless of where the mouse cursor is located. The most typical problem is that you have a control that cannot receive the focus. A Panel for example.

This changes when you put a control on the panel. Now that control can get the focus and gets the MouseWheel message. It won't have any use for it so the message passes to its parent. Which does have a use for it, the panel scrolls as expected.

You can get a focusable panel control from this answer. A generic "make it work like a browser or Office program" solution from this question

Community
  • 1
  • 1
Hans Passant
  • 922,412
  • 146
  • 1,693
  • 2,536
  • Thanks for the explaination! However, I am actually able to set Focus, my question is rather if there is any drawbacks or design issues with setting focus when scrolling. – Mystogan May 07 '13 at 16:04
  • I don't see the connection. Your program forcing the focus is certainly a bad idea, it should always be left to the user. – Hans Passant May 07 '13 at 16:06
1

Here's another approach that gives focus when the scrollbar area of panel1 inside SomeUserControl is clicked. It uses NativeWindow so you don't have to change the panel in your UserControl. This way Focus() will only be called once, when the mouse goes down in the scrollbar area:

public partial class SomeUserControl : UserControl
{
    private TrapMouseDownOnScrollArea trapScroll = null;

    public SomeUserControl()
    {
        InitializeComponent();
        this.VisibleChanged += new EventHandler(SomeUserControl_VisibleChanged);
    }

    void SomeUserControl_VisibleChanged(object sender, EventArgs e)
    {
        if (this.Visible && trapScroll == null)
        {
            trapScroll = new TrapMouseDownOnScrollArea(this.panel1);
        }
    }

    private class TrapMouseDownOnScrollArea : NativeWindow
    {
        private Control control = null;
        private const int WM_NCLBUTTONDOWN = 0xA1;

        public TrapMouseDownOnScrollArea(Control ctl)
        {
            if (ctl != null && ctl.IsHandleCreated)
            {
                this.control = ctl;
                this.AssignHandle(ctl.Handle);
            }
        }

        protected override void WndProc(ref Message m)
        {
            switch (m.Msg)
            {
                case WM_NCLBUTTONDOWN:
                    if (this.control != null)
                    {
                        Rectangle screenBounds = control.RectangleToScreen(new Rectangle(0, 0, control.Width, control.Height));
                        if (screenBounds.Contains(Cursor.Position))
                        {
                            control.Focus();
                        }
                    }

                    break;
            }
            base.WndProc(ref m);
        }

    }

}

This might be overkill for your scenario, but it demonstrates one way to trap lower level messages. As said before, you could also derive from Panel to achieve the same affect. You could also trap messages at the application level with IMessageFilter.

Idle_Mind
  • 38,363
  • 3
  • 29
  • 40
0

If childControl has a MouseEnter() event then use that instead:

    private void childControl_MouseEnter(object sender, EventArgs e)
    {
        childControl.Focus();
    }

Then the mouse wheel events should be direct to childControl.

Idle_Mind
  • 38,363
  • 3
  • 29
  • 40
  • I would rather not use MouseEnter... only setting this when the user has actively clicked on the scrollbar. (and tried using the `Click` event but did not help.) – Mystogan May 07 '13 at 16:08
  • To trap a **mouse down on the scroll bar area** you could Inherit from the base class and look for a WM_NCLBUTTONDOWN message in WndProc(). – Idle_Mind May 07 '13 at 16:28