How to manage lost focus across a group of WinForms controls?
up vote
0
down vote
favorite
I'm trying to reliably detect when focus leaves a group of controls. So if I tab or click between two controls in the group, that's considered not leaving, but as soon as I click on a control outside the group, it raises an event. Here's how I'm currently doing it:
public class LeaveTracker
{
private HashSet<Control> trackedControls = new HashSet<Control>();
public void Track(Control ctrl)
{
ctrl.Leave += Ctrl_Leave;
trackedControls.Add(ctrl);
}
private void Ctrl_Leave(object sender, EventArgs e)
{
foreach (var ctrl in trackedControls)
if (ctrl.ContainsFocus)
return;
if (LeftControlGroup != null)
LeftControlGroup(this, EventArgs.Empty);
}
public event EventHandler LeftControlGroup;
}
Sometimes it works perfectly, but occasionally it seems to stop raising events. Is there a more reliable way to do this?
c# winforms
|
show 5 more comments
up vote
0
down vote
favorite
I'm trying to reliably detect when focus leaves a group of controls. So if I tab or click between two controls in the group, that's considered not leaving, but as soon as I click on a control outside the group, it raises an event. Here's how I'm currently doing it:
public class LeaveTracker
{
private HashSet<Control> trackedControls = new HashSet<Control>();
public void Track(Control ctrl)
{
ctrl.Leave += Ctrl_Leave;
trackedControls.Add(ctrl);
}
private void Ctrl_Leave(object sender, EventArgs e)
{
foreach (var ctrl in trackedControls)
if (ctrl.ContainsFocus)
return;
if (LeftControlGroup != null)
LeftControlGroup(this, EventArgs.Empty);
}
public event EventHandler LeftControlGroup;
}
Sometimes it works perfectly, but occasionally it seems to stop raising events. Is there a more reliable way to do this?
c# winforms
If you can place these controls on a panel, you can just use theLeave
event of the panel instead.
– Ahmed Abdelhameed
Nov 21 at 21:25
Have you tried putting the sets of controls inside another custom User Control? I believe that User Control will have its own Leave event
– JayV
Nov 21 at 21:26
There are other controls that are intermixed that aren't part of the group.
– Bryce Wagner
Nov 21 at 21:35
It's really hard to picture how that is supposed to work. You have a screen shot of your form that shows these "groups"?
– LarsTech
Nov 21 at 21:36
@AhmedAbdelhameed This question is in no way a duplicate of the mouse leaving event. This is about focus, not mouse pointer.
– Bryce Wagner
Nov 21 at 21:37
|
show 5 more comments
up vote
0
down vote
favorite
up vote
0
down vote
favorite
I'm trying to reliably detect when focus leaves a group of controls. So if I tab or click between two controls in the group, that's considered not leaving, but as soon as I click on a control outside the group, it raises an event. Here's how I'm currently doing it:
public class LeaveTracker
{
private HashSet<Control> trackedControls = new HashSet<Control>();
public void Track(Control ctrl)
{
ctrl.Leave += Ctrl_Leave;
trackedControls.Add(ctrl);
}
private void Ctrl_Leave(object sender, EventArgs e)
{
foreach (var ctrl in trackedControls)
if (ctrl.ContainsFocus)
return;
if (LeftControlGroup != null)
LeftControlGroup(this, EventArgs.Empty);
}
public event EventHandler LeftControlGroup;
}
Sometimes it works perfectly, but occasionally it seems to stop raising events. Is there a more reliable way to do this?
c# winforms
I'm trying to reliably detect when focus leaves a group of controls. So if I tab or click between two controls in the group, that's considered not leaving, but as soon as I click on a control outside the group, it raises an event. Here's how I'm currently doing it:
public class LeaveTracker
{
private HashSet<Control> trackedControls = new HashSet<Control>();
public void Track(Control ctrl)
{
ctrl.Leave += Ctrl_Leave;
trackedControls.Add(ctrl);
}
private void Ctrl_Leave(object sender, EventArgs e)
{
foreach (var ctrl in trackedControls)
if (ctrl.ContainsFocus)
return;
if (LeftControlGroup != null)
LeftControlGroup(this, EventArgs.Empty);
}
public event EventHandler LeftControlGroup;
}
Sometimes it works perfectly, but occasionally it seems to stop raising events. Is there a more reliable way to do this?
c# winforms
c# winforms
edited Nov 21 at 21:29
Ahmed Abdelhameed
5,48971943
5,48971943
asked Nov 21 at 21:17
Bryce Wagner
429210
429210
If you can place these controls on a panel, you can just use theLeave
event of the panel instead.
– Ahmed Abdelhameed
Nov 21 at 21:25
Have you tried putting the sets of controls inside another custom User Control? I believe that User Control will have its own Leave event
– JayV
Nov 21 at 21:26
There are other controls that are intermixed that aren't part of the group.
– Bryce Wagner
Nov 21 at 21:35
It's really hard to picture how that is supposed to work. You have a screen shot of your form that shows these "groups"?
– LarsTech
Nov 21 at 21:36
@AhmedAbdelhameed This question is in no way a duplicate of the mouse leaving event. This is about focus, not mouse pointer.
– Bryce Wagner
Nov 21 at 21:37
|
show 5 more comments
If you can place these controls on a panel, you can just use theLeave
event of the panel instead.
– Ahmed Abdelhameed
Nov 21 at 21:25
Have you tried putting the sets of controls inside another custom User Control? I believe that User Control will have its own Leave event
– JayV
Nov 21 at 21:26
There are other controls that are intermixed that aren't part of the group.
– Bryce Wagner
Nov 21 at 21:35
It's really hard to picture how that is supposed to work. You have a screen shot of your form that shows these "groups"?
– LarsTech
Nov 21 at 21:36
@AhmedAbdelhameed This question is in no way a duplicate of the mouse leaving event. This is about focus, not mouse pointer.
– Bryce Wagner
Nov 21 at 21:37
If you can place these controls on a panel, you can just use the
Leave
event of the panel instead.– Ahmed Abdelhameed
Nov 21 at 21:25
If you can place these controls on a panel, you can just use the
Leave
event of the panel instead.– Ahmed Abdelhameed
Nov 21 at 21:25
Have you tried putting the sets of controls inside another custom User Control? I believe that User Control will have its own Leave event
– JayV
Nov 21 at 21:26
Have you tried putting the sets of controls inside another custom User Control? I believe that User Control will have its own Leave event
– JayV
Nov 21 at 21:26
There are other controls that are intermixed that aren't part of the group.
– Bryce Wagner
Nov 21 at 21:35
There are other controls that are intermixed that aren't part of the group.
– Bryce Wagner
Nov 21 at 21:35
It's really hard to picture how that is supposed to work. You have a screen shot of your form that shows these "groups"?
– LarsTech
Nov 21 at 21:36
It's really hard to picture how that is supposed to work. You have a screen shot of your form that shows these "groups"?
– LarsTech
Nov 21 at 21:36
@AhmedAbdelhameed This question is in no way a duplicate of the mouse leaving event. This is about focus, not mouse pointer.
– Bryce Wagner
Nov 21 at 21:37
@AhmedAbdelhameed This question is in no way a duplicate of the mouse leaving event. This is about focus, not mouse pointer.
– Bryce Wagner
Nov 21 at 21:37
|
show 5 more comments
1 Answer
1
active
oldest
votes
up vote
-1
down vote
Apparently, the Leave
event gets triggered before the control actually loses focus. You can confirm that by checking the value of (sender as Control).ContainsFocus
in your event handler.
Either use the Control.LostFocus
event instead:
private void Ctrl_LostFocus(object sender, EventArgs e)
{
foreach (var ctrl in trackedControls)
if (ctrl.ContainsFocus)
return;
Debug.Print("The tracked controls are not focused anymore!");
//..
}
Or if you want to use the Leave
event, you can wait for a little while until the control actually loses focus. This also worked fine for me:
private async void Ctrl_Leave(object sender, EventArgs e)
{
while ((sender as Control).ContainsFocus)
await Task.Delay(50);
foreach (var ctrl in trackedControls)
if (ctrl.ContainsFocus)
return;
Debug.Print("The tracked controls are not focused anymore!");
//..
}
A better alternative to using await
is to wrap the code under Ctrl_Leave
in a BeginInvoke delegate as suggested by Hans Passant:
private void Ctrl_Leave(object sender, EventArgs e)
{
BeginInvoke((Action)(() =>
{
foreach (var ctrl in trackedControls)
if (ctrl.ContainsFocus)
return;
Debug.Print("The tracked controls are not focused anymore!");
//..
}));
}
The Leave event replaced that event. Read the notes in your link.
– LarsTech
Nov 21 at 21:48
@LarsTech I'm aware of that but it doesn't work for this particular case. I was in the process of updating my answer with more info + a workaround for using the Leave event when you left your comment. Please, check the updated answer.
– Ahmed Abdelhameed
Nov 21 at 21:56
1
It is getting somewhere, but is still too awkward. All you need to do is use this.BeginInvoke() in the event handler. That makes the invoked code run later, after the focus has changed and the ContainsFocus property is accurate. You get it now by accident from await.
– Hans Passant
Nov 21 at 22:22
@HansPassant How is it 'by accident' if the await is repeated untilContainsFocus
is false? I agree with you though that BeginInvoke is a better idea. I actually learned to use it in such situations from some of your answers but for some reason, didn't think of it this time :D
– Ahmed Abdelhameed
Nov 21 at 22:35
1
The awaiter for an async void event handler uses BeginInvoke() to run the continuation. That kind of accident. Mostly a fatal accident to the programmer's brain region :)
– Hans Passant
Nov 21 at 22:39
|
show 2 more comments
1 Answer
1
active
oldest
votes
1 Answer
1
active
oldest
votes
active
oldest
votes
active
oldest
votes
up vote
-1
down vote
Apparently, the Leave
event gets triggered before the control actually loses focus. You can confirm that by checking the value of (sender as Control).ContainsFocus
in your event handler.
Either use the Control.LostFocus
event instead:
private void Ctrl_LostFocus(object sender, EventArgs e)
{
foreach (var ctrl in trackedControls)
if (ctrl.ContainsFocus)
return;
Debug.Print("The tracked controls are not focused anymore!");
//..
}
Or if you want to use the Leave
event, you can wait for a little while until the control actually loses focus. This also worked fine for me:
private async void Ctrl_Leave(object sender, EventArgs e)
{
while ((sender as Control).ContainsFocus)
await Task.Delay(50);
foreach (var ctrl in trackedControls)
if (ctrl.ContainsFocus)
return;
Debug.Print("The tracked controls are not focused anymore!");
//..
}
A better alternative to using await
is to wrap the code under Ctrl_Leave
in a BeginInvoke delegate as suggested by Hans Passant:
private void Ctrl_Leave(object sender, EventArgs e)
{
BeginInvoke((Action)(() =>
{
foreach (var ctrl in trackedControls)
if (ctrl.ContainsFocus)
return;
Debug.Print("The tracked controls are not focused anymore!");
//..
}));
}
The Leave event replaced that event. Read the notes in your link.
– LarsTech
Nov 21 at 21:48
@LarsTech I'm aware of that but it doesn't work for this particular case. I was in the process of updating my answer with more info + a workaround for using the Leave event when you left your comment. Please, check the updated answer.
– Ahmed Abdelhameed
Nov 21 at 21:56
1
It is getting somewhere, but is still too awkward. All you need to do is use this.BeginInvoke() in the event handler. That makes the invoked code run later, after the focus has changed and the ContainsFocus property is accurate. You get it now by accident from await.
– Hans Passant
Nov 21 at 22:22
@HansPassant How is it 'by accident' if the await is repeated untilContainsFocus
is false? I agree with you though that BeginInvoke is a better idea. I actually learned to use it in such situations from some of your answers but for some reason, didn't think of it this time :D
– Ahmed Abdelhameed
Nov 21 at 22:35
1
The awaiter for an async void event handler uses BeginInvoke() to run the continuation. That kind of accident. Mostly a fatal accident to the programmer's brain region :)
– Hans Passant
Nov 21 at 22:39
|
show 2 more comments
up vote
-1
down vote
Apparently, the Leave
event gets triggered before the control actually loses focus. You can confirm that by checking the value of (sender as Control).ContainsFocus
in your event handler.
Either use the Control.LostFocus
event instead:
private void Ctrl_LostFocus(object sender, EventArgs e)
{
foreach (var ctrl in trackedControls)
if (ctrl.ContainsFocus)
return;
Debug.Print("The tracked controls are not focused anymore!");
//..
}
Or if you want to use the Leave
event, you can wait for a little while until the control actually loses focus. This also worked fine for me:
private async void Ctrl_Leave(object sender, EventArgs e)
{
while ((sender as Control).ContainsFocus)
await Task.Delay(50);
foreach (var ctrl in trackedControls)
if (ctrl.ContainsFocus)
return;
Debug.Print("The tracked controls are not focused anymore!");
//..
}
A better alternative to using await
is to wrap the code under Ctrl_Leave
in a BeginInvoke delegate as suggested by Hans Passant:
private void Ctrl_Leave(object sender, EventArgs e)
{
BeginInvoke((Action)(() =>
{
foreach (var ctrl in trackedControls)
if (ctrl.ContainsFocus)
return;
Debug.Print("The tracked controls are not focused anymore!");
//..
}));
}
The Leave event replaced that event. Read the notes in your link.
– LarsTech
Nov 21 at 21:48
@LarsTech I'm aware of that but it doesn't work for this particular case. I was in the process of updating my answer with more info + a workaround for using the Leave event when you left your comment. Please, check the updated answer.
– Ahmed Abdelhameed
Nov 21 at 21:56
1
It is getting somewhere, but is still too awkward. All you need to do is use this.BeginInvoke() in the event handler. That makes the invoked code run later, after the focus has changed and the ContainsFocus property is accurate. You get it now by accident from await.
– Hans Passant
Nov 21 at 22:22
@HansPassant How is it 'by accident' if the await is repeated untilContainsFocus
is false? I agree with you though that BeginInvoke is a better idea. I actually learned to use it in such situations from some of your answers but for some reason, didn't think of it this time :D
– Ahmed Abdelhameed
Nov 21 at 22:35
1
The awaiter for an async void event handler uses BeginInvoke() to run the continuation. That kind of accident. Mostly a fatal accident to the programmer's brain region :)
– Hans Passant
Nov 21 at 22:39
|
show 2 more comments
up vote
-1
down vote
up vote
-1
down vote
Apparently, the Leave
event gets triggered before the control actually loses focus. You can confirm that by checking the value of (sender as Control).ContainsFocus
in your event handler.
Either use the Control.LostFocus
event instead:
private void Ctrl_LostFocus(object sender, EventArgs e)
{
foreach (var ctrl in trackedControls)
if (ctrl.ContainsFocus)
return;
Debug.Print("The tracked controls are not focused anymore!");
//..
}
Or if you want to use the Leave
event, you can wait for a little while until the control actually loses focus. This also worked fine for me:
private async void Ctrl_Leave(object sender, EventArgs e)
{
while ((sender as Control).ContainsFocus)
await Task.Delay(50);
foreach (var ctrl in trackedControls)
if (ctrl.ContainsFocus)
return;
Debug.Print("The tracked controls are not focused anymore!");
//..
}
A better alternative to using await
is to wrap the code under Ctrl_Leave
in a BeginInvoke delegate as suggested by Hans Passant:
private void Ctrl_Leave(object sender, EventArgs e)
{
BeginInvoke((Action)(() =>
{
foreach (var ctrl in trackedControls)
if (ctrl.ContainsFocus)
return;
Debug.Print("The tracked controls are not focused anymore!");
//..
}));
}
Apparently, the Leave
event gets triggered before the control actually loses focus. You can confirm that by checking the value of (sender as Control).ContainsFocus
in your event handler.
Either use the Control.LostFocus
event instead:
private void Ctrl_LostFocus(object sender, EventArgs e)
{
foreach (var ctrl in trackedControls)
if (ctrl.ContainsFocus)
return;
Debug.Print("The tracked controls are not focused anymore!");
//..
}
Or if you want to use the Leave
event, you can wait for a little while until the control actually loses focus. This also worked fine for me:
private async void Ctrl_Leave(object sender, EventArgs e)
{
while ((sender as Control).ContainsFocus)
await Task.Delay(50);
foreach (var ctrl in trackedControls)
if (ctrl.ContainsFocus)
return;
Debug.Print("The tracked controls are not focused anymore!");
//..
}
A better alternative to using await
is to wrap the code under Ctrl_Leave
in a BeginInvoke delegate as suggested by Hans Passant:
private void Ctrl_Leave(object sender, EventArgs e)
{
BeginInvoke((Action)(() =>
{
foreach (var ctrl in trackedControls)
if (ctrl.ContainsFocus)
return;
Debug.Print("The tracked controls are not focused anymore!");
//..
}));
}
edited Nov 21 at 22:52
answered Nov 21 at 21:46
Ahmed Abdelhameed
5,48971943
5,48971943
The Leave event replaced that event. Read the notes in your link.
– LarsTech
Nov 21 at 21:48
@LarsTech I'm aware of that but it doesn't work for this particular case. I was in the process of updating my answer with more info + a workaround for using the Leave event when you left your comment. Please, check the updated answer.
– Ahmed Abdelhameed
Nov 21 at 21:56
1
It is getting somewhere, but is still too awkward. All you need to do is use this.BeginInvoke() in the event handler. That makes the invoked code run later, after the focus has changed and the ContainsFocus property is accurate. You get it now by accident from await.
– Hans Passant
Nov 21 at 22:22
@HansPassant How is it 'by accident' if the await is repeated untilContainsFocus
is false? I agree with you though that BeginInvoke is a better idea. I actually learned to use it in such situations from some of your answers but for some reason, didn't think of it this time :D
– Ahmed Abdelhameed
Nov 21 at 22:35
1
The awaiter for an async void event handler uses BeginInvoke() to run the continuation. That kind of accident. Mostly a fatal accident to the programmer's brain region :)
– Hans Passant
Nov 21 at 22:39
|
show 2 more comments
The Leave event replaced that event. Read the notes in your link.
– LarsTech
Nov 21 at 21:48
@LarsTech I'm aware of that but it doesn't work for this particular case. I was in the process of updating my answer with more info + a workaround for using the Leave event when you left your comment. Please, check the updated answer.
– Ahmed Abdelhameed
Nov 21 at 21:56
1
It is getting somewhere, but is still too awkward. All you need to do is use this.BeginInvoke() in the event handler. That makes the invoked code run later, after the focus has changed and the ContainsFocus property is accurate. You get it now by accident from await.
– Hans Passant
Nov 21 at 22:22
@HansPassant How is it 'by accident' if the await is repeated untilContainsFocus
is false? I agree with you though that BeginInvoke is a better idea. I actually learned to use it in such situations from some of your answers but for some reason, didn't think of it this time :D
– Ahmed Abdelhameed
Nov 21 at 22:35
1
The awaiter for an async void event handler uses BeginInvoke() to run the continuation. That kind of accident. Mostly a fatal accident to the programmer's brain region :)
– Hans Passant
Nov 21 at 22:39
The Leave event replaced that event. Read the notes in your link.
– LarsTech
Nov 21 at 21:48
The Leave event replaced that event. Read the notes in your link.
– LarsTech
Nov 21 at 21:48
@LarsTech I'm aware of that but it doesn't work for this particular case. I was in the process of updating my answer with more info + a workaround for using the Leave event when you left your comment. Please, check the updated answer.
– Ahmed Abdelhameed
Nov 21 at 21:56
@LarsTech I'm aware of that but it doesn't work for this particular case. I was in the process of updating my answer with more info + a workaround for using the Leave event when you left your comment. Please, check the updated answer.
– Ahmed Abdelhameed
Nov 21 at 21:56
1
1
It is getting somewhere, but is still too awkward. All you need to do is use this.BeginInvoke() in the event handler. That makes the invoked code run later, after the focus has changed and the ContainsFocus property is accurate. You get it now by accident from await.
– Hans Passant
Nov 21 at 22:22
It is getting somewhere, but is still too awkward. All you need to do is use this.BeginInvoke() in the event handler. That makes the invoked code run later, after the focus has changed and the ContainsFocus property is accurate. You get it now by accident from await.
– Hans Passant
Nov 21 at 22:22
@HansPassant How is it 'by accident' if the await is repeated until
ContainsFocus
is false? I agree with you though that BeginInvoke is a better idea. I actually learned to use it in such situations from some of your answers but for some reason, didn't think of it this time :D– Ahmed Abdelhameed
Nov 21 at 22:35
@HansPassant How is it 'by accident' if the await is repeated until
ContainsFocus
is false? I agree with you though that BeginInvoke is a better idea. I actually learned to use it in such situations from some of your answers but for some reason, didn't think of it this time :D– Ahmed Abdelhameed
Nov 21 at 22:35
1
1
The awaiter for an async void event handler uses BeginInvoke() to run the continuation. That kind of accident. Mostly a fatal accident to the programmer's brain region :)
– Hans Passant
Nov 21 at 22:39
The awaiter for an async void event handler uses BeginInvoke() to run the continuation. That kind of accident. Mostly a fatal accident to the programmer's brain region :)
– Hans Passant
Nov 21 at 22:39
|
show 2 more comments
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53420594%2fhow-to-manage-lost-focus-across-a-group-of-winforms-controls%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
If you can place these controls on a panel, you can just use the
Leave
event of the panel instead.– Ahmed Abdelhameed
Nov 21 at 21:25
Have you tried putting the sets of controls inside another custom User Control? I believe that User Control will have its own Leave event
– JayV
Nov 21 at 21:26
There are other controls that are intermixed that aren't part of the group.
– Bryce Wagner
Nov 21 at 21:35
It's really hard to picture how that is supposed to work. You have a screen shot of your form that shows these "groups"?
– LarsTech
Nov 21 at 21:36
@AhmedAbdelhameed This question is in no way a duplicate of the mouse leaving event. This is about focus, not mouse pointer.
– Bryce Wagner
Nov 21 at 21:37