Link to home
Start Free TrialLog in
Avatar of kaufmed
kaufmedFlag for United States of America

asked on

Scroll a Panel Without Having Focus

I have created a panel that has several objects on it. Autoscroll is set to true, so when the number of objects exceeds the visible bounds, the panel automatically adds scrollbars. I want to be able to use the mouse wheel to scroll the panel; however, I would like not to be forced to give the panel focus in order to do so.

I have been experimenting with IMessageFilter to pre-process the mouse wheel event (actually the WM_MOUSEWHEEL message). The code I have been working with is below, and yes, I have added the message filter to the form, even though that line of code is not below. Some code snippets online suggested using SendMessage() to relay the message to the Panel without it needing focus. Would I be correct in my assumption that the panel will process the WM_MOUSEWHEEL message even though it does not have focus?

To summarize, my overall intent is to have the panel be scrollable with the mouse wheel, and only when the mouse is over the panel, not always. My preference is to not give the panel focus and still be able to scroll with the mouse wheel.

Thanks in advance!
const int WM_MOUSEWHEEL = 0x020A;

public bool PreFilterMessage(ref Message m)
{
    if (m.HWnd == this.taskPanel1.Handle && m.Msg == WM_MOUSEWHEEL)
    {                
        SendMessage(this.taskPanel1.Handle, m.Msg, m.WParam, m.LParam);
        m.Result = IntPtr.Zero;

        return true;
    }

    return false;
}

Open in new window

ASKER CERTIFIED SOLUTION
Avatar of Mike Tomlinson
Mike Tomlinson
Flag of United States of America image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
on control, Create a Public Function MouseWeel(MouseEventArgs e) that calls base.OnMouseWeel(e)

Then on the Form manage the MouseWheel event:

Tested on Vb:

        If ActiveControl IsNot PrvCtl Then
            If New Rectangle(PrvCtl.Location, PrvCtl.Size).Contains(New Point(e.X, e.Y)) Then
                PrvCtl.MouseWeel(e)
            End If
        End If
Avatar of kaufmed

ASKER

@Idle_Mind

I swear you have an affinity for this GUI stuff ;)

So you implemented the message filter on the control itself. Right now I implemented it at the form level. Should I implement it only for the panel, or does it even matter? To me, it seems as though implementing at the form level as I have means that there will be more messages passing through the filter, but the end result is the same. Am I correct?

BTW:  By "at the form level," I mean

    Application.AddMessageFilter(this);

on my form.

@x77

I assume the code you provided above is to be placed in the MouseWheel handler of the form. Calling OnMouseWheel as you have it implemented above will result in an endless loop (to the point of stack overflow).

It doesn't matter where you implement it...ALL messages intended for your app still go thru the Filter...  =)

I passed in a reference to the Panel since that was all that I was interested in.  You could pass in whatever you want really!.

You could also NOT pass in anything at all and instead make the filter raise an event that the Form subscribes to.  Then, in the form itself, you could do the check to see if the cursor is over the panel and scroll from there accordingly.

That might look like:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Runtime.InteropServices;

namespace WindowsFormsApplication1
{
    public partial class Form1 : Form
    {

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

        public Form1()
        {
            InitializeComponent();
            MyScroll ms = new MyScroll();
            ms.MouseWheelScrolled += new MyScroll.MouseWheel(ms_MouseWheelScrolled);
            Application.AddMessageFilter(ms);
        }

        private void ms_MouseWheelScrolled(Message m)
        {
            if (this.panel1.RectangleToScreen(this.panel1.ClientRectangle).Contains(Cursor.Position))
            {
                SendMessage(this.panel1.Handle, m.Msg, m.WParam.ToInt32(), m.LParam.ToInt32());
            }
        }

    }

    public class MyScroll : IMessageFilter
    {

        public delegate void MouseWheel(Message m);
        public event MouseWheel MouseWheelScrolled;

        private const int WM_MOUSEWHEEL = 0x020A;

        bool IMessageFilter.PreFilterMessage(ref Message m)
        {
            if (m.Msg == WM_MOUSEWHEEL)
            {
                MouseWheelScrolled(m);
            }

            return false;
        }

    }

}

Open in new window

Avatar of kaufmed

ASKER

LOL. This is hilarious. I made a boo boo.

I was investigating why I could not get the panel to scroll using SendMessage(). The issue was that I had a panel, but the panel was part of a user control. The Handle I was passing in to SendMessage() was that of the user control and not the panel that was a part of the user control (lol).

Sorry about taking so long, but I was determined to figure that one out (and I didn't wan to respond back with more questions without understanding the problem).
Avatar of kaufmed

ASKER

Always a pleasure working with you, sir!
Hehe...glad you figured it out!  =)
Re - I assume the code you provided above is to be placed in the MouseWheel handler of the form. Calling OnMouseWheel as you have it implemented above will result in an endless loop (to the point of stack overflow).

No, I define a new Function on the control.

Public void NewFunction(MouseEventArgs e)
{
   base.OnMouseWeel(e);
}

I call it MouseWeell, then I can call it from MouseWeell event on Form.
I can't call Control.OnMouseWeel(e) from Form methods, it is Protected.