FlatStyle.System raise strange MouseHover event - c#

With the following code, when i move the mouse into the button and click it, the final value of result is 2 instead of 1.
static int result = 0;
private void button2_MouseHover(object sender, EventArgs e)
{
result++;
}
private void button2_MouseDown(object sender, MouseEventArgs e)
{
button2.FlatStyle = FlatStyle.System;
}
I had tried different setting:
Using MouseEnter instead of MouseHover give result = 1
Only changing of FlatStyle to System will trigger MouseHover one more time after button is clicked.
If the FlatStyle of button is originally FlatStyle.System, give result = 1;
If the FlatStyle is changed to others Style(popup, flat), give result = 1;
So i guess the problem lies with the FlatStyle set to FlatStyle.System, anyone can explains this to me?

This is a fairly common quirk in Winforms. You can visualize what's going with this code:
private void button1_MouseHover(object sender, EventArgs e) {
Console.WriteLine("{0:X}, {1}", button1.Handle, DateTime.Now.TimeOfDay);
result++;
}
Look in the Output window for the displayed diagnostic. On mine:
12780286, 03:04:10.7619696
12780286, 03:04:16.2935137
12780286, 03:04:20.1062178
12780286, 03:04:23.9970596
Click
12845822, 03:04:28.9191911
12845822, 03:04:32.3099880
Note how the Handle property value changed after the click. In other words, the physical Windows window changed. This happened because you changed the FlatStyle property. Winforms implements this by destroying the button control window and recreating it. If you look really close then you can actually see this, the button briefly flickers.
There are a few properties that are implemented like this, they are "heavy" properties that requires the window to be recreated since the style flags changed. Style flags that are passed in the native CreateWindowEx() call. A change in those style flags requires another call to CreateWindowEx().
This tends to have side-effects. Destroying the old window causes its state to be lost. Winforms tries to restore the state of the new window as best it can, but there are a few it cannot get to. The "window was hovered" state is one of them. Clearly the new window has that internal state flag set to false. Which is why you get the MouseHover event again. You'll need to work around this quirk.

Related

How to hide control when click outside it?

I have a WindowForm and some controls on it.
My point is that when I click button "?" on top-right of the datagridview, it will show a picture box and when I click outside the pictureBox, it must invisible.
My MainForm
MyPictureBox
I have searched some topics on this site, but some dont work, some work partly. Like
this.
I also tried:
void pictureBox1_LostFocus(object sender, EventArgs e)
{
if (pictureBox1.Visible)
pictureBox1.Visible = false;
}
But when I click on button2, button3, ... The pictureBox wasn't invisible.
Any solution will be highly appreciated.
I think your pictureBox1 isn't losing focus, cause it never actually GOT focused. Set it to be focused after making it visible.
Oh, I have encountered this before...
I was making a Label that you could double click and it would allow you to edit the Label.Text, like a TextBox. However, I was having problems hooking into the events to know when the user had clicked off the Control and wished to stop editing. I tried Control.LostFocus, and Control.Leave, but nothing. I even got frustrated/desperate and tried some silly ones like Control.Invalidated.
What I ended up having to do was subscribe to the Click event of the Form/Container/Control behind it.
However, putting the responsibility of wiring up this event into the Form that wants to use it is poor design. What you can do, however is to make the constructor to Control class require a reference to the owner/parent/container as a parameter. That way, the requirements are not hidden, they must be satisfied before you can get a object instance, and the control can wired up to the Form.Click within itself, where that logic belongs.
private Form owner;
public EditLabel(Form Owner)
{
this.owner = Owner;
owner.Click += EndEditing;
}
Add this method in designer.cs:
pictureBoxEvent this.MouseLeave += new EventHandler(pictureBox_MouseLeave);
Add this code in cs file:
private void pictureBox_MouseLeave(object sender, EventArgs e)
{
pictureBox1.Visible = false;
}

Got focus and lost focus both event calling repeatedly when handling get and lost focus event of multiple textboxes it goes into infinite loop

