Problem determining when user has clicked outside my UserControl

R

Rhy Mednick

I'm creating a custom control (inherited from UserControl) that is displayed
by other controls on the form. I would like for the control to disappear
when the user clicks outside my control the same way a menu does. To do
this my control needs to get notified when the user tried to click off of
it. The Leave and LostFocus events of the UserControl work most of the time
but not always. For example, if they click on a part of the form which does
not have a control then the event doesn't fire. It's more accurate to say
that clicking on any control which can't be selected (like a picture box,
for example) doesn't cause the event to fire.

I tried to work around this problem by having my control hook into the click
event of each control on the form (and the form itself). I had to hook each
control because I don't know what the other controls are at runtime and I
needed to make sure that I was trapping every possible way that a user might
click off my control. This works perfectly as long as the user clicks on
the client area of the form. However, I'm still not getting notified if the
user clicks on the title bar of the form. Any suggestions you may have
would be greatly appreciated.

To demonstrate this problem I created a small form with a user control on it
(code is below). When you click on the control it turns red and when you
click off of it, it turns grey. I added a few other controls to the form to
show how different controls behave differently. In the code below, every
event that I can think of is trapped, but I still can't detect when the form
title is clicked. The code is in C# but I believe that VB would have the
same problem.

--- Form Code ---
using System;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.Data;

namespace Focus_Problem
{
/// <summary>
/// Summary description for Form1.
/// </summary>
public class Form1 : System.Windows.Forms.Form
{
private Focus_Problem.Focus_Control focus_Control1;
private System.Windows.Forms.Button button1;
private System.Windows.Forms.CheckBox checkBox1;
private System.Windows.Forms.PictureBox pictureBox1;
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.Container components = null;

public Form1()
{
//
// Required for Windows Form Designer support
//
InitializeComponent();

//
// TODO: Add any constructor code after InitializeComponent call
//
}

/// <summary>
/// Clean up any resources being used.
/// </summary>
protected override void Dispose( bool disposing )
{
if( disposing )
{
if (components != null)
{
components.Dispose();
}
}
base.Dispose( disposing );
}

#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.focus_Control1 = new Focus_Problem.Focus_Control();
this.button1 = new System.Windows.Forms.Button();
this.checkBox1 = new System.Windows.Forms.CheckBox();
this.pictureBox1 = new System.Windows.Forms.PictureBox();
this.SuspendLayout();
//
// focus_Control1
//
this.focus_Control1.BackColor = System.Drawing.Color.DarkGray;
this.focus_Control1.Location = new System.Drawing.Point(24, 16);
this.focus_Control1.Name = "focus_Control1";
this.focus_Control1.Size = new System.Drawing.Size(64, 64);
this.focus_Control1.TabIndex = 0;
//
// button1
//
this.button1.Location = new System.Drawing.Point(168, 24);
this.button1.Name = "button1";
this.button1.Size = new System.Drawing.Size(104, 32);
this.button1.TabIndex = 1;
this.button1.Text = "button1";
//
// checkBox1
//
this.checkBox1.Location = new System.Drawing.Point(168, 72);
this.checkBox1.Name = "checkBox1";
this.checkBox1.Size = new System.Drawing.Size(104, 16);
this.checkBox1.TabIndex = 2;
this.checkBox1.Text = "checkBox1";
//
// pictureBox1
//
this.pictureBox1.Location = new System.Drawing.Point(0, 0);
this.pictureBox1.Name = "pictureBox1";
this.pictureBox1.TabIndex = 0;
this.pictureBox1.TabStop = false;
//
// Form1
//
this.AutoScaleBaseSize = new System.Drawing.Size(5, 13);
this.ClientSize = new System.Drawing.Size(292, 266);
this.Controls.Add(this.pictureBox1);
this.Controls.Add(this.checkBox1);
this.Controls.Add(this.button1);
this.Controls.Add(this.focus_Control1);
this.Name = "Form1";
this.Text = "Form1";
this.Load += new System.EventHandler(this.Form1_Load);
this.ResumeLayout(false);

}
#endregion

/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
Application.Run(new Form1());
}

private void Form1_Load(object sender, System.EventArgs e)
{

}
}
}

---- UserControl Code -----
using System;
using System.Collections;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Windows.Forms;

