Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Mousewheel scroll in panel with dynamically added picturebox controls?

I've dynamically added 20 pictureboxes to a panel and would like to see the panel scroll when I use the mouse wheel. To implement this I have tried to set the autoscroll to true on the panel control. Here is the code. For i As Integer = 1 To 20:

        Dim b As New PictureBox()
        b.Image = Nothing
        b.BorderStyle = BorderStyle.FixedSingle
        b.Text = i.ToString()
        b.Size = New Size(60, 40)
        b.Location = New Point(0, (i * b.Height) - b.Height)
        b.Parent = Panel1
        Panel1.Controls.Add(b)
    Next

I did the same thing with button control and it works just fine. For i As Integer = 1 To 100:

        Dim b As New Button()

        b.Text = i.ToString()
        b.Size = New Size(60, 40)
        b.Location = New Point(0, (i * b.Height) - b.Height)
        b.Parent = Panel1
        Panel1.Controls.Add(b)
    Next

It works for "button" control, but not for the "picturebox" or "label" controls? How can I implementthe scrolling affect using 'mousewheel'?

like image 322
mike_jik Avatar asked Dec 02 '09 00:12

mike_jik


2 Answers

The panel scrolls with the mousewheel when it or a control within it has focus. The problem you are running into is that neither the PictureBox nor the panel receives focus when you click on it. If you call select() on the panel, you will see that the mouse wheel starts working again.

One possible solution would be to select the panel whenever the mouse cursor enters it, by handling the Control.MouseEnter event:

void panel1_MouseEnter(object sender, EventArgs e)
{
    panel1.select();
}
like image 88
cwick Avatar answered Nov 12 '22 18:11

cwick


"cwick" is quite right, Windows posts the WM_MOUSWHEEL notification to the window that has the focus. It works when you put a button in the panel because it can get the focus. The next thing that happens is that Windows keeps looking for a Parent control to take the message. Button won't care for it, it's Parent is the panel and it will happily scroll and consume the message.

Other than doctoring with the child controls' ability to take focus (you'll have to override them and call SetStyle(ControlStyles.Selectable)), you could consider changing the way this message gets handled. Many commercial apps seemingly don't have this problem (browsers, Office apps) because they have only a few windows. WF apps usually have a lot, one for each control. Do so by processing the message early, before it gets sent to the focused control. The IMessageFilter interface allows this. This sample code scrolls the control under the mouse instead of the control that has the focus:

using System;
using System.ComponentModel;
using System.Drawing;
using System.Windows.Forms;
using System.Runtime.InteropServices;

namespace WindowsApplication1 {
  public partial class Form1 : Form, IMessageFilter {
    public Form1() {
      InitializeComponent();
      Application.AddMessageFilter(this);
    }

    public bool PreFilterMessage(ref Message m) {
      if (m.Msg == 0x20a) {
        // WM_MOUSEWHEEL, find the control at screen position m.LParam
        Point pos = new Point(m.LParam.ToInt32());
        IntPtr hWnd = WindowFromPoint(pos);
        if (hWnd != IntPtr.Zero && hWnd != m.HWnd && Control.FromHandle(hWnd) != null) {
          SendMessage(hWnd, m.Msg, m.WParam, m.LParam);
          return true;
        }
      }
      return false;
    }

    // P/Invoke declarations
    [DllImport("user32.dll")]
    private static extern IntPtr WindowFromPoint(Point pt);
    [DllImport("user32.dll")]
    private static extern IntPtr SendMessage(IntPtr hWnd, int msg, IntPtr wp, IntPtr lp);
  }
}

Beware that this code is active for any window in your app. Make sure you try it and verify that it won't confuse the user too much.

like image 22
Hans Passant Avatar answered Nov 12 '22 19:11

Hans Passant