PunCha (PCH)


FocusScope学习三: 对FocusScope 的探究与总结


这个帖子很好的讨论了Focus Scope的种种,WeiFen 和John给出了我最接受的总结: 我在最后也给出了我的总结:

From WeiFen:

I think I can explain this after digging into the framework source code.

1. After clicking, the MenuBase and ButtonBase class will try to restore keyboard focus to previous focused element by calling Keyboard.Focus(null). This will set keyboard focus to top level main window. Since Window is a FocusScope, this will set focus to main window's FocusedElement. Normally we don't expect a click will change the focus, that's where the confusion come from. But this behavior makes sense:

1) MenuBase works in a open-dismiss way. It should not have the focus after clicking. Actually I think they should not get focus at all, like in Win32 or Win forms. But this is another topic.

2) Normally after clicking a button, we're not likely to click it again. So it's naturaly to restore the focus to previously focused element.


2. The nested FocusScope. It's not actually that complex as we expected. Whenever an UIElement receives keyboard focus, it will find its DIRECT parent focus scope, and set this focus scope's FocusedElement to itself. Keyboard focus leaves the focus scope will do nothing.


Consider the check box sample you provided:

1) If you set IsFocusScope="true" on StackPanel, focus the check box will set StackPanel's FocusedElement to check box, and main window's FocusedElement remains unchanged - ListViewItem; After clicking the check box, because of Keyboard.Focus(null) called, the keyboard focus will set to previous ListViewItem.

2) If you remove IsFocusScope="true" on StackPanel, focus the check box will set main window's FocusedElement to the check box. Keyboard.Focus(null) will set the focus back to the check box.

If you want both IsFocusScope="true" and no keyboard focus change after clicking the check box, add the following code to your window class (of course you need to add x:Name="checkBox" in the XAML file):

假如Window没有Focused Element,那么焦点则不会转移。

 override void OnGotKeyboardFocus(KeyboardFocusChangedEventArgs e)



if (e.Source == checkBox)

FocusManager.SetFocusedElement(this, checkBox);



Hope this helps,

- Weifen

From John:

Wow long discussion on this...  I had to figure some of this out early on, and I thought I chip in with what I discovered.  I found some errors in the documentation that I don't think have been corrected.

Based on my testing the correct explanation of command routing when no target is set is:

The element within the Windows focus scope that has logical focus will be used as the command target.

Note that it's the windows focus scope not the active focus scope.  And it's logical focus not keyboard focus.

When it comes to command routing, FocusScopes remove any item you place them on and it's child elements from the command routing path.  

So if you create a focus scope in your app and want a command to route in to it you will  have to set the command target manually.

Or, you can not use FocusScopes other than for toolbars, menus etc and handle the container focus problem manually.

The above is pretty much covered in the posts above, but I think this states it a little clearer.  If not... sorry for butting in.<g>


John Fenton

From Me:


1)假如CommandSource在一个FocusScope里面,命令被触发,那么WPF会先寻找该FocusScope的事件处理程序(CommandBindings),如果有,则使用这个。我测试的是CommandBinding正好是CommandSource的Parent Container,所以不确定是不是该FocusScope的CommandBindings还是Parent Container的CommandBindings。
2)假如主窗口有Focus Element,那么就会使用该Focus Element的Built-in的事件处理程序,假如没有,那么就是用主窗口的。假如还是没有,会找到你的FocusScope和主窗口之间的CommandBindings(直线路上,就是ParentContainer, Parent-Parent Container的,而不会找Sibling的)但是只会找CommandBindings,不会找到那些Built-in的事件处理程序。

其实也不必太在意,真正写代码不会那么复杂的!另外 Pro WPF in C# 2010里面也讲的比较透彻,并给出了一个例子。

个人分类: WPF
想对作者说点什么? 我来说一句



FocusScope学习三: 对FocusScope 的探究与总结