namespace Focus_Problem
{
/// <summary>
/// Summary description for Focus_Control.
/// </summary>
public class Focus_Control : System.Windows.Forms.UserControl
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.Container components = null;

public Focus_Control()
{
// This call is required by the Windows.Forms Form Designer.
InitializeComponent();

// TODO: Add any initialization after the InitializeComponent call

}

/// <summary>
/// Clean up any resources being used.
/// </summary>
protected override void Dispose( bool disposing )
{
if( disposing )
{
if(components != null)
{
components.Dispose();
}
}
base.Dispose( disposing );
}

#region Component Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
//
// Focus_Control
//
this.BackColor = System.Drawing.SystemColors.ControlDark;
this.Name = "Focus_Control";
this.Size = new System.Drawing.Size(104, 104);
this.Click += new System.EventHandler(this.Focus_Control_Click);
this.Load += new System.EventHandler(this.Focus_Control_Load);
this.Paint += new
System.Windows.Forms.PaintEventHandler(this.Focus_Control_Paint);
this.Leave += new System.EventHandler(this.Focus_Control_Leave);

}
#endregion

private bool Hooked = false;

private void Focus_Control_Load(object sender, System.EventArgs e)
{
this.BackColor = Color.DarkGray;
this.LostFocus +=new EventHandler(Focus_Control_LostFocus);


}

private void Focus_Control_Click(object sender, System.EventArgs e)
{
this.BackColor = Color.Red;
}

private void Focus_Control_Leave(object sender, System.EventArgs e)
{
this.BackColor = Color.DarkGray;
}

private void Focus_Control_LostFocus(object sender, EventArgs e)
{
this.BackColor = Color.DarkGray;
}

private void Focus_Control_Paint(object sender,
System.Windows.Forms.PaintEventArgs e)
{
if (!Hooked)
{
//I need to do this odd dance of hooking these events in the paint
//event because it was the only event I could find which I was sure
//would fire after the ParentForm property was set.
foreach (Control c in this.ParentForm.Controls)
{
if (c != this)
c.Click +=new EventHandler(c_Click);
}
this.ParentForm.Click += new EventHandler(c_Click);
Hooked = true;
}
}

private void c_Click(object sender, EventArgs e)
{
this.BackColor = Color.DarkGray;
}
}
}


Thanks for your help.
- Rhy
 
B

Bob Powell [MVP]

Did you try capturing the mouse and detecting the position? See
Control.Capture.

The other alternative is to install a mouse hook.

http://support.microsoft.com/default.aspx?scid=kb;EN-US;318804

--
Bob Powell [MVP]
Visual C#, System.Drawing

Image transition effects, snap-to-grid and Layered Windows are
all discussed in May's edition of Well Formed for C# or VB programmers
http://www.bobpowell.net/currentissue.htm

Answer those GDI+ questions with the GDI+ FAQ
http://www.bobpowell.net/gdiplus_faq.htm

The GDI+ FAQ RSS feed: http://www.bobpowell.net/faqfeed.xml
Windows Forms Tips and Tricks RSS: http://www.bobpowell.net/tipstricks.xml
Bob's Blog: http://bobpowelldotnet.blogspot.com/atom.xml






Rhy Mednick said:
I'm creating a custom control (inherited from UserControl) that is displayed
by other controls on the form. I would like for the control to disappear
when the user clicks outside my control the same way a menu does. To do
this my control needs to get notified when the user tried to click off of
it. The Leave and LostFocus events of the UserControl work most of the time
but not always. For example, if they click on a part of the form which does
not have a control then the event doesn't fire. It's more accurate to say
that clicking on any control which can't be selected (like a picture box,
for example) doesn't cause the event to fire.

I tried to work around this problem by having my control hook into the click
event of each control on the form (and the form itself). I had to hook each
control because I don't know what the other controls are at runtime and I
needed to make sure that I was trapping every possible way that a user might
click off my control. This works perfectly as long as the user clicks on
the client area of the form. However, I'm still not getting notified if the
user clicks on the title bar of the form. Any suggestions you may have
would be greatly appreciated.

To demonstrate this problem I created a small form with a user control on it
(code is below). When you click on the control it turns red and when you
click off of it, it turns grey. I added a few other controls to the form to
show how different controls behave differently. In the code below, every
event that I can think of is trapped, but I still can't detect when the form
title is clicked. The code is in C# but I believe that VB would have the
same problem.

--- Form Code ---
using System;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.Data;