Following is my code to handle gotfocus and lostfocus event for all textboxes available in form.
private void Form1_Load(object sender, EventArgs e)
{
foreach (Control c in this.Controls)
{
if (c is TextBox)
{
c.GotFocus += new System.EventHandler(this.txtGotFocus);
c.LostFocus += new System.EventHandler(this.txtLostfocus);
}
}
}
private void txtGotFocus(object sender, EventArgs e)
{
TextBox tb = (TextBox)sender;
if (tb != null)
{
tb.BackColor = Color.Silver;
tb.BorderStyle = BorderStyle.FixedSingle;
}
}
private void txtLostFocus(object sender, EventArgs e)
{
TextBox tb = (TextBox)sender;
if (tb != null)
{
tb.BackColor = Color.White;
tb.BorderStyle = BorderStyle.Fixed3D;
}
}
It works fine with first textbox but when I go to next textbox by pressing tab key it will repeatedly call both events and textbox behave like blinking. After some time error message display in code like:
A callback was made on a garbage collected delegate of type 'System.Windows.Forms!System.Windows.Forms.NativeMethods+WndProc::Invoke'. This may cause application crashes, corruption and data loss. When passing delegates to unmanaged code, they must be kept alive by the managed application until it is guaranteed that they will never be called.
Whats wrong with code? Is there any solution?
c.LostFocus += new System.EventHandler(this.txtLostfocus);
LostFocus is a dangerous event, the MSDN Library article for Control.LostFocus warns about this and strongly recommends to use the Leave event instead. This is something you can see in the designer, drop a TextBox on the form and click the lightning bolt icon in the Properties window. Note how both the GotFocus and LostFocus events are not visible. You must use the Enter and Leave events instead.
Some background on what is going on here. Your program blows up because you assign the BorderStyle property. That's a "difficult" property, it is one that affects the style flag of the window, the one that's passed to the native CreateWindowEx() function. So changing the border style requires Winforms to create the native window again. This is what causes the flicker you see, the text box is destroyed and re-created, then repainted. You see that.
But that has side-effects beyond the flicker, it also causes the low-level GotFocus and LostFocus events to fire. Because the destroyed window of course also loses the focus. This interacts very poorly in your program since your LostFocus event handler changes the BorderStyle again, forcing Winforms to yet again recreate the window. And fire the GotFocus event, you change the BorderStyle yet again. This repeats over and over again, you see the textbox rapidly blinking. This doesn't go on endlessly, after 10,000 times of creating the window, the operating system pulls the plug and doesn't let your program create yet another one. The hard crash on the window procedure is the outcome.
Not a problem with the Enter and Leave events, they don't work from the low-level Windows notification so don't fire when the textbox window is recreated. You can only get rid of the one-time flicker, if it is still bothering you, by not changing the BorderStyle property.
This occurs cause you change the borderstyle. Dont ask me why, i dont know it. If you remove tb.BorderStyle = XXX it will work. I guess(!) changing the borderstyle makes the control loose its fokus (and get it again)
Edit: Hans was faster, with a much better explanation :)

Win32Exception - error creating window handle?

