We have a WPF .NET 4.0 C# based application. We built our user interface from XML definitions (not XAML) but underneath we use a WPF to present the UI. That is at runtime, w
WPF treats the entire UI Tree as a single Tab scope. It isn't broken up into smaller areas such as you would expect. This includes controls inside UserControls.
For example, if you had
<StackPanel>
<TextBox Name="TextBox1" />
<MyUserControl />
<TextBox Name="TextBox3" />
</StackPanel>
And MyUserControl
looked like
<MyUserControl>
<TextBox Name="TextBox2" />
</MyUserControl>
The default tab cycle would be TextBox1, TextBox2, TextBox3. This is because no TabIndex properties are defined, so all controls run at the default tab order, which is the order in which they're added to the UI.
If you set the TabIndex on your controls such as below,
<StackPanel>
<TextBox Name="TextBox1" TabIndex="1" />
<MyUserControl TabIndex="2" />
<TextBox Name="TextBox3" TabIndex="3" />
</StackPanel>
Your tabbing would change to TextBox1, TextBox3, TextBox2. This is because TextBox2 doesn't have a TabIndex specified, so the default is assumed and it is tabbed to after all the other controls with a TabIndex specified get cycled through.
The way I usually get around this is to bind the TabIndex
of controls inside the UserControl to the UserControl.TabIndex.
For example adding the following binding to the UserControl would make the Tab cycle correct again
<MyUserControl>
<TextBox Name="TextBox2" TabIndex="{Binding Path=TabIndex, RelativeSource={RelativeSource AncestorType={x:Type local:MyUserControl}}}" />
</MyUserControl>
I usually prefer to set this binding in the Loaded
event of the UserControl instead of having to remember to set this binding on all the controls inside the UserControl. I'm sure there are also more efficient ways of doing this as well, however the problem has not come up often enough for me to sit down and take the time to research how to use tab scopes correctly to avoid this workaround.
You should try setting a KeyboardNavigation.TabNavigation attached property on either your Tree
control, or the StackPanel
derived control, in case you want your bottom buttons to also participate in a tab cycle:
<controls:CustomStackPanel KeyboardNavigation.TabNavigation="Cycle">
<Tree>
...
</Tree>
</controls:CustomStackPanel>
You can even combine a code-behind approach that you, I assume, is currently trying to use to control the tab behaviour inside the Tree control, with the KeyboardNavigation.TabNavigation
to take care of the tabbing outside the tree control.
Not an answer per-se but WPF tabbing is extremely fiddly. Requires setting the TabNavigation, IsTabStop properties in Xaml and fiddling around with focus scope. I have spent days trying to get tabbing right using Xaml alone. I would suggest to begin with your XML -> WPF model really ought to be Xaml -> WPF! However I would imagine that's not possible.
How about this for a workaround. Is it possible for you to handle the tabbing in generated code? If your WPF usercontrols are generated by your own software from your own XML then I'd suggest putting a TabOrder element in your XML and then using that to wire up a TabOut event.
Look at the following code sample to force a move operation in code (Similar to Tabbing)
// Creating a FocusNavigationDirection object to perform the tab operation
// values include Next, Previous, First, Last etc...
FocusNavigationDirection focusDirection = FocusNavigationDirection.Next;
// MoveFocus takes a TraveralReqest as its argument.
TraversalRequest request = new TraversalRequest(focusDirection);
// Gets the element with keyboard focus.
UIElement elementWithFocus = Keyboard.FocusedElement as UIElement;
// Change keyboard focus.
if (elementWithFocus != null)
{
elementWithFocus.MoveFocus(request);
}
Secondly wire in to the KeyUp (or PreviewKeyUp) event of your "tabbable" controls and if the key was tab call the above code to cause focus to jump to the next element.
The above code sample will basically force in code what WPF should do out of the box. As other posters have suggested I would go through your generated WPF code to see what the values of KeyboardNavigation.IsTabStop and KeyboardNavigation.TabNavigation
Best regards,
Try KeyboardNavigation.TabNavigation="Once"