namespace Focus_Problem
{
/// <summary>
/// Summary description for Form1.
/// </summary>
public class Form1 : System.Windows.Forms.Form
{
private Focus_Problem.Focus_Control focus_Control1;
private System.Windows.Forms.Button button1;
private System.Windows.Forms.CheckBox checkBox1;
private System.Windows.Forms.PictureBox pictureBox1;
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.Container components = null;

public Form1()
{
//
// Required for Windows Form Designer support
//
InitializeComponent();

//
// TODO: Add any constructor code after InitializeComponent call
//
}

/// <summary>
/// Clean up any resources being used.
/// </summary>
protected override void Dispose( bool disposing )
{
if( disposing )
{
if (components != null)
{
components.Dispose();
}
}
base.Dispose( disposing );
}

#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.focus_Control1 = new Focus_Problem.Focus_Control();
this.button1 = new System.Windows.Forms.Button();
this.checkBox1 = new System.Windows.Forms.CheckBox();
this.pictureBox1 = new System.Windows.Forms.PictureBox();
this.SuspendLayout();
//
// focus_Control1
//
this.focus_Control1.BackColor = System.Drawing.Color.DarkGray;
this.focus_Control1.Location = new System.Drawing.Point(24, 16);
this.focus_Control1.Name = "focus_Control1";
this.focus_Control1.Size = new System.Drawing.Size(64, 64);
this.focus_Control1.TabIndex = 0;
//
// button1
//
this.button1.Location = new System.Drawing.Point(168, 24);
this.button1.Name = "button1";
this.button1.Size = new System.Drawing.Size(104, 32);
this.button1.TabIndex = 1;
this.button1.Text = "button1";
//
// checkBox1
//
this.checkBox1.Location = new System.Drawing.Point(168, 72);
this.checkBox1.Name = "checkBox1";
this.checkBox1.Size = new System.Drawing.Size(104, 16);
this.checkBox1.TabIndex = 2;
this.checkBox1.Text = "checkBox1";
//
// pictureBox1
//
this.pictureBox1.Location = new System.Drawing.Point(0, 0);
this.pictureBox1.Name = "pictureBox1";
this.pictureBox1.TabIndex = 0;
this.pictureBox1.TabStop = false;
//
// Form1
//
this.AutoScaleBaseSize = new System.Drawing.Size(5, 13);
this.ClientSize = new System.Drawing.Size(292, 266);
this.Controls.Add(this.pictureBox1);
this.Controls.Add(this.checkBox1);
this.Controls.Add(this.button1);
this.Controls.Add(this.focus_Control1);
this.Name = "Form1";
this.Text = "Form1";
this.Load += new System.EventHandler(this.Form1_Load);
this.ResumeLayout(false);

}
#endregion

/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
Application.Run(new Form1());
}

private void Form1_Load(object sender, System.EventArgs e)
{

}
}
}

---- UserControl Code -----
using System;
using System.Collections;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Windows.Forms;

namespace Focus_Problem
{
/// <summary>
/// Summary description for Focus_Control.
/// </summary>
public class Focus_Control : System.Windows.Forms.UserControl
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.Container components = null;

public Focus_Control()
{
// This call is required by the Windows.Forms Form Designer.
InitializeComponent();

// TODO: Add any initialization after the InitializeComponent call

}

/// <summary>
/// Clean up any resources being used.
/// </summary>
protected override void Dispose( bool disposing )
{
if( disposing )
{
if(components != null)
{
components.Dispose();
}
}
base.Dispose( disposing );
}

#region Component Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
//
// Focus_Control
//
this.BackColor = System.Drawing.SystemColors.ControlDark;
this.Name = "Focus_Control";
this.Size = new System.Drawing.Size(104, 104);
this.Click += new System.EventHandler(this.Focus_Control_Click);
this.Load += new System.EventHandler(this.Focus_Control_Load);
this.Paint += new
System.Windows.Forms.PaintEventHandler(this.Focus_Control_Paint);
this.Leave += new System.EventHandler(this.Focus_Control_Leave);

}
#endregion

private bool Hooked = false;

private void Focus_Control_Load(object sender, System.EventArgs e)
{
this.BackColor = Color.DarkGray;
this.LostFocus +=new EventHandler(Focus_Control_LostFocus);


}

private void Focus_Control_Click(object sender, System.EventArgs e)
{
this.BackColor = Color.Red;
}

private void Focus_Control_Leave(object sender, System.EventArgs e)
{
this.BackColor = Color.DarkGray;
}

private void Focus_Control_LostFocus(object sender, EventArgs e)
{
this.BackColor = Color.DarkGray;
}

private void Focus_Control_Paint(object sender,
System.Windows.Forms.PaintEventArgs e)
{
if (!Hooked)
{
//I need to do this odd dance of hooking these events in the paint
//event because it was the only event I could find which I was sure
//would fire after the ParentForm property was set.
foreach (Control c in this.ParentForm.Controls)
{
if (c != this)
c.Click +=new EventHandler(c_Click);
}
this.ParentForm.Click += new EventHandler(c_Click);
Hooked = true;
}
}

private void c_Click(object sender, EventArgs e)
{
this.BackColor = Color.DarkGray;
}
}
}


Thanks for your help.
- Rhy
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top