I have created my own ComboBox-like control where the dropdown part contains a tree. I have seen those solutions using an ordinary ComboBox and overwriting the WndProc, but there was always some odd behavior despite lots and lots of code. So I decided to make it simple: just a label with a ToolStripDropDown/ToolStripControlHost that is opened when mouse goes down on the label. The missing ComboBox triangle doesn't hurt.
Everything works perfectly, except one tiny thing: like with the stock ComboBox I would like the dropdown to hide when I click on the label again. But when I click on it, the dropdown hides for a split second, just to appear again. If I click outside the label, the dropdown just hides, like it should be.
public class RepoNodeComboBox: Label
{
RepoTreeView repoTreeView;
ToolStripControlHost treeViewHost;
ToolStripDropDown dropDown;
bool isDropDownOpen;
public int DropDownHeight;
public RepoNodeComboBox()
{
repoTreeView = new RepoTreeView();
repoTreeView.BorderStyle = BorderStyle.None;
repoTreeView.LabelEdit = false;
treeViewHost = new ToolStripControlHost(repoTreeView);
treeViewHost.Margin = Padding.Empty;
treeViewHost.Padding = Padding.Empty;
treeViewHost.AutoSize = false;
dropDown = new ToolStripDropDown();
dropDown.CanOverflow = true;
dropDown.AutoClose = true;
dropDown.DropShadowEnabled = true;
dropDown.Items.Add(treeViewHost);
dropDown.Closing += dropDownClosing;
TextAlign = ContentAlignment.MiddleLeft;
BackColor = SystemColors.Window;
BorderStyle = BorderStyle.FixedSingle;
DropDownHeight = 400;
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
if (dropDown != null)
{
dropDown.Dispose();
dropDown = null;
}
}
base.Dispose(disposing);
}
// when mouse goes down on the label, this is executed first
void dropDownClosing(object sender, System.ComponentModel.CancelEventArgs e)
{
// just to test if I can get more out of it than with dropdown.Visible
isDropDownOpen = false;
}
// this is subsidiary to the Closing event of the dropdown
protected override void OnMouseDown(MouseEventArgs e)
{
if (dropDown != null)
{
if (isDropDownOpen)
{
dropDown.Hide();
}
else
{
repoTreeView.Size = new Size(Width, DropDownHeight);
treeViewHost.Width = Width;
treeViewHost.Height = DropDownHeight;
dropDown.Show(this, 0, Height);
isDropDownOpen = true;
}
}
base.OnMouseDown(e);
}
}
As far as I can see (breakpoints), the dropdown catches the MOUSEDOWN event first in order to close itself. Only after that my label gets passed through the MOUSEDOWN event, and since it sees the dropdown is closed, it thinks the label has been clicked like for the first time - and opens the dropdown again.
So it seems the label has no chance of knowing if the MOUSEDOWN was the result of closing the dropdown item. I could open it every other event, but that would require no other closing events to happen.
Is there any way to make sure that an open dropdown item just closes even if I click on the label?
Try checking for the Mouse position when the floating host closes:
void dropDownClosing(object sender, CancelEventArgs e) {
isDropDownOpen = this.ClientRectangle.Contains(this.PointToClient(Control.MousePosition));
}
On the MouseDown code, make sure to set isDropDownOpen to false in the true block:
protected override void OnMouseDown(MouseEventArgs e) {
if (dropDown != null) {
if (isDropDownOpen) {
isDropDownOpen = false;
dropDown.Hide();
} else {
isDropDownOpen = true;
repoTreeView.Size = new Size(Width, DropDownHeight);
treeViewHost.Width = Width;
treeViewHost.Height = DropDownHeight;
dropDown.Show(this, 0, Height);
}
}
base.OnMouseDown(e);
}
Related
I´m using ContextMenuStrip in my Form app as my drop down menu. Exactly, when I click on button, ContextMenuStrip shows right under it. Everithing is OK, but I really want to auto-close ContextMenuStrip after mouse leave its area. Ok, so I´m try to use MouseLeave event. Once again, everything is OK, but when I add dropdown items to some ToolStripItem in ContextMenuStrip, the mouseLeave event donť recognize this new area as a part of ContextMenuStrip. This is my newest attempt, but it is not finished. Any idea, how to resolve this problem?
private void ContextMenuStrip_MouseLeave(object sender, EventArgs e)
{
ContextMenuStrip cms = (sender is ContextMenuStrip) ? sender as ContextMenuStrip : null;
if (cms != null)
{
//List<Rectangle> cmsFullArea = new List<Rectangle>();
//cmsFullArea.Add(new Rectangle(cms.Bounds.Location, cms.Bounds.Size));
bool itemIsPressed = false;
for (int i = 0; i < cms.Items.Count; i++)
{
if (cms.Items[i].Pressed) { itemIsPressed = true; break; }
}
if (!itemIsPressed) { cms.Close(); }
}
}
This works fine, when I leave CMS to dropDown items, but it is not working, when I leave them too after. I need to close whole CMS, when I leave any of his areas.
Add a region variable which will be ContextMenuStrip plus the DropDownMenus.
private Region rgn = new Region();
Initialize region:
public Form1() {
InitializeComponent();
rgn.MakeEmpty();
}
When ContextMenuStrip opens update region:
private void contextMenuStrip1_Opened( object sender, EventArgs e ) {
rgn.Union( contextMenuStrip1.Bounds );
}
In leave event check if mouse is inside this region:
private void contextMenuStrip1_MouseLeave( object sender, EventArgs e ) {
Point pnt = Cursor.Position;
if( rgn.IsVisible( pnt ) == false ) {
rgn.MakeEmpty();
contextMenuStrip1.Close();
}
}
When you create a new ToolStripDropDownMenu adding items to eg toolStripMenuItem0, add these event handlers:
//toolStripMenuItem0 is an item of your ContextMenuStrip
toolStripMenuItem0.DropDown.MouseLeave += DropDown_MouseLeave;
toolStripMenuItem0.DropDown.Opened += DropDown_Opened;
toolStripMenuItem0.DropDown.Closed += DropDown_Closed;
private void DropDown_Closed( object sender, ToolStripDropDownClosedEventArgs e ) {
ToolStripDropDownMenu tsddm = (ToolStripDropDownMenu)sender;
rgn.Exclude( tsddm.Bounds ); //remove rect from region
}
private void DropDown_Opened( object sender, EventArgs e ) {
ToolStripDropDownMenu tsddm = (ToolStripDropDownMenu)sender;
rgn.Union( tsddm.Bounds ); //add rect to region
}
private void DropDown_MouseLeave( object sender, EventArgs e ) {
Point pnt = Cursor.Position;
if( rgn.IsVisible( pnt ) == false ) {
rgn.MakeEmpty();
contextMenuStrip1.Close();
}
}
Do the same for every DropDownMenu you create.
As I promise in comments, I want to post another solution for this problem.
Firstly there is MouseLeave method for event handle. This method is common to ContextMenuStrip (CMS) and ToolStripDropDownMenu (TSDDM).
private void ContextMenuStrip_MouseLeave(object sender, EventArgs e)
{
ContextMenuStrip cms = (sender is ContextMenuStrip) ? sender as ContextMenuStrip : null;
//Recognize CMS or TSDDM, in this case we dont need anything else
if (cms != null)
{
//Check, if mouse position is on any of CMS DropDownMenus.
//If false, close CMS. If true, we dont want to close it - CMS is actively in use
if (!IsMouseOnDropDown(cms.Items)) { cms.Close(); }
}
else
{
ToolStripDropDownMenu ddm = (sender is ToolStripDropDownMenu) ? sender as ToolStripDropDownMenu : null;
if (ddm != null)
{
//As above, check mouse position against items DropDownMenus
if (IsMouseOnDropDown(ddm.Items)) { return; }
//Declare our CMS
cms = GetPrimaryOwner(ddm);
//Get TSDDM owner
//var is important here, because we dont know if it is CMS or another TSDDM!!!
//Also TSDDM and CMS have the same properties for our purpose, so var is OK
var owner = ddm.OwnerItem.Owner;
Point pnt = Cursor.Position;
//If owner doesn't contains mouse position, close whole CMS
if (!owner.Bounds.Contains(pnt))
{
cms.Close();
}
else
{
//If does, we need to check if mouse position is exactly on parent item,
//because its prevent to TSDDM unnecessary close/open
//(explanation: Mouse leave TSDDM -> TSDDM close;
//Mouse is on parent item -> TSDDM open)
for (int i = 0; i < owner.Items.Count; i++)
{
//Define own rectangle, because item has its own bounds against the owner
//so we need to add up their X and Y to get the real one
int x = owner.Bounds.X + (owner.Items[i] as ToolStripMenuItem).Bounds.X;
int y = owner.Bounds.Y + (owner.Items[i] as ToolStripMenuItem).Bounds.Y;
Rectangle rect = new Rectangle(new Point(x, y), (pupik.Items[i] as ToolStripMenuItem).Bounds.Size);
//If its our DropDownMenu and mouse position is in there,
//we dont want to close ddm
if ((owner.Items[i] as ToolStripMenuItem).DropDown == ddm
&& rect.Contains(pnt))
{
return;
}
}
ddm.Close();
}
}
}
}
Above you can see IsMouseOnDropDown and GetPrimaryOwner methods, so there is the code:
private static bool IsMouseOnDropDown(ToolStripItemCollection itemCollection)
{
Point pnt = Cursor.Position;
//All what we do is check, if some of DropDownMenus from input collection is active (Visible)
//and if mouse position is in it
for (int i = 0; i < itemCollection.Count; i++)
{
if ((itemCollection[i] as ToolStripMenuItem).DropDown.Visible
&& (itemCollection[i] as ToolStripMenuItem).DropDown.Bounds.Contains(pnt))
{
return true;
}
}
return false;
}
private static ContextMenuStrip GetPrimaryOwner(ToolStripDropDownMenu dropDownMenu)
{
//All what we do is take owner by owner until we find our CMS,
//which is the last one -> primary owner
object cmsItems = dropDownMenu;
while (!(cmsItems is ContextMenuStrip))
{
cmsItems = (cmsItems as ToolStripDropDownMenu).OwnerItem.Owner;
}
return cmsItems as ContextMenuStrip;
}
The last thing what we need to do, is handle the mouseLeave event for every DropDownMenu an ContextmenuStrip
this.ContextMenuStrip1.MouseLeave += new System.EventHandler(ContextMenuStrip_MouseLeave);
this.StripMenuItem1.DropDown.MouseLeave += new System.EventHandler(ContextMenuStrip_MouseLeave);
With this example everything works fine to me, so if you find an error, please let me know.
It is just an example, so I dont write Try/Catch there..
i want to make trafic app for that i have to switch between radio button that act like a signals, actually i have 4 panels every panel have 3 radio buttons red , green , orange
Problem: when i select red in any panel or green other radio buttons must set1 to green or red according to requirment
Remember their will be only 1 green and all other reds at same time
i tried to implement code in check change event but if i apply the code on every button this become recursive so create problem i tried that too but not suitable solution
1 event on every radio button
private void R_lane3Red_CheckedChanged(object sender, EventArgs e)
{
// var checkedButton2 = panel2.Controls.OfType<RadioButton>()
// .FirstOrDefault(r => r.Checked);
// var checkedButton1 = panel1.Controls.OfType<RadioButton>()
// .FirstOrDefault(r => r.Checked);
// var checkedButton3 = panell3.Controls.OfType<RadioButton>()
// .FirstOrDefault(r => r.Checked);
// var checkedButton4 = panell4.Controls.OfType<RadioButton>()
// .FirstOrDefault(r => r.Checked);
//if (checkedButton2.Name.Contains("Green")){
// R_lane1Red.Checked = true;
// R_lane4Red.Checked = true;
// R_lane3Red.Checked = true;
//}
//if (checkedButton1.Name.Contains("Green"))
//{
// R_lane2Red.Checked = true;
// R_lane4Red.Checked = true;
// R_lane3Red.Checked = true;
//}
//if (checkedButton3.Name.Contains("Green"))
//{
// R_lane2Red.Checked = true;
// R_lane4Red.Checked = true;
// R_lane1Red.Checked = true;
//}
//if (checkedButton4.Name.Contains("Green"))
//{
// R_lane2Red.Checked = true;
// R_lane3Red.Checked = true;
// R_lane1Red.Checked = true;
//}
below is image of panel and radio buttons
image of UI
I suggest creating your Traffic Lights as a control. Then you can build the rules into the traffic light control for how it should behave when a specific light is set. Something like the following may work for you:
// The event that fires when a light changed, sends notification to the main form.
public delegate void LightChangedHandler(object sender, Lights light);
// Simple enum to pass the state of the TrafficLight around.
public enum Lights
{
Green,
Orange,
Red
}
public partial class TrafficLights : UserControl
{
public event LightChangedHandler LightChanged;
protected virtual void OnChanged(Lights light)
{
if (LightChanged != null)
{
LightChanged(this, light);
}
}
private bool _changing = false;
public TrafficLights()
{
InitializeComponent();
}
// The business rules for the Traffic Light states.
public void SetLight(Lights light)
{
if (_changing)
{
return;
}
_changing = true;
switch (light)
{
case Lights.Green:
radioGreen.Checked = true;
radioOrange.Checked = false;
radioRed.Checked = false;
break;
case Lights.Orange:
radioOrange.Checked = true;
radioGreen.Checked = false;
radioRed.Checked = false;
break;
case Lights.Red:
radioRed.Checked = true;
radioGreen.Checked = false;
radioOrange.Checked = false;
break;
}
OnChanged(light);
_changing = false;
}
private void radioGreen_CheckedChanged(object sender, EventArgs e)
{
SetLight(Lights.Green);
}
private void radioOrange_CheckedChanged(object sender, EventArgs e)
{
SetLight(Lights.Orange);
}
private void radioRed_CheckedChanged(object sender, EventArgs e)
{
SetLight(Lights.Red);
}
}
Then you can add as many TrafficLights as you want to your main form. When a light changes in any of your traffic lights, its raises an event to the main form. You can then handle this event and apply the business rules for how the bank of traffic lights should behave. Something like this:
private readonly List<TrafficLights> _trafficLights = new List<TrafficLights>();
private bool _changing = false;
public Form1()
{
InitializeComponent();
// Add each TrafficLight control on the Form to a list so we can spin through them later and set their state.
_trafficLights.Add(trafficLights1);
_trafficLights.Add(trafficLights2);
_trafficLights.Add(trafficLights3);
_trafficLights.Add(trafficLights4);
// Add the handler for the LightChanged event of each TrafficLight.
this.trafficLights1.LightChanged += TrafficLightsOnLightChanged;
this.trafficLights2.LightChanged += TrafficLightsOnLightChanged;
this.trafficLights3.LightChanged += TrafficLightsOnLightChanged;
this.trafficLights4.LightChanged += TrafficLightsOnLightChanged;
}
private void TrafficLightsOnLightChanged(object sender, Lights light)
{
// Only allow the first LightChanged event to fire off to avoid the recursive events.
if (_changing)
{
return;
}
_changing = true;
TrafficLights currentLight = sender as TrafficLights;
// Set the Light state of all other TrafficLights.
foreach (TrafficLights trafficLight in _trafficLights
.Where(trafficLight => trafficLight != currentLight))
{
LightChanged(trafficLight, light);
}
_changing = false;
}
// The business rules for setting the other TrafficLight states.
private static void LightChanged(TrafficLights trafficLight, Lights light)
{
// You will need to adjust the following to suit your specific business rules.
switch (light)
{
case Lights.Green:
trafficLight.SetLight(Lights.Red);
break;
case Lights.Orange:
trafficLight.SetLight(Lights.Orange);
break;
case Lights.Red:
trafficLight.SetLight(Lights.Green);
break;
}
}
Adding the bool _changing variable allows you to stop the event from recursively occurring. When the first event is fired, you set the _changing to true. This will stop further events from firing until you set it back to false once you are done handling the first event.
this is my problem: I have a main form where I have a panel that contains some buttons, when the user clicks a button a form is opened.( I have some buttons and clicking these buttons the user can open different forms )If the user click again the same button he can close the form.
this is what I do:
in the main form I have a method that is invoked when one of these buttons is clicked by the user, the method checks the text associated to the button in order to decide which is the button clicked. Once I have discovered which is the button that has been clicked it launches the form associated with the button.
this is the code
private void tlStBtn_Click(object sender, EventArgs e)
{
//// loop through all items in the ToolStrip
//foreach (Object item in toolStripMain.Items)
//{
// // if this item is a ToolStripButton object, check it
// if (item is ToolStripButton)
// {
// // cast the item to a ToolStripButton object and check it if the sender of the event is the currently looked at button in the loop
// ToolStripButton button = (ToolStripButton)item;
// button.Checked = (button == sender);
// }
//}
foreach (ToolStripItem item in this.VerticalToolBox.Items)
{
if ((item != sender) &&
(item is ToolStripButton))
{
((ToolStripButton)item).Checked = false;
}
}
if (sender_old != sender)
{
sender_old = sender;
if ((sender as ToolStripButton).Text == "Protection")
{
if (!Application.OpenForms.OfType<frm_Protection>().Any())
{
frm_Protection Newf = new frm_Protection(ref CurrentProject);
Newf.Show();
}
}
else
{
if (Application.OpenForms.OfType<frm_Protection>().Any())
{
Application.OpenForms.OfType<frm_Protection>().First().Close();
Properties.Settings.Default.Save();
}
GC.Collect();
GC.WaitForPendingFinalizers();
}
if ((sender as ToolStripButton).Text == "Info")
{
if (!Application.OpenForms.OfType<Frm_ObjectInfo>().Any())
{
Frm_ObjectInfo Newform = new Frm_ObjectInfo();
Newform.Show();
}
}
else
{
if (Application.OpenForms.OfType<Frm_ObjectInfo>().Any())
{
Application.OpenForms.OfType<Frm_ObjectInfo>().First().Close();
Properties.Settings.Default.Save();
}
GC.Collect();
GC.WaitForPendingFinalizers();
}
if ((sender as ToolStripButton).Text == "Layers")
{
if (!Application.OpenForms.OfType<Frm_LayersManage>().Any())
{
Frm_LayersManage Newform = new Frm_LayersManage();
Newform.Show();
Application.OpenForms.OfType<Frm_LayersManage>().First().UpdateLayers(null, CurrentProject.layers);
}
}
else
{
if (Application.OpenForms.OfType<Frm_LayersManage>().Any())
{
Application.OpenForms.OfType<Frm_LayersManage>().First().Close();
Properties.Settings.Default.Save();
UpdateScreen = true;
}
GC.Collect();
GC.WaitForPendingFinalizers();
}
if (Properties.Settings.Default.Grip2Enabled && (sender as ToolStripButton).Text == "Insert Grip")
{
gbx_SelectGrip.Visible = true;
}
else
{
gbx_SelectGrip.Visible = false;
}
//SelectedPoints.Clear();
//MousePointList.Clear();
//myIDs.Clear();
//IdxPointsEnt.Clear();
//RatiosLines.Clear();
//CadSource.cuts_tmp.Clear();
//IDAddedCutList.Clear();
//ZoomPort.SetValue(0, 0);
//ZoomPort.SetValue(0, 1);
//ZoomPort.SetValue(0, 2);
//ZoomPort.SetValue(0, 3);
//// Reset index of scrap selected by moving gripper
//idxScrap = -1;
//pnl_OpenTK.Refresh();
//// Se ho evidenziato uno SCRAP , annullo l'evidenziazione.
//if (IdsScrapDisablePath[0] != -1)
//{
// int identifiedScrap = CadSource.IdToIdx_Scrap(IdsScrapDisablePath[0]);
// if (CadSource.scraps[identifiedScrap].GripExists())
// {
// CadSource.scraps[identifiedScrap].Enabled = ScrapAbilitation.Enabled; // Disable clicked scrap
// }
// else
// {
// CadSource.scraps[identifiedScrap].Enabled = ScrapAbilitation.WithoutGrip; // Disable clicked scrap
// }
//}
//numScrap = 0;
//IdsScrapDisablePath = new List<int>() { -1, -1 };
}
else
{
(sender as ToolStripButton).Checked = false;
(sender as ToolStripButton).BackColor = Color.Transparent;
sender_old = new object() { };
if (Application.OpenForms.OfType<frm_Protection>().Any())
{
Application.OpenForms.OfType<frm_Protection>().First().Close();
Properties.Settings.Default.Save();
}
if (Application.OpenForms.OfType<Frm_ObjectInfo>().Any())
{
Application.OpenForms.OfType<Frm_ObjectInfo>().First().Close();
Properties.Settings.Default.Save();
}
if (Application.OpenForms.OfType<Frm_LayersManage>().Any())
{
Application.OpenForms.OfType<Frm_LayersManage>().First().Close();
Properties.Settings.Default.Save();
}
gbx_SelectGrip.Visible = false;
GC.Collect();
GC.WaitForPendingFinalizers();
}
SelectedPoints.Clear();
MousePointList.Clear();
myIDs.Clear();
IdxPointsEnt.Clear();
RatiosLines.Clear();
CurrentProject.cuts_tmp.Clear();
IDAddedCutList.Clear();
ZoomPort.SetValue(0, 0);
ZoomPort.SetValue(0, 1);
ZoomPort.SetValue(0, 2);
ZoomPort.SetValue(0, 3);
// Reset index of scrap selected by moving gripper
idxScrap = -1;
pnl_OpenTK.Refresh();
// Se ho evidenziato uno SCRAP , annullo l'evidenziazione.
if (IdsScrapDisablePath[0] != -1)
{
int identifiedScrap = CurrentProject.IdToIdx_Scrap(IdsScrapDisablePath[0]);
if (CurrentProject.scraps[identifiedScrap].GripExists())
{
CurrentProject.scraps[identifiedScrap].Enabled = ScrapAbilitation.Enabled; // Disable clicked scrap
}
else
{
CurrentProject.scraps[identifiedScrap].Enabled = ScrapAbilitation.WithoutGrip; // Disable clicked scrap
}
}
numScrap = 0;
IdsScrapDisablePath = new List<int>() { -1, -1 };
}
the forms that are opned clicking the buttons are forms where I have set the controlbox at false because I don't want that the user is able to close the forms without clicking again the button that has clicked to open it, but I have found a problem because if the user closes the form in this way (see the picture below) the status of the button remains checked but the form has been closed manually
To solve this problem I have thought to add this method associated to the event closing of my forms this is the code
private void frm_Protection_FormClosed(object sender, FormClosedEventArgs e)
{
////// if user closes manually the window without using the button I have to change the state of the button
Frm_Main f = new Frm_Main();
f = Application.OpenForms.OfType<Frm_Main>().Last();
f.tlsBut_Protection.Checked = false;
}
with this code if I close manually the form the status of the button it becomes false again in the main form
but I have discovered that It causes some problems to my program , one of this problem is that after closing the form if I click again the button it seems that the method associated to the clicking event is not called and the form is not opened I have to click it twice before it works again.
do you know why what do I do in the wrong way???
thanks for your help
To prevent a Form from closing, you can subscribe to the FormClosing event, which fires before the Form closes, and intercept (and cancel) the user's action.
private void frm_Protection_FormClosing(object sender, FormClosingEventArgs e)
{
if (e.CloseReason == CloseReason.UserClosing)
e.Cancel = true;
}
Unfortunately, clicking the "X" (in the Taskbar preview or in the program itself) and calling this.Close() are both treated as "UserClosing" close reasons, so we need to modify it slightly.
Add a property to frm_Protection, and use that to determine whether the Form can be closed:
public bool CanClose { private get; set; }
private void frm_Protection_FormClosing(object sender, FormClosingEventArgs e)
{
e.Cancel = !CanClose;
}
And then only set the property to true when you want to allow the Form to be closed:
var frmP = Application.OpenForms.OfType<frm_Protection>().FirstOrDefault();
if (frmP != null)
{
frmP.CanClose = true;
frmP.Close();
}
I've made a TextBox that retains what you type, and when you click the button associated it gives you a messagebox. When people want to click no, I want the button to change location so people cannot click it so they are forced to click yes,Well the problem is mousehover works just 1 time and does not go back to initial position after i leave my pointer out. Can you help me? Here is the code:
{
MsgBox = new CustomMsgBox();
MsgBox.label1.Text = Text;
MsgBox.button1.Text = btnOK;
MsgBox.button2.Text = btnCancel;
MsgBox.Text = Caption;
result = DialogResult.No;
MsgBox.ShowDialog();
return result;
}
private void button2_Click(object sender, EventArgs e)
{
button2.Location = new Point(25, 25);
}
private void button2_MouseHover(object sender, EventArgs e)
{
button2.Location = new Point(+50, +50);
}
private void button2_MouseLeave(object sender, EventArgs e)
{
button2.Location = new Point(+100, +100);
}
You should not use MouseLeave because when you move the button in the MouseHover, the button will move so the mouse will leave the button area. Meaning the No button will flip from the original position to the new position and back again all the time. What you could do is use the MouseMove to see if the user moved away from the area where the Button2 was originally and then move it back. Or include a non-visible control, like an empty label behind the button2. And set the MouseLeave on the label instead.
Oh and don't forget to set button2.TabStop = false, otherwise the user can use tab to get to the button.
Made a quick and dirty proof of concept for this, hope that helps ;)
public partial class Form1 : Form
{
private Rectangle buttonRectangle;
private bool checkRectangle = false;
public Form1()
{
InitializeComponent();
button2.TabStop = false;
buttonRectangle = button2.ClientRectangle;
buttonRectangle.Location = button2.Location;
}
private void button2_Click(object sender, EventArgs e)
{
button2.Location = new Point(25, 25);
}
private void button2_MouseHover(object sender, EventArgs e)
{
button2.Location = new Point(50, 50);
checkRectangle = true;
}
private void Form1_MouseMove(object sender, MouseEventArgs e)
{
if (!checkRectangle)
{
return;
}
if (!buttonRectangle.Contains(e.X, e.Y))
{
checkRectangle = false;
button2.Location = buttonRectangle.Location;
}
}
}
The buttonRectangle is set based on where the button is found during construction of the form. It has a contains method that can be used to check if a certain point (mouse move) is contained in it.
I also set the button2.TabStop to false so it will no longer be active during tab cycles.
When your hover (can change this to mouse enter, but just used your code) event fires, I set checkRectangle to true. I use this in the mouse move event handler to see if anything should be checked (prevents from doing anything when the mouse is not "over" the button).
If the buttonRectangle and the mouse location are not intersected this means we left the area where the button was, so we can move the button back.
In my C# winforms application, I have a picturebox which some bitmap images can be loaded into it. What I want to do is if user clicks somewhere within picturebox, at the mouse location a small textbox will be appeared and user can add a custom text (note) to the picture.
I know how to write a string into a bitmap file, but I couldnt find a way to POP UP a textbox at mouse location, and automaticly add the text to the image when user wrote something and hit enter key. How this textbox and its properties should be defined?
Thanks.
You could embed a control in a custom popup form, as shown below.
The last argument in the PopupForm constructor specifies the action to take, when the user presses Enter. In this example an anonymous method is specified, setting the title of the form.
Usage
private void pictureBox1_MouseUp(object sender, MouseEventArgs e)
{
// in this case we create a TextBox, but the
// PopupForm can hold any type of control.
TextBox textBox = new TextBox();
Point location = pictureBox1.PointToScreen(e.Location);
PopupForm form = new PopupForm(textBox, location,
() => this.Text = textBox.Text);
form.Show();
}
PopupForm Class
public class PopupForm : Form
{
private Action _onAccept;
private Control _control;
private Point _point;
public PopupForm(Control control, int x, int y, Action onAccept)
: this(control, new Point(x, y), onAccept)
{
}
public PopupForm(Control control, Point point, Action onAccept)
{
if (control == null) throw new ArgumentNullException("control");
this.FormBorderStyle = FormBorderStyle.None;
this.ShowInTaskbar = false;
this.KeyPreview = true;
_point = point;
_control = control;
_onAccept = onAccept;
}
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
this.Controls.Add(_control);
_control.Location = new Point(0, 0);
this.Size = _control.Size;
this.Location = _point;
}
protected override void OnKeyDown(KeyEventArgs e)
{
base.OnKeyDown(e);
if (e.KeyCode == Keys.Enter)
{
_onAccept();
this.Close();
}
else if (e.KeyCode == Keys.Escape)
{
this.Close();
}
}
protected override void OnDeactivate(EventArgs e)
{
base.OnDeactivate(e);
this.Close();
}
}
I suppose, you could create Textbox dynamically on mouse click and use it's BringToFront() method in case is will not appear above the picture box. When the user presses Enter, handle this event, get text from Textbox and remove if if needed.