Can you explain why this happens? Here are steps to reproduce the exception:
Drag-n-drop a TextBox on a form. Add any other focusable control such as a button on the form.
Add 2 event handlers for that TextBox as follow:
private void textBox_GotFocus(object sender, EventArgs e){
((TextBox)sender).HideSelection = false; //<-- exception highlighted at here.
}
private void textBox_LostFocus(object sender, EventArgs e){
((TextBox)sender).HideSelection = true;
}
Run the form and first click on the textBox, then click on the button then click on the textBox again, and the exception will throw: "Win32Exception - error creating window handle".
The code is simply to make the HideSelection change accordingly when the textBox gets focused and loses focus.
UPDATE
I don't know why is so volatile, I created another project and now the problem is different, there is no exception but an indefinite loop which makes the textbox flicker, the form doesn't seem to responsive, the CPU usage is consumed up to about 17-20%. The last demo is still opened and that demo still has the Win32Exception thrown. Not a clue at all. Now the code of the two projects are the same but the problems are different.
Well, there are a couple of things to note.
First of all, HideSelection doesn't hide or unhide the selection. It specifies, whether the selection will be hidden (or not) when the control loses focus. So it's pointless to change it when the TextBox becomes focus'ed.
What you're doing in LostFocus is default, btw. Why there is an exception in GotFocus is burried within Windows API, I guess. Maybe some undefined state when HideSelection checks inside a focus changed event whether the TextBox has focus or not or tries to hide the selection which is not shown.
Edit: It's not the Windows API on the first hand, but the framework. It tries to "recreate the handle" in the setter of HideSelection if it is changed (don't know why yet - would have to analyse sources) and seems to fail (don't know why either).
Edit2: Finally there's some problem in Win32 DestroyWindow - which leads to skipping the creation of the new window. Maybe because the old one is "in use" in the focus change events?
Interestingly, as soon as the exception occurs (for me), the LostFocus event is fired, immediately followed by GotFocus which throws another exception a.s.o. thus blocking the GUI. Both assignments to HideSelection throw the exception.
Also, when you click the TextBox, any selection is deselected automatically. This, however, is not the reason for the problem, because the exception is thrown if you change focus by pressing Tab (whose normal behaviour is to restore focus). But it may be related (state problems).
If you actually want to restore the selection, you could do it like this:
int selStart;
int selLen;
void textBox1_LostFocus(object sender, EventArgs e)
{
selStart = textBox1.SelectionStart;
selLen = textBox1.SelectionLength;
}
void textBox1_GotFocus(object sender, EventArgs e)
{
BeginInvoke((Action)(() =>
{
textBox1.SelectionStart = selStart;
textBox1.SelectionLength = selLen;
}));
}
I repro this crash. You can easily see it going wrong by setting breakpoints on your event handlers, note how they fire over and over again before your program bombs. The explanation is a bit long winded, I'll give the short version first. The MSDN documentation for the LostFocus event gives stern warnings, both a Note and a Caution, pointing out that this is a low-level event that is dangerous. These events are also hidden in the Properties window for that reason. Fix your problem by using the Enter and Leave event instead.
The long version: the TextBox.HideSelection property is rather special. It is related to the way some properties on native Windows controls are specified. These controls are created with the CreateWindowEx() winapi function, it takes a dwExStyle and dwStyle argument, flags that specify style options for the window. The HideSelection property is such a style flag, ES_NOHIDESEL.
That presents a problem when you want to change the property. Difficult because it can only be specified when the native control is created. Winforms does something pretty heroic to deal with that limitation, it destroys the native control and re-creates it.
That can have pretty interesting side effects, to put it mildly. Most are not observable, but you do for example see the window on the screen getting destroyed and recreated. That's why it flickers. The core problem with your code is, inevitably, because the native window is getting destroyed it also loses the focus. So the LostFocus event immediately fires, right after you got the GotFocus event. Which does something unfortunate, it again changes the HideSelection property. Which forces Winforms to recreate the native control again.
This repeats over and over again when your GotFocus event handler yet again runs for the new native control. This does eventually end when Windows puts a stop to it and doesn't allow any more native windows to be created, it pulls the plug at 10,000 controls after a while. Which generates the "Error creating window handle" exception.
The Enter and Leave events should always be used for focusing events, they only fire if the user actually moved the focus, and don't fire when it happened because of other reasons, like this one. Also notable is that there's no point at all in changing the HideSelection property like you do, the property only has an affect when TextBox doesn't have the focus. The selection is never hidden when it has the focus. So the proper fix here is remove these event handlers and simply set the HideSelection property to True in the Properties window. The default value.
Cant reproduce this works for me:
private void textBox1_Leave(object sender, EventArgs e)
{
((TextBox)sender).HideSelection = false;
}
private void textBox1_Enter(object sender, EventArgs e)
{
((TextBox)sender).HideSelection = true;
}

ToolStripButton "Reset Appearance" / "Fire Leave Event"

I have a ToolStripButton that performs an action. The user clicks the button and the button is disabled to prevent the action being performed twice. After the action is complete the button is re-enabled. It all works perfectly...except:
Because the button is disabled it does not fire the "MouseLeave" event and as a result the appearance of the button is not updated. To be absolutely clear, when the mouse enters a ToolStripButton the button is highlighted in orange (by default) with a black box around it. This highlight is not being removed when I re-enable the button. The mouse cursor is, by this time, long gone from the control. Mousing over the button naturally fixes the button by redrawing it.
What I would like to do would be some method on the ToolStripButton that "resets" its appearance. Such a method may even exist on the ToolStrip, but despite searching I have been unable to find anything like this.
As an alternative I could fire the "Mouse Leave" event on the button directly. As far as I know there is no way to easily do this in C# .NET.
Any advice at this point in time would be most appreciated, naturally I don't want to tear up my application and replace the tool strip.
Update:
I reproduced your problem, trying figuring out!
I didn't get a better way other than reset the style in the click event
private void toolStripButton1_Click(object sender, EventArgs e)
{
toolStripButton1.BackColor = Color.FromKnownColor(KnownColor.Control);
toolStripButton1.Enabled = false;
}
private void toolStripButton1_MouseEnter(object sender, EventArgs e)
{
toolStripButton1.BackColor = Color.Red;
}
private void toolStripButton1_MouseLeave(object sender, EventArgs e)
{
toolStripButton1.BackColor = Color.FromKnownColor(KnownColor.Control);
}
Hope this helps!
Have you tried Control.Invalidate()?
from MSDN: Invalidates the entire surface of the control and causes the control to be redrawn.
I had the same problem. I "fixed" it by hiding and then showing back the ToolStripButton using the Visible property after the task was complete.
Before disabling ToolStrip or ToolStripItem:
private void RemoveHighlightFromToolStrip(ToolStrip toolStrip)
{
foreach (ToolStripItem item in toolStrip.Items)
{
if (item.Pressed || item.Selected)
{
item.Visible = false;
item.Visible = true;
}
}
}
also you can just hide and show entire ToolStrip, but this may affect other controls in your form (i.e. if you have some docked DataGridView it would be redrawn)

Is there any way to detect a mouseclick outside a user control?

I'm creating a custom dropdown box, and I want to register when the mouse is clicked outside the dropdown box, in order to hide it. Is it possible to detect a click outside a control? or should I make some mechanism on the containing form and check for mouseclick when any dropdownbox is open?
So I finally understand that you only want it to close when the user clicks outside of it. In that case, the Leave event should work just fine... For some reason, I got the impression you wanted it to close whenever they moved the mouse outside of your custom dropdown. The Leave event is raised whenever your control loses the focus, and if the user clicks on something else, it will certainly lose focus as the thing they clicked on gains the focus.
The documentation also says that this event cascades up and down the control chain as necessary:
The Enter and Leave events are hierarchical and will cascade up and down the parent chain until the appropriate control is reached. For example, assume you have a Form with two GroupBox controls, and each GroupBox control has one TextBox control. When the caret is moved from one TextBox to the other, the Leave event is raised for the TextBox and GroupBox, and the Enter event is raised for the other GroupBox and TextBox.
Overriding your UserControl's OnLeave method is the best way to handle this:
protected override void OnLeave(EventArgs e)
{
// Call the base class
base.OnLeave(e);
// When this control loses the focus, close it
this.Hide();
}
And then for testing purposes, I created a form that shows the drop-down UserControl on command:
public partial class Form1 : Form
{
private UserControl1 customDropDown;
public Form1()
{
InitializeComponent();
// Create the user control
customDropDown = new UserControl1();
// Add it to the form's Controls collection
Controls.Add(customDropDown);
customDropDown.Hide();
}
private void button1_Click(object sender, EventArgs e)
{
// Display the user control
customDropDown.Show();
customDropDown.BringToFront(); // display in front of other controls
customDropDown.Select(); // make sure it gets the focus
}
}
Everything works perfectly with the above code, except for one thing: if the user clicks on a blank area of the form, the UserControl doesn't close. Hmm, why not? Well, because the form itself doesn't want the focus. Only controls can get the focus, and we didn't click on a control. And because nothing else stole the focus, the Leave event never got raised, meaning that the UserControl didn't know it was supposed to close itself.
If you need the UserControl to close itself when the user clicks on a blank area in the form, you need some special case handling for that. Since you say that you're only concerned about clicks, you can just handle the Click event for the form, and set the focus to a different control:
protected override void OnClick(EventArgs e)
{
// Call the base class
base.OnClick(e);
// See if our custom drop-down is visible
if (customDropDown.Visible)
{
// Set the focus to a different control on the form,
// which will force the drop-down to close
this.SelectNextControl(customDropDown, true, true, true, true);
}
}
Yes, this last part feels like a hack. The better solution, as others have mentioned, is to use the SetCapture function to instruct Windows to capture the mouse over your UserControl's window. The control's Capture property provides an even simpler way to do the same thing.
Technically, you'll need to p/invoke SetCapture() in order to receive click events that happen outside of your control.
But in your case, handling the Leave event, as #Martin suggests, should be sufficient.
EDIT: While looking for an usage example for SetCapture(), I came across the Control.Capture property, of which I was not aware. Using that property means you won't have to p/invoke anything, which is always a good thing in my book.
So, you'll have to set Capture to true when showing the dropdown, then determine if the mouse pointer lies inside the control in your click event handler and, if it doesn't, set Capture to false and close the dropdown.
UPDATE:
You can also use the Control.Focused property to determine if the control has got or lost focus when using a keyboard or mouse instead of using the Capture with the same example provided in the MSDN Capture page.
Handle the Form's MouseDown event, or override the Form's OnMouseDown
method:
enter code here
And then:
protected override void OnMouseDown(MouseEventArgs e)
{
if (!theListBox.Bounds.Contains(e.Location))
{
theListBox.Visible = false;
}
}
The Contains method old System.Drawing.Rectangle can be used to indicate if
a point is contained inside a rectangle. The Bounds property of a Control is
the outer Rectangle defined by the edges of the Control. The Location
property of the MouseEventArgs is the Point relative to the Control which
received the MouseDown event. The Bounds property of a Control in a Form is
relative to the Form.
You are probably looking for the leave event:
http://msdn.microsoft.com/en-us/library/system.windows.forms.control.leave.aspx
Leave occurs when the input focus leaves the control.
I just wanted to share this. It is probably not a good way of doing it that way, but looks like it works for drop down panel that closes on fake "MouseLeave", I tried to hide it on Panel MouseLeave but it does not work because moving from panel to button leaves the panel because the button is not the panel itself. Probably there is better way of doing this but I am sharing this because I used about 7 hours figuring out how to get it to work. Thanks to #FTheGodfather
But it works only if the mouse moves on the form. If there is a panel this will not work.
private void click_to_show_Panel_button_MouseDown(object sender, MouseEventArgs e)
{
item_panel1.Visible = true; //Menu Panel
}
private void Form1_MouseMove(object sender, MouseEventArgs e)
{
if (!item_panel1.Bounds.Contains(e.Location))
{
item_panel1.Visible = false; // Menu panel
}
}
I've done this myself, and this is how I did it.
When the drop down is opened, register a click event on the control's parent form:
this.Form.Click += new EventHandler(CloseDropDown);
But this only takes you half the way. You probably want your drop down to close also when the current window gets deactivated. The most reliable way of detecting this has for me been through a timer that checks which window is currently active:
[System.Runtime.InteropServices.DllImport("user32.dll")]
static extern IntPtr GetForegroundWindow();
and
var timer = new Timer();
timer.Interval = 100;
timer.Tick += (sender, args) =>
{
IntPtr f = GetForegroundWindow();
if (this.Form == null || f != this.Form.Handle)
{
CloseDropDown();
}
};
You should of course only let the timer run when the drop down is visible. Also, there's probably a few other events on the parent form you'd want to register when the drop down is opened:
this.Form.LocationChanged += new EventHandler(CloseDropDown);
this.Form.SizeChanged += new EventHandler(CloseDropDown);
Just don't forget to unregister all these events in the CloseDropDown method :)
EDIT:
I forgot, you should also register the Leave event on you control to see if another control gets activated/clicked:
this.Leave += new EventHandler(CloseDropDown);
I think I've got it now, this should cover all bases. Let me know if I'm missing something.
If you have Form, you can simply use Deactivate event just like this :
protected override void OnDeactivate(EventArgs e)
{
this.Dispose();
}

Categories

Resources