Hi,
I need to be able to detect when a XamDataGrid gets and loses focus. There could be multiple grids displayed and I would like to hook up some commands when a grid receives focus and un-hook the commands when the grid looses focus - so the commands apply to the currently active grid.
I would expect that the grid raises a GotFocus event when it receives focus, and a LostFocus event when it loses focus, however, there are several items within the template of the grid that are Focusable. Every time I simply change the selection within the grid, I get 2 LostFocus events followed by 1 GotFocus.
I tried specifying a style for the DataRecordPresenter (the main element causing the extra events to be fired) and setting the "Focusable" property to "False". This appears to have the desired effect - I am only getting one GotFocus event when the focus moves to the grid - I can navigate within the grid without subsequent focus events firing, then only when I navigate to a different element within the window do I get the LostFocus firing :-)
Is this ok? Could it have any undesired side-effects, or is this an acceptable thing to do? ...or am I missing the point and should be tackling this some other way?
Any guidance would be much appreciated,
Dave
Thanks for the tips, you are of course correct, I'd forgot there will be other focus scopes for context menus and the like.
I'm still working on this - It feels as though it shouldn't be so hard to accomplish - What we're trying to achieve is hardly complex!
I'll post back when I've got further,
Cheers,
Unfortunately it sounds like you will need to tailor your own logic based on your requirements. The sample I gave was based on your original attempt to use Got/Lost Focus events which is based on logical focus and not keyboard focus so the sample just helped you to track when logical focus moved into or out of the grid as opposed to using those events which was telling you when the logically focused element was shifted within the grid. Moving focus to an element in another focus scope does not change logical focus - which is why the events were not raised when focusing an element in a menu/toolbar/ribbon or other window; this is what distinguishes logical focus from keyboard focus. Likewise the contentpanes of xamdockmanager are focusscopes so that each maintains its own focusedelement. Even if the contentpane wasn't itself a focusscope (something you could change using the focusmanager.isfocusscope attached property), you could still get this because the floating windows (which are just wpf windows) are separate focus scopes in separate visual trees. You can try to write logic that based on the keyboard focus change checks its visualtree to see what its within but you'll have to deal with all the possible situations - e.g. keyboard focus went into the ribbon (so you ignored it) and the user went from the ribbon to activating another window in the os (again ignored) and then activated another element within the window when it returned, etc. - so I don't think you can just ignore when focus goes into the ribbon/outlookbar/toolbar/menu. You'll probably need to take into account messageboxes and context menus as well. Essentially I think you will need to write your own custom logic based on a combination of events - maybe logical focus and the pane activated event of the xamdockmanager or perhaps some combination with the keyboard focus.
Hi Andrew,
I'm sorry to report I'm still having problems with this focus issue. Your solution works great in a simple app with one focus scope, however, our actual application is rather more complex.
Our main UI consists of your XamRibbon at the top of the window, with a XamOutlookBar to the left and a XamDockManager using the rest of the space. We then load documents at run time (from definition stored in a DB) into ContentPanes within the DockManager. These documents can contain multiple 'widgets' of which your XamDataGrid is one.
I've altered the sample you provided to illistrate the problems we are now facing. As each ContentPane is its own focus scope, your LostLogicalFocus event is not firing when we change the active ContentPane.
So, I was thinking the Keyboard focus may be better after all - I've added a couple of extra Debug.WriteLine statements within an IsKeyboardFocusWithinChanged handler. These events are being fired when we change the active tab (what we want). They are also firing when focus moves away from the app (not great), but more importantly, when focus shifts to the Ribbon (by clicking the drop-down Paste button in the example).
I'm now not sure what to do :-( I think I like the logical focus approach better, but to meet our needs, it would need to notify when the active focus scope changes as well as when the logical focus changes within the focus scope. However, it would need to recognise and ignore shifts to specific focus scopes (namely, the Ribbon and OutlookBar ones).
If we pursue the keyboard focus idea, when the grid loses keyboard focus, it would need to check the Keyboard.FocusedElement and somehow detect whether the new element resides within either the Ribbon or OutlookBar and therefore ignore it.
Any ideas as to the best approach?
Much appreciated,
Cheers for the explanation, it makes perfect sense (now I've done some more reading about focus scopes, focused elements and checked out your example app along with the FocusWithinManager :-) )
I had got to the point where I was looking at the GotKeyboardFocus and LostKeyboardFocus events - they are much closer to what I was looking, but not as good as your solution. In my test, the LostKeyboardFocus was being raised when focus left the application, but your LostLogicalFocus event only gets called when focus moves within our app - perfect.
So, thanks very much for the sample app - it'll prove useful in several places in our UI (which I'm sure is why you created a manager for it!)
p.s. Yep, I realised the DataRecordCellArea was one element in the tree that was focusable - I now realise it's not a good idea to try and prevent all sub-elements of a control being focusable just to solve the issue I had!
Dave,
From your first post it is clear that you want to know which grid is focused. For example, you have 3 xamDataGrids and you want to hook up commands to each of them when they are focused. You can wire an event to all of them for GotFocus like this:
xamDataGrid1.GotFocus += new RoutedEventHandler(DataGrid_GotFocus);
xamDataGrid2.GotFocus += new RoutedEventHandler(DataGrid_GotFocus);
xamDataGrid3.GotFocus += new RoutedEventHandler(DataGrid_GotFocus);
then in the event e.OriginalSource is your grid
void DataGrid_GotFocus(object sender, RoutedEventArgs e)
{
if((e.OriginalSource as XamDataGrid).Name == "xamDataGrid1")
//hook up commands
}
if((e.OriginalSource as XamDataGrid).Name == "xamDataGrid2")
//hook up other commands commands
Alex.