0

I have an application with a ListView control that contains a number of objects the user can "drag and drop" within a Panel. Originally, I used DoDragDrop but decided not to so that I could implement a semi-transparent, custom control I call ShadowBox. The ShadowBox inherits from Form and is centred on the mouse cursor as you place the item. The item is placed in the Panel as a custom control that inherits from Label we can call Item.

When using the the MouseMove event on the Item I am able to freely drag the ShadowBox anywhere on screen and it disappears once I let go of the mouse regardless of where on the screen I am. This means MouseMove is continuing to fire even after the cursor technically left the bounds of the Item.

My problem has two elements:

1) When I do the same thing on a ListView the MouseMove is not fired unless the mouse is directly over the ListView. This means the ShadowBox blocks the MouseMove from triggering and I can't continue the MouseMove as I enter the panel. Is there a way to fix this (see code below). I think that this:

How do I capture the mouse move event

may be my answer, but I can't get it to work.

2) Just for the sake of my understanding, and assuming I'm not just doing something wrong on my MouseDown or MouseMove events, why is the MouseMove behaviour different for Item than for ListView? (i.e. why does MouseMove seem to continue to fire even when the mouse isn't directly over the Item, but the same is not true for the ListView?)

ShadowBox sb;   
Point startMousePosition;

private void Toolbox_MouseDown(object sender, MouseEventArgs e)
{
    ListViewItem lvi;
    Point p;

    lvi = lvToolbox.GetItemAt(e.X, e.Y);
    if (lvi != null)
    {
        if ((e.Button & MouseButtons.Left) == MouseButtons.Left)
        {
            sb = new ShadowBox();

            p = lvToolbox.PointToScreen(e.Location);
            sb.Show();
            sb.Size = new Size(144, 108); //Note: I simplified this code, the size will depend on the ListViewItem selected.
            sb.Opacity = 0.8;
            sb.Location = new Point(p.X - sb.Width / 2, p.Y - sb.Height / 2);
            sb.Show();
            startMouseDown = new Point(sb.Width / 2, sb.Height / 2); //e.Location;

            lvToolbox.MouseMove += new MouseEventHandler(Toolbox_MouseMove);
        }
    }
}

private void Toolbox_MouseMove(object sender, MouseEventArgs e)
{
   if ((e.Button & MouseButtons.Left) == MouseButtons.Left)
   {
       sb.Location = PointToScreen(new Point(e.X - startMouseDown.X, e.Y - startMouseDown.Y));
   }
}

private void Toolbox_MouseUp(object sender, MouseEventArgs e)
{
    if (sb != null)
    {
        sb.Dispose();
    }

    lvToolbox.MouseMove -= new MouseEventHandler(Toolbox_MouseMove);
}
Community
  • 1
  • 1
matthew_b
  • 739
  • 1
  • 7
  • 18
  • Mouse capture is the key here. If you don't want ListView to capture the mouse then use `e.Handled = true;`. And `sb.Capture = true;` to get the MouseMove event to fire on your ShadowBox class. – Hans Passant Feb 29 '16 at 17:35
  • Thanks Hans. Where do I place e.Handled = true;? It gives an error ("'MouseEventArgs' does not contain a definition for 'Handled'") in my MouseDown and MouseMove event methods. – matthew_b Feb 29 '16 at 19:13
  • Oh, right, just sb.Capture is enough. Keep in mind that you now need to use the ShadowBox.MouseMove and MouseUp events. – Hans Passant Feb 29 '16 at 19:31
  • Thanks! That did the trick. For some reason, moving the mouse too quickly caused me to leave the ShadowBox and stop the MouseMove from triggering, but I fixed that by adding a MouseLeave event that recentred the ShadowBox. I'd up-vote your comment, but I don't have sufficient privileges :) – matthew_b Feb 29 '16 at 19:49

0 Answers0