Reputation: 33186
I have create a field of small panels on a form and I want the user to be able to color these panels. To color them you have to simpely click the panel, this works. Now I want to be able to let the use click a panel and drag over other panels to color more panels at once.
I have added the mousedown and mouseup event to all panels to set a boolean. The I use the mousemove event to color the panels. Yet only the first panel gets collored.
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.Diagnostics;
namespace Application
{
public partial class Form1 : Form
{
private const int dim = 25;
private int cols;
private int rows;
private bool mouse_down = false;
private sbyte fill = -1;
private Panel pnlTmp;
public Form1()
{
InitializeComponent();
this.buildGrid();
}
/// <summary>
/// Rebuild the grid to fit the screen.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void toolStripMenuItem2_Click(object sender, EventArgs e)
{
this.buildGrid();
}
/// <summary>
/// Build the grid to fit the screen.
/// </summary>
private void buildGrid()
{
panel1.Controls.Clear();
cols = int.Parse(Math.Floor((double)panel1.Width / dim).ToString());
rows = int.Parse(Math.Floor((double)panel1.Height / dim).ToString());
for (int i = 0; i < rows; i++)
{
for (int j = 0; j < cols; j++)
{
Panel pnl = new Panel();
pnl.BorderStyle = BorderStyle.FixedSingle;
pnl.Width = dim;
pnl.Height = dim;
pnl.Location = new Point(j * 25 + 1, i * 25 + 1);
pnl.Name = string.Format("pnl-{0}-{1}", j, i);
pnl.MouseDown += new MouseEventHandler(pnl_MouseDown);
pnl.MouseUp += new MouseEventHandler(pnl_MouseUp);
pnl.MouseMove += new MouseEventHandler(pnl_MouseHover);
panel1.Controls.Add(pnl);
}
}
}
void pnl_MouseHover(object sender, EventArgs e)
{
if (mouse_down)
{
Panel p = (Panel)sender;
if (p != pnlTmp)
{
if (fill == -1)
fill = (p.BackColor == SystemColors.Control) ? (sbyte)1 : (sbyte)0;
if (fill == 1)
p.BackColor = Color.Blue;
else
p.BackColor = SystemColors.Control;
Debug.WriteLine(p.Name);
}
pnlTmp = p;
}
}
void pnl_MouseDown(object sender, MouseEventArgs e)
{
Debug.WriteLine("true");
mouse_down = true;
}
void pnl_MouseUp(object sender, MouseEventArgs e)
{
Debug.WriteLine("false");
mouse_down = false;
fill = -1;
}
private void panel1_MouseLeave(object sender, EventArgs e)
{
//mouse_down = false;
//fill = -1;
}
}
}
I have tried debugging the application and the result was that only the first panel keeps firing the event, even if I am moving over other panels.
Could someone tell me why this is?
Upvotes: 0
Views: 864
Reputation: 941545
Your problem is caused by a feature called "mouse capture". It is controlled by the Control.Capture property. The default behavior is that it is turned on automatically in the OnMouseDown() method, before it fires the MouseDown event.
Mouse capture forces all mouse events to be directed to the window that you clicked, even if you move the mouse outside of the window. Which is why you only ever get the MouseMove and MouseUp events for the panel that you clicked on. It is important in a number of scenarios, particularly to reliably generate the Click and MouseUp events.
The workaround is to simply turn the capture back off in your MouseDown event handler:
void pnl_MouseDown(object sender, MouseEventArgs e) {
((Control)sender).Capture = false;
}
Do note that you now have a new problem, your "mouse_down" variable is no longer reliable. If you move the mouse outside of any panel, or outside of the form, and release the mouse then the MouseUp event is sent to the wrong window and your mouse_down variable remains set to true even though the mouse is no longer down. You lost the guarantee provided by the capture feature. You solve that problem by checking the button state in your MouseMove event handler, like this:
private void pnl_MouseMove(object sender, MouseEventArgs e) {
if (e.Button == MouseButtons.Left) {
// etc...
}
}
Beware that I fixed your incorrect pnl_MouseHover event handler and renamed it to pnl_MouseMove. I can see how you ended up making this mistake, but it possibly did prevent you from discovering this solution yourself. Careful with that axe Eugene ;)
Upvotes: 2