Focus and TabIndex on UserControls - wpf

I have a strange behaviour:
I have a MainWindow containing textboxes and (simple) usercontrols (textbox and button), but I stripped this to only a textbox for debug purposes.
When I use textboxes and usercontrols WITHOUT setting a TabIndex property the cursor steps through the controls in right order (in the order the controls are added to the window)
When I use textboxes and usercontrols WITH setting a TabIndex property the cursor steps through the controls in invalid order (first all usercontrols, then all textboxes), this is also true when the TabIndex is set to value corresponding to the order in which the control was added
Here is my usercontrol
<UserControl x:Class="SmallControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
>
<TextBox x:Name="txTEXT" Text="{Binding Text}" />
</UserControl>
The following Mainwindow xaml leads to order 000000,111111,222222,333333 ,thats ok
<GroupBox Header="Small,Textbox,Small,TextBox without TabIndex">
<UniformGrid Columns="4">
<local:SmallControl Text="000000" />
<TextBox Text="111111" />
<local:SmallControl Text="222222" />
<TextBox Text="333333" />
</UniformGrid>
</GroupBox>
The following Mainwindow xaml leads to order 000000,222222,111111,333333, thats NOT ok
<GroupBox Header="Small,Textbox,Small,TextBox with TabIndex">
<UniformGrid Columns="4">
<local:SmallControl TabIndex="0" Text="000000" />
<TextBox TabIndex="1" Text="111111" />
<local:SmallControl TabIndex="2" Text="222222" />
<TextBox TabIndex="3" Text="333333" />
</UniformGrid>
</GroupBox>
Is there a way to use TabIndex without beeing forced to add controls in the "right" order in XAML?

By default, WPF reads all the controls, both inside and outside UserControls, at the same tab level (unless specified otherwise). Since the controls inside the UserControl do not have a TabIndex specified, they get tabbed to last after the first tab cycle.
To change this behavior I usually set IsTabStop="False" on my UserControl definition, then I bind the inner controls TabIndex to the UserControl's TabIndex
UserControl XAML
<TextBox x:Name="txTEXT" Text="{Binding Text}"
TabIndex="{Binding Path=TabIndex, RelativeSource={RelativeSource
AncestorType={x:Type local:SearchView}}}"/>
Usage XAML
<GroupBox Header="Small,Textbox,Small,TextBox with TabIndex">
<UniformGrid Columns="4">
<local:SmallControl TabIndex="0" Text="000000" IsTabStop="False" />
<TextBox TabIndex="1" Text="111111" />
<local:SmallControl TabIndex="2" Text="222222" IsTabStop="False" />
<TextBox TabIndex="3" Text="333333" />
</UniformGrid>
</GroupBox>
You might also be able to get it tabbing correctly by setting the KeyboardNavigation.TabNavigation attached property on your UserControl to Local. I seem to recall having issues with this, but I honestly can't remember the details, so it might work.
<UserControl x:Class="SmallControl" ...
KeyboardNavigation.TabNavigation="Local" />

Related

ItemsControl items bindings called when collapsed

I have an ItemsControl which displays a list of messages. It's defined as ...
<ItemsControl HorizontalAlignment="Stretch" VerticalAlignment="Stretch"
ItemsSource="{Binding Messages}" >
</ItemsControl>
I then have a DataTemplate which handles the display for each message. It's defined as...
<DataTemplate DataType="{x:Type vm:MessageViewModel}">
<Button Command="{Binding CommandOpenPage}">
<Button.Template>
<ControlTemplate>
<Border Margin="2" BorderThickness="1"
BorderBrush="{Binding Flags, Converter={StaticResource msgFlagConverter}}"
Background="{Binding Flags, Converter={StaticResource msgFlagConverter}, ConverterParameter=1}" >
<TextBlock Text="{Binding Path=Message}" Style="{StaticResource ActionItem}" TextWrapping="Wrap" />
</Border>
</ControlTemplate>
</Button.Template>
</Button>
</DataTemplate>
Everything displays OK. My problem is when the parent controls are set to Visibility=Collapsed my ItemsControl still goes through the DataTemplate and calls the converters for BorderBrush and BackgroundBrush for each MessageViewModel.
This is bothersome because when the list is very large the bindings are set and converters are executed when they shouldn't. This list is only visible when the user chooses to see it. I understood the binding engine ignores elements under a collapsed parent. Is there an exception to this rule? Or am I just missing something?
I found my problem. The above ItemsControl and DataTemplate were in a UserControl. The visibility was originally handled inside the usercontrol itself by binding the main layout grid to a visibility property. By simply setting the user controls visibility in the parent XAML all bindings started behaving as expected.
This fixes my problem but I still don't understand the difference between setting the visibility of the main layout grid vs the visibility of the usercontrol itself.
<c:ApplicationMenuView Grid.Column="1" Grid.Row="4"
HorizontalAlignment="Left" Margin="1"
VerticalAlignment="Stretch"
DataContext="{Binding Menu}"
Visibility="{Binding IsVisible, Converter={StaticResource BooleanToVisibilityConverter}}"/>

