views:

681

answers:

2

I've dynamically added 20 pictureboxes to a panel. i would like to see the panel scroll when I use the mousewheel. I set the autoscroll = true on 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

why does it work for "button" control but not for "picturebox" or "label" controls? how do i achieve the scrolling affect using mousewheel. thanks.

+2  A: 

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();
}
cwick
+2  A: 

"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() & 0xffff, m.LParam.ToInt32() >> 16);
        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.

Hans Passant