What is the difference(s) between GotFocus and GotKeyboardFocus -and similarly LostFocus and LostKeyboardFocus?
Sorry for the simple question, but, I googled it and read a lot of blog posts, but I'm still confused. It seems nobody knows exactly what is the difference ):
UPDATE:
My usage:
I am creating a custom control by extending Control class. Something like ComboBox but with some other effects. I'm trying to open and close a Popup by setting a property: IsDropDownOpen just like a ComboBox through the GotFocus and LostFocus events. I don't want to Popup get closed, when I Alt+Tabed the windows, but get closed when I click on a Button for example or I go to a TextBox. I did:
private static void OnGotFocusHandler(object sender, RoutedEventArgs e) {
if (e.Handled)
return;
((SearchBox)sender).IsDropDownOpen = true;
e.Handled = true;
}
private static void OnLostFocusHandler(object sender, RoutedEventArgs e) {
if (e.Handled)
return;
((SearchBox)sender).IsDropDownOpen = false;
e.Handled = true;
}
The GotFocus works. But the Lost one didn't. If I do the Lost stuff in LostKeyboardFocus then when I Alt+Tab the windows, or Window goes to inactive, then the method get called, while I don't want. How can I solve it?
MSDN has an overview of focus, but I'll try to explain it here.
WPF has 2 concepts regarding focus. There is the physical keyboard focus, and there is logical focus. Only one element can have keyboard focus (and if the application isn't the active application, no element will have keyboard focus).
Multiple items can have logical focus. In fact, you can create new "focus scopes". As per MSDN:
When keyboard focus leaves a focus scope, the focused element will lose keyboard focus but will retain logical focus. When keyboard focus returns to the focus scope, the focused element will obtain keyboard focus. This allows for keyboard focus to be changed between multiple focus scopes but ensures that the focused element in the focus scope regains keyboard focus when focus returns to the focus scope.
You can define your own focus scope on an element (typically a Panel) by setting FocusManager.IsFocusScope="True". The controls in WPF that are focus scopes by default are Window, MenuItem, ToolBar, and ContextMenu.
This makes sense if you think about having multiple Windows in your application. When you Alt-Tab between them, you expect your keyboard focus to return to the same place it was the last time the Window had focus. By keeping keyboard focus and logical focus separate, you can achieve this.
Related
I've got TextBoxes in a C# form. The user enters data, and then when they leave the control (almost always by hitting Tab), I check the data to make sure it's valid. If it is invalid, I want to highlight their text so they can immediately fix it rather than having to click it.
Right now, on Control.Leave, I validate their entry. This works just fine. However, since they hit Tab, right after they dismiss the error message, it goes on to the next object, even though I've got ((TextBox)sender).Focus();
How can I have the above line fire after the form Tabs to the next control.
You may want to look into Control.CausesValidation property
http://msdn.microsoft.com/en-us/library/system.windows.forms.control.causesvalidation(v=vs.110).aspx
You can validate the control prior to the user leaving focus rather than waiting on Focus moving itself.
And here's MSDN documentation for Control.Validating event, does a good job at laying out the sequence of events when gaining / losing focus of a Control.
http://msdn.microsoft.com/en-us/library/system.windows.forms.control.validating(v=vs.110).aspx
Notice how Control.Validating and Control.Validated are launched prior to Control.LostFocus. You can perform your validation step prior to allowing the user to lose focus of your Textbox.
There's also a pretty good previous answer on stackoverflow.com which outlines how to do this: C# Validating input for textbox on winforms
If you handle the Control.Validating event, setting e.Cancel to true will stop the change of focus from occurring.
Note that this method will also stop buttons from working, so you may need to set Control.CausesValidation to false on certain buttons.
You will also need the following snippet on the main form to allow the close button to work:
protected override void OnFormClosing(FormClosingEventArgs e) {
e.Cancel = false;
base.OnFormClosing(e);
}
Try using the LostFocus event on the TextBox to Focus it again
The WinRT TextBox control has a delete button that shows up when the control has focus and there is text in it.
When the control loses focus by tapping outside of it or tabbing outside of it, the delete button disappears.
When the control loses focus by tapping in a different app (two apps are up in splitscreen), the delete button doesn't disappear.
Is there a way to detect the different lost focus events? Nothing from the sender or RoutedEventArgs is different in the two cases where the control loses focus.
You can handle Window.Activated to check if the whole app has lost focus:
This event occurs when a Window has been activated or deactivated by
the system. An app can determine what the status of the Window
activation is by checking the
WindowActivatedEventArgs.WindowActivationState property. A Window
could be visible on screen but not be active (for example, in snapped
apps). Additionally, if any other parts of the system takes focus away
from the window, this event will occur. This could happen as a result
of user interaction or code, and the WindowActivationState will
indicate which action has taken place.
http://msdn.microsoft.com/en-us/library/windows/apps/windows.ui.xaml.window.activated.aspx
I want to do a map editor for a game. Its a program that will have Windows Forms UI (like propertyGrid to edit object's properties) but it will also have a panel on which map will be drawn.
What i want:
When focus is on the panel with the map, i'd like to use keyboard to move map around (arrow keys), add objects (number keys) etc. When focus isnt on this panel, i'd like the buttons to work as normal in windows forms - allow to tab between controls etc.
My form looks like this:
It has a ToolStripControl that has a menuStrip (for main menu) and a statusStrip (for status bar). In the middle of the form (or toolstripcontrol), SplitControl is docked (dock=fill) that has two panels. Panel 1 has the PanelMap - a Panel that displays the map, Panel 2 has all other stuff like propertygrid, tabcontrols, buttons etc.
I have KeyPreview of form set to true and process keyboard events in form's keydown event handler.
Now, what happens is if i assign focus to PanelMap, next time i press an arrow key, NO KeyDown event fires. Not a single one! Even form which is supposed to process all events because it has "KeyPreview" doesnt get its even to fire. When i press an arrow, PanelMap loses focus towards the SplitControl.
Okay, i thought, maybe PanelMap is not supposed to ever have focus, lets give focus to SplitControl (if i press arrow key while it has focus, i can handle it so it doesnt go further). But then, if anything like a textbox that is inside something that is inside SplitControl has focus, then SplitControl CANNOT get focus. .Focus() will do nothing - focus remains in the whichever control that had it!
Why does it act so strange? Why doesnt Form's KeyDown fire when panel has focus and arrow key is pressed? Why doesnt SplitControl get focused when i call .Focus() even though CanFocus=true?
And ultimately, how do i achieve what i want? Is there a way to do it?
I think you're running into the widgets taking the keystrokes before your events get to them for navigation. I had this issue, and did this:
private void RemoveCursorNavigation(Control.ControlCollection controls)
{
foreach(Control ctrl in controls)
{
ctrl.PreviewKeyDown += new PreviewKeyDownEventHandler(MainWin_PreviewKeyDown);
RemoveCursorNavigation(ctrl.Controls);
}
}
I call this function in the main form's Load handler, like this:
RemoveCursorNavigation(this.Controls);
In your PreviewKeyDown handler, you need to do this:
public void MainWin_PreviewKeyDown(Object sender, PreviewKeyDownEventArgs e)
{
switch(e.KeyCode)
{
case Keys.Up:
case Keys.Down:
case Keys.Left:
case Keys.Right:
e.IsInputKey = true;
break;
default:
break;
}
}
The e.IsInputKey = true; tells the outside that you've used this event, and don't want it going anywhere else.
Now you get to see the keystrokes before they go to the widgets, and you won't get navigation between them from the cursor keys.
I found an answer like this:
I made a textbox that is hidden under a panel (but enabled and visible). This textbox is given focus when i want to "lock" focus on my PanelMap. It has onkeydown even with e.suppress = true so that textbox never gets any keystrokes to affect it.
Crude workaround but works wonders... typical M$ business...
I would like to be able to have a user be able run through the tabs, setting focus to each one, but only when they hit enter, the tabpage will render.
You would think that the paint event would be involved, but I don't know how to "cancel out" of it, if that would even do the job..
First, I should caution you that you're overriding the standard Windows behavior. In any property page dialog or anywhere else that uses tabs in the user interface, using the left and right arrow keys will flip through the tabs and cause them to display their contents in the tab control. You do not have to press Enter to get the selected tab page to display. Make sure that your users understand that your application is different (and that you understand the needs of your users) if you decide to go this route.
That said, you can override this behavior by handling the KeyDown event for the TabControl, detecting when one of the arrow keys has been pressed, and cancelling it. For example:
private void myTabControl_KeyDown(object sender, System.Windows.Forms.KeyEventArgs e)
{
//Check to see if an arrow key was pressed
if ((e.KeyCode == Keys.Left) || (e.KeyCode == Keys.Right))
{
//Cancel the keypress by indicating it was handled
e.Handled = true;
}
}
However, once you do this, there will be no way for the user to set focus to a particular tab page's tab, because once the tab gets focus, the tab page is immediately brought into view. This is handled by the parent TabControl and is unrelated to the Paint event (which is responsible for how the control gets painted, not when or why).
Of course, you can always determine if the Enter key was pressed in the same KeyDown event and activate any tab page that you wish (such as by using a counter variable that is incremented/ decremented each time the corresponding arrow key is pressed), but there will be no visible indication to the user which tab will then be brought into view. The focus rectangle will not be drawn.
Also be aware that pressing Ctrl+Tab or Ctrl+Page Up/Page Down will switch between tab pages. If this is also undesirable, you'll need to watch for and cancel these key combinations as well.Any time you start trying to override default behaviors, you're in for a lot more trouble than if you just design your application around it. If there's a particular reason you want to require the Enter key to commit tab page switching, we might be able to help you come up with an easier and better solution.
I'm not sure I understand what you are trying to accomplish, but it sounds like you can do it using the Visible property.
You should be able to set the TabPage's visibility to false when the user switches to it, and then set it to true only when you want to.
Rep steps:
create example .NET form application
put a TextBox on the form
wire a function up to the TextBox's Enter event
When you run this application, the Control.Enter event fires when focus first goes to the TextBox. However, if you click away into another application and then click back into the test application, the event will not fire again.
So moving between applications does not trigger Enter/Leave.
Is there another alternative Control-level event that I can use, which will fire in this scenario?
Ordinarily, I would use Form.Activated. Unfortunately, that is troublesome here because my component is hosted by a docking system that can undock my component into a new Form without notifying me.
What are you trying to do in the Enter event?
I can't find another control-level event that fires in your example program but when my test app does regain focus, the control that last had focus still has it.
Interesting question but it needs a little more context.
If I try your example and click outside the control on another window, desktop, etc, I can get the Got and Lost Focus events to fire, but if you're only trying to click within a form or a control with only 1 control, these event will never be fired because it is the only thing to focus on. Neither will Entered or left, unless you change the dynamics or overload the controls, you cannot get this to happen
In your example, I think you need another control. The reason being is that the first control (tabIndex 0) is the one with focus. With no other control to switch focus to, this control will always be focused, and therefore can never be entered. Switching to another application or form will not change the focus or active control in this form so when you return you will still not get the event fired.
With added controls control.entered should work fine. If this is your only control, why not call the event on formLoad, or TextChanged, when the form gets focus?
Thanks, I'll give some background.
My control is a UserControl that contains a grid and a toolbar. A user will typically launch several of these controls to view different slices of the system's data.
There are several keyboards shortcuts that can launch actions from the selected row in the current grid. However, it is a requirement that these keyboard shortcuts should apply not only to the currently focused grid. If the user is currently focused on one of the many other areas of the application, then this keyboard shortcut should still work, and it should be routed to the last focused grid.
So I wired a function to the Control.Enter event of my UserControl to basically say LastFocusedGrid = this.
And it would work, except for the docking and undocking...
See, these controls are hosted inside an application with docking features, somewhat similar to visual studio.
By default, the control launches as a tab within the main working area of the application, similar to the way a source file opens in visual studio.
However, the user can "rip out" a tab by grabbing the tab header and dragging it out of the main application. At this point, the application creates a new "float form" to host the control. Switching between the main application and this float form is the same as switching between apps, for the purposes of the Control.Enter and Form.Activated events.
At that point we have the "one control within a form" scenario simulated with the example application described in the original post.
Now, there are some ways around this. I could leverage the Form.Activated event, which DOES fire when switching between forms. If you add an event in the test application to the Form's Activated event, you will see that it works great.
The problem is that my UserControl's relationship with its parent Form is fluid, making the solution somewhat complicated. I tried wiring up to "this.ParentForm.Activated" which worked okay. The problem is when do you call this? What happens when you are undocked/redocked? I ended up with a nasty bunch of code with things like "previousParentForm" so that I could unhook from the old form, and then I was still facing the problem that the docking system doesn't notify me when my parent Form is being changed, so I was going to have to make a bunch of changes there, too.
These problems are not unsolvable, but if there is a simpler control-level "parent form was activated" event, then that would be a lot more elegant.
That's rather long, but I hope it clarifies the situation.
So when creating your grid, can you not set the KeyPressed, or KeyUp, etc. event? If so, all the grids can make use of the same event handler. Just make sure that when you get into the event handler to do something like:
Grid currentGrid = (Grid)sender;
Then you should be able to apply that block of code to any grid that gets sent in without having to worry about keeping track.
Since all the event handler really is, it's location is a mute point really as long as everything you need to execute it is accessible.
Frye, the problem is that the keyboard shortcuts should work no matter where the user is in the application. They are gloabl commands, handled at the top level, and then routed to the "last focused grid."
So handling the keystrokes at the grid level will not help.
To be more specific, assume user launches grids A, B, and C. But he also launches other controls X, Y, and Z that have nothing to do with my code.
User clicks on A, then on C. Then he clicks on Y, then on Z. With focus on Z, he hits my keyboard shortcut. In this case, grid C should respond since it was the last grid the user was focused in.
It sounds like the issue that you're having is not directly related to the Enter event and more to the point, if you have controls "that have nothing to do with your code" then you really aren't looking at a control level event.
Guess I wasn't clear.
My control lives in a container application. So do other unrelated controls by other teams. Think of it like visual studio -- my control is the code editing tab, but there is also the pending changes list and the properties window, which cohabitate with the source files but aren't directly related.
The keyboard shortcut is handled by the container application. Then it should be routed to the last one of my controls that the user was focused on.
Maintaing this "LastFocusedGrid" reference is what I do in the Enter event.
If you want to see similar functionality at work in visual studio, try this:
open a few source files
navigate to the "Start Page" tab.
Hit Ctrl-F and search "current document" for some string
Notice that the serach feature auto-navigates to the LAST FOCUSED source file to perform the search.
So even though you weren't focused in the source file, the ctrl-F command was processed by visual studio and routed to the last focused source file tab.
Now try the same thing with Ctrl-G. It doesn't work unless you are focused directly in the source file.
My keyboard commands need to work like Ctrl-F here, not like Ctrl-G. That is why I don't just capture the keyboard events directly in my control.
Does that clarify or make things worse?
Have you tried just a simple Control.GotFocus?
in this example if you toggle between clicking the textboxes neither the enter or got focus will do as expected, however if you click the child forms instead both will behave as expected.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Forms;
namespace EnterBrokenExample
{
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Form Form1 = new Form();
Form c1 = new Form();
Form c2 = new Form();
Form1.IsMdiContainer = true;
c1.MdiParent = Form1;
c2.MdiParent = Form1;
c1.Show();
c2.Show();
TextBox tb1 = new TextBox();
c1.Controls.Add(tb1);
tb1.Enter += ontbenter;
tb1.Text = "Some Text";
tb1.GotFocus += ongotfocus;
TextBox tb2 = new TextBox();
c2.Controls.Add(tb2);
tb2.Enter += ontbenter;
tb2.Text = "some other text";
tb2.GotFocus += ongotfocus;
Application.Run(Form1);
}
static void ontbenter(object sender, EventArgs args)
{
if (!(sender is TextBox))
return;
TextBox s = (TextBox)sender;
s.SelectAll();
}
static void ongotfocus(object sender, EventArgs args)
{
if (!(sender is TextBox))
return;
TextBox s = (TextBox)sender;
s.SelectAll();
}
}
}