WPF Element Binding in a within a Resource control doesnt work

I have some xaml that will just copy text from one text box to another:
<StackPanel Orientation="Horizontal">
<TextBox Width="100" Height="30" Text="{Binding ElementName=src1, Path=Text}" />
<TextBox x:Name="src1" Width="100" Height="30" />
</StackPanel>
Nothing special, works fine. A bit dumb but just an example.
However if I put the StackPanel as a resource in the Window and create dynamically from code, like this:
<Window.Resources>
<StackPanel x:Key="MySP" Orientation="Horizontal">
<TextBox Width="100" Height="30" Text="{Binding ElementName=src, Path=Text}"/>
<TextBox x:Name="src" Width="100" Height="30" />
</StackPanel>
</Window.Resources>
.. then the element binding doesnt work anymore.
Why? and how to make it work? Any ideas gratefully received.
The following Xaml should work just fine
<Window ...>
<Window.Resources>
<StackPanel x:Key="MySP" Orientation="Horizontal">
<TextBox Width="100" Height="30" Text="{Binding ElementName=src, Path=Text}"/>
<TextBox x:Name="src" Width="100" Height="30" />
</StackPanel>
</Window.Resources>
<StaticResource ResourceKey="MySP"/>
</Window>
You could also use it from code
StackPanel mySP = TryFindResource("MySP") as StackPanel;
if (mySP != null)
{
this.Content = mySP;
}
However, what is the reason for you to have a StackPanel in the Windows Resoures?
If you want to be able to reuse it several times you would have to set x:Shared="False" on the Resource but then you'll get an exception saying something like Cannot register duplicate Name 'src' in this scope the second time you add it.
As far as I'm concerned you should not put that in <Window.Resources>. Only styles, static, dynamic resources and such...
http://msdn.microsoft.com/en-us/library/ms750613.aspx
<Window>
<Window.Resources>
</Window.Resources>
<StackPanel x:Key="MySP" Orientation="Horizontal">
<TextBox Width="100" Height="30" Text="{Binding ElementName=src, Path=Text}"/>
<TextBox x:Name="src" Width="100" Height="30" />
</StackPanel>
<Window>
Having a similar issue, trying to get relative binding to my source control - In my case, I'm creating a designer and need the element as a static so styles can use it's dimensions for centering calculations on a canvas.
Taking a line from [WPF Xaml Namescopes],
ResourceDictionary does not use XAML names or namescopes ; it uses keys instead, because it is a dictionary implementation.
So, directly using ElementName in a resource Dictionary simply does not work, because no name will bind without a NameScope. Also attempted reproducing your situation with Style setters, but no luck - one cannot set an object's name via a Style.
Now, the convoluted solution I cam up with is to
Create a DependencyProperty in the code-behind of the class
you're declaring this resource in.
replace ElementName=Root with RelativeSource={RelativeSource
FindAncestor, AncestorType={x:Type namespace:RootClass}} and bind
to said container directly, allowing you to bind to said
DependencyProperty whilst bypassing the names.
If you need bindings to operate between two elements in the same
StaticResource, bind the source to said DependencyProperty as
OneWayToSource or TwoWay, and the destination as OneWay or TwoWay.
1

WPF: Focus in a Window and UserControl

I'm trying to get a UserControl to tab properly and am baffled. The logical tree looks like this.
|-Window
-Grid
-TabControl
-TabItem
-StackPanel
-MyUserControl
|-StackPanel
-GroupBox
-Grid
-ComboBox
-Textbox1
-Textbox2
Everything works fine, except when the visibility converter for the ComboBox returns Visibility.Collapsed (don't allow user to change database mode), then when textbox1 is selected, instead of being able to tab through the controls in the UserControl, the focus shifts to a button declared at the bottom of the window. Nothing else apart from the controls displayed has TabIndex or FocusManager properties set.
I'm banging my head against a brick wall and I must be missing something. I've tried IsFocusScope=True/False, played with FocusedElement and nothing works if that ComboBox is invisible (Visibility.Collapsed).
<Window x:Class="MyNamespace.Client.WinInstaller"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
FocusManager.FocusedElement="{Binding ElementName=tabWizard}">
<Window.Resources>
<props:Settings x:Key="settings" />
</Window.Resources>
<Grid Grid.IsSharedSizeScope="True">
<!-- row and column definitions omitted -->
<loc:SmallHeader Grid.Row="0" x:Name="headerBranding" HeaderText="Setup" />
<TabControl x:Name="tabWizard" DataContext="{StaticResource settings}" SelectedIndex="0" FocusManager.IsFocusScope="True">
<TabItem x:Name="tbStart" Height="0">
<StackPanel>
<TextBlock Text="Database Mode"/>
<loc:DatabaseSelector x:Name="dbSelector" AllowChangeMode="False" TabIndex="1"
AvailableDatabaseModes="SQLServer" IsPortRequired="False"
DatabaseMode="{Binding Default.DbMode,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"
DatabasePath="{Binding Default.DatabasePath,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"/>
</StackPanel>
</TabItem>
...
The top of the user control is below:
<UserControl x:Class="MyNamespace.Client.DatabaseSelector"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Name="root"
FocusManager.IsFocusScope="True"
FocusManager.FocusedElement="{Binding ElementName=cboDbMode}">
<UserControl.Resources>
<conv:DatabaseModeIsFileBased x:Key="DatabaseModeIsFileBased"/>
<BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter"/>
</UserControl.Resources>
<StackPanel DataContext="{Binding}">
<GroupBox>
<Grid>
<!-- row and column definitions omitted -->
<Label Content="Database Mode"/>
<ComboBox x:Name="cboDbMode" SelectedValue="{Binding ElementName=root,Path=DatabaseMode,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"
DisplayMemberPath="Value" SelectedValuePath="Key" TabIndex="1" Visibility="{Binding AllowChangeMode,ElementName=root,Converter={StaticResource BooleanToVisibilityConverter}}" />
<!-- AllowChangeMode is a DependencyProperty on the UserControl -->
<Grid><!-- row and column definitions omitted -->
<Label "Host"/>
<TextBox x:Name="txtDBHost" Text="{Binding ElementName=root,Path=DatabaseHost,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" TabIndex="2" />
<TextBox x:Name="txtDBPort" Text="{Binding ElementName=root,Path=DatabasePortString,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" TabIndex="3" />
I know this response is quite late... but have you tried:
<UserControl ... KeyboardNavigation.TabNavigation="Local">
Doing so will ensure once your UserControl has recieved focus, you will navigate only through TabStop within your UserControl (instead of worring about conflicting TabIndex values throughout your app). After looping through the TabStops of your UserControl, TabNavigation will resume to the TabStop outside of it.
http://msdn.microsoft.com/en-us/library/system.windows.input.keyboardnavigationmode.aspx
Maybe the problem is that you hide the FocusManager.FocusedElement.
Anyway, you could make life easier by just eliminating some complicating factors:
Remove FocusManager.FocusedElement...
the ComboBox is the first control within anyway.
Remove FocusManager.IsFocusScope...
I suppose it's ok if everytime you enter the usercontrol the first control within will be focussed, not the one that was focussed when you left it before. Just let the usercontrol be "inlined" in the surrounding controls.
Remove the explicit TabIndices in the UserControl. Your layout already implies the same ordering.
If you eliminate these three complicating factors, you might already be done.
Maybe you also have to set your UserControl Focusable=False, s.t. the focus is passed to the first focussable Control within - comboBox or TextBox1.

WPF Focus Navigation Wrapping

Is there a way to force Focus Navigation (as controlled by the Tab key or MoveFocus method) to wrap inside a given container? I have included code which demonstrates this problem below. What is the easiest way to make Tab move focus from TextBox "Charlie" to TextBox "Able" (and visa-versa for Shift+Tab on TextBox "Able") rather than moving it to MenuItem "Alpha"?
<Window x:Class="NavWrapExample.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="300" Width="300">
<DockPanel LastChildFill="True">
<Menu DockPanel.Dock="Top">
<MenuItem Header="Alpha" />
<MenuItem Header="Bravo" />
<MenuItem Header="Charlie" />
</Menu>
<StackPanel>
<TextBox Text="Able" />
<TextBox Text="Baker" />
<TextBox Text="Charlie" />
</StackPanel>
</DockPanel>
</Window>
Use the KeyboardNavigation.TabNavigation attached property, like so:
<StackPanel KeyboardNavigation.TabNavigation="Cycle">
<TextBox Text="Able" />
<TextBox Text="Baker" />
<TextBox Text="Charlie" />
</StackPanel>
Found the answer on Mark Smith's blog.
It sounds like what you want is the same behavior as toolbars: you can tab into them, but once an element within the toolbar gets keyboard focus, focus loops inside. If so, use FocusManager as follows:
<StackPanel FocusManager.IsFocusScope="True">
<!-- Controls go here... -->
</StackPanel>

WPF - Databind to a StackPanel using DataTemplates

I've modified my question since it has changed focus when trying things out.
I narrowed the problem down to the following...
I try to bind the selected Item of a TreeView to a StackPanel (or some other container that can hold User Controls). This container will then display a UserControl, depending on the type of the selected item.
Here is the xaml of the StackPanel (both treeview and stackpanel are in the same window ==> different grid column)
<StackPanel Grid.Column="2" MinWidth="500" DataContext="{Binding ElementName=myTree, Path=SelectedItem, Mode=OneWay}">
<StackPanel.Resources>
<DataTemplate DataType="{x:Type mvTypes:MyTypeA}">
<controls:UserControlA DataContext="{Binding}" />
</DataTemplate>
<DataTemplate DataType="{x:Type mvTypes:MyTypeB}">
<controls:UserControlB DataContext="{Binding}" />
</DataTemplate>
</StackPanel.Resources>
</StackPanel>
When I place a user control directly under the stackpanel (not in the resources), it displays it with the selected object as their datacontext.
Idem if I place a TextBox in it, it will show the correct type of the selected item.
<TextBox Name="textBox1" Text="{Binding}" />
For some reason, placing it within a DataTemplate (even without setting the DataType) results in nothing to display.
Any sugestions. I'm thinking that maybe a StackPanel is not the right control for this, though I can't seem to find other controls that look suitable as containers like this.
Thanks in advance.
Replace the StackPanel in your example with ContentPresenter and instead of DataContext set the Content property. That should work.
Although you have set the Binding on the second custom control, are you setting the DataContext, as the binding is the route to the information and the DataContext is the information it applies this binding information to.
Andrew
You can create a UserControl to display the TreeView and the selection info on the right, all in one. It saves you from creating any custom control. A custom control is basically unnecessary since you do not create anything which didn't exist before.
<UserControl x:Class="NameSpace.SelectionView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:controls="namespace.Controls"
Height="300" Width="300">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<TreeView Name="customTree">
<!--Items go here-->
</TreeView>
<StackPanel Grid.Column="1" MinWidth="50" DataContext="{Binding ElementName=customTree, Path=SelectedItem, Mode=OneWay}">
<StackPanel.Resources>
<DataTemplate DataType="{x:Type StylingTest:CustomViewModelA}">
<controls:CustomADetailsControl />
</DataTemplate>
<DataTemplate DataType="{x:Type StylingTest:CustomViewModelB}">
<controls:CustomBDetailsControl />
</DataTemplate>
</StackPanel.Resources>
<TextBlock Text="{Binding}"/>
</StackPanel>
</Grid>
</UserControl>
Any other custom behaviour, I'm sure you could create or set in styles/templates here.
Also, you might find one of my other answers useful.
Good luck with wpf, cheers.

Resources