• 0

[C#] Highlighting a panel and all Controls with a colour


Question

Hi,

I've tried a number of approaches to this and have settled on one that 'sort' of works but seems like a terrible solution.

I have a panel control (UserControl item) that exists on screen with a load of others. As the mouse goes over this panel control it is to have a pale blue background. This panel control also has many Controls within it, whos back colours must also be changed.

Obviously, when the mouse enters the panel, it fires the mouseEnter event of the control and everything can be changed as required. However, when the mouse goes over one of the contained controls, it fires the mouseLeaving event for the parent Control and the mouseEnter for the newly mouse-overed control.

So, at the moment, I have one method for mouseIn, and one for MouseOut which are constantly alternated causing a slight white flicker as it changes between items within being mouse-overed.

I tried to test for the pointer position in the mouseOut code and not run it if it's still within the outer bounds of the main Control but to no avail. Does anyone know how to sort this, and (if possible) code samples would be really useful as I've already messed this up a few times!!!

Thanks,

Chris

12 answers to this question

Recommended Posts

  • 0

Do something like this:

		private void userControl11_MouseEnter(object sender, EventArgs e)
		{
			userControl11.BackColor = Color.LightBlue;
			makeAllOfUserControl1sControlsLightBlueMofo();
		}

		private void Form1_MouseEnter(object sender, EventArgs e)
		{
			userControl11.BackColor = Color.Empty;
			makeAllOfUserControl1sControlsNormalMofo();
		}

In other words, don't use utilize any MouseLeaving events.

  • 0
  boogerjones said:
Do something like this:

		private void userControl11_MouseEnter(object sender, EventArgs e)
		{
			userControl11.BackColor = Color.LightBlue;
			makeAllOfUserControl1sControlsLightBlueMofo();
		}

		private void Form1_MouseEnter(object sender, EventArgs e)
		{
			userControl11.BackColor = Color.Empty;
			makeAllOfUserControl1sControlsNormalMofo();
		}

In other words, don't use utilize any MouseLeaving events.

Sometimes the mouse moves too fast over the form, and the Form_MouseEnter was never fired. Handling MouseLeaving takes care of that (and opens up a different can of worms, as the OP experienced).

@OP If you're experienced with Win32 APIs, there's are some functions you can use. Checking the mouse position point, getting a rectangle definition of a control, then checking whether a point is inside a rectangle. You can then utilise them in your MouseLeaving handler. Sorry, it's been a while, and I can only remember these vaguely...

  • 0

OK, then try this:

		private void userControl11_MouseEnter(object sender, EventArgs e)
		{
			userControl11.BackColor = Color.LightBlue;
		}

		private void userControl11_MouseLeave(object sender, EventArgs e)
		{
			if (AmIStillInsideTheUserControl(userControl11) == false)
				userControl11.BackColor = Color.Empty;
		}

		private bool AmIStillInsideTheUserControl(Control control)
		{
			Point whereTheHellIsMyUserControl = control.PointToScreen(new Point(0, 0));
			return (Cursor.Position.X > whereTheHellIsMyUserControl.X && Cursor.Position.X < (whereTheHellIsMyUserControl.X + control.Width) &&
					Cursor.Position.Y > whereTheHellIsMyUserControl.Y && Cursor.Position.Y < (whereTheHellIsMyUserControl.Y + control.Height));
		}

It checks to see if the mouse is still inside the user control before resetting the color.

Edited by boogerjones
  • 0

Hmm it's all coming back now. I forgot .Net has all these functions built in.

Here's a simpler version:

	
private bool AmIStillInsideTheUserControl(Control control)
{
  Rectangle r = control.RectangleToScreen(control.ClientRectangle);
  Point p = Cursor.Position;
  return r.Contains(p);
}

Although, I just realised that there's still a flaw in this method. It won't work if the container control is partially covered by *another window* and the mouse moved to that window, but still within the rectangle bounds of your container.

  • 0
  GreenMartian said:
Hmm it's all coming back now. I forgot .Net has all these functions built in.

Here's a simpler version:

	
private bool AmIStillInsideTheUserControl(Control control)
{
  Rectangle r = control.RectangleToScreen(control.ClientRectangle);
  Point p = Cursor.Position;
  return r.Contains(p);
}

Although, I just realised that there's still a flaw in this method. It won't work if the container control is partially covered by *another window* and the mouse moved to that window, but still within the rectangle bounds of your container.

Nice simplification, but the rectangle.Contains() method just does the exact calculation that I was doing anyway. The remaining flaw is a pretty picky one, though it would be nice to have a 100% working implementation.
  • 0
  boogerjones said:
Nice simplification, but the rectangle.Contains() method just does the exact calculation that I was doing anyway. The remaining flaw is a pretty picky one, though it would be nice to have a 100% working implementation.

Yeah, it's picky, but a pretty common case :)

Here's one that might work:

Declare this on your form's method declarations:

[DllImport("user32")]
public static extern IntPtr WindowFromPoint(int xPoint, int yPoint);

Then use this:

private bool AmIStillInsideTheUserControl(Control control)
{
  bool insidechild = false;
  Point p = Cursor.Position;
  IntPtr handle = WindowFromPoint(p.X, p.Y);

  foreach (Control child in control.Controls)
	if (child.Handle == handle)
	{
	  insidechild = true;
	  break;
	}
  return insidechild;
}

It will handle the case of leaving to another window covering the container..

EDIT: If you have containers inside the container, you might want to take the child control checking out to a recursive function...

Edited by GreenMartian
  • 0
  boogerjones said:
I almost wonder if this is a bug in the Windows messaging pipeline. Shouldn't the control.MouseLeave event be called if the mouse rolls over an obscuring window?

It is called, but our rectangle-checking method will tell us that the cursor's still inside the rectangle..

  • 0
  GreenMartian said:
Hmm it's all coming back now. I forgot .Net has all these functions built in.

Here's a simpler version:

	
private bool AmIStillInsideTheUserControl(Control control)
{
  Rectangle r = control.RectangleToScreen(control.ClientRectangle);
  Point p = Cursor.Position;
  return r.Contains(p);
}

Although, I just realised that there's still a flaw in this method. It won't work if the container control is partially covered by *another window* and the mouse moved to that window, but still within the rectangle bounds of your container.

  GreenMartian said:
Yeah, it's picky, but a pretty common case :)

Here's one that might work:

Declare this on your form's method declarations:

[DllImport("user32")]
public static extern IntPtr WindowFromPoint(int xPoint, int yPoint);

Then use this:

private bool AmIStillInsideTheUserControl(Control control)
{
  bool insidechild = false;
  Point p = Cursor.Position;
  IntPtr handle = WindowFromPoint(p.X, p.Y);

  foreach (Control child in control.Controls)
	if (child.Handle == handle)
	{
	  insidechild = true;
	  break;
	}
  return insidechild;
}

It will handle the case of leaving to another window covering the container..

EDIT: If you have containers inside the container, you might want to take the child control checking out to a recursive function...

Both of those seem pretty good actually... I'll whack them in to the project in a minute and see if we can't fix what is just so ugly in code.

Thanks so much for all of your efforts!!!

Chris

  • 0
  Mike said:
How about changing the colour on MouseEnter of your control and changing it back on MouseEnter of the parent object of your control (in most cases, the form)?

Because sometimes if you move your mouse just a bit too fast, the form one won't be fired.

This topic is now closed to further replies.
  • Recently Browsing   0 members

    • No registered users viewing this page.