WPF: Focus in a Window and UserControl - wpf

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.

Related

WPF Usercontrol based on Textbox/Popup definition

I created a "UserControl" based on a TextBox. That means I created a new UserControl and replaced UserControl by TextBox in xaml and xaml.cs files.
Now I want that my new TextBox control shall have a popup to display some suggestions.
Now my question is: Where can I define the look/structure of the Popup as XAML? The Popup definition shall be part of the newTextBox.
That's what I have:
<TextBox x:Class="WpfApplication11.UserControl2"
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"
xmlns:local="clr-namespace:WpfApplication11"
mc:Ignorable="d"
MaxWidth="{Binding Path=ActualWidth}">
</TextBox>
I think you're totally missing what a UserControl actually is. To put it simply, a UserControl is a group or collection of controls that make up one larger... uh... control.
I want that my new TextBox control shall have a popup to display some suggestions.
What you are describing here is a UserControl. You cannot place a Popup inside a TextBox.
So. What you're after here is probably something like this:
<UserControl ... >
<Grid>
<TextBox Name="txt"
Width="150" ... />
<Popup PlacementTarget="{Binding ElementName=txt}"
Placement="Bottom"
IsOpen="True"
StaysOpen="True"
Width="{Binding ActualWidth, ElementName=txt}">
<!-- Some popup content here -->
</Popup>
</Grid>
</UserControl>
This is obviously an extremely simplified example, but you get the idea.

Flowdocument and Databinding

After thoroughly reading the documentation on flowdocuments (in WPF) it seems as though flowdocument does not easily support databinding. PLEASE say this is not true! I have a listbox using the sample data in Expression Blend and I inserted a textblock into the flowdocument. The textblock (text property) is databinded to the string data in the listbox. After running the project I would expect that the textblock text changes as the listbox selection changes, but nothing happens. Databinding is not working. What is the easiest way to make databinding work with flowdocument?
Here is the XAML.
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="WpfApplication1.MainWindow"
x:Name="Window"
Title="MainWindow"
Width="640" Height="480">
<Window.Resources>
<DataTemplate x:Key="ItemTemplate">
<StackPanel>
<TextBlock Text="{Binding Property1}"/>
</StackPanel>
</DataTemplate>
</Window.Resources>
<Grid x:Name="LayoutRoot" DataContext="{Binding Source={StaticResource SampleDataSource}}">
<FlowDocumentScrollViewer Margin="120,64,256,126">
<FlowDocument>
<Paragraph><Run Text="Only A Test"/><InlineUIContainer>
<TextBlock TextWrapping="Wrap" Text="{Binding Collection[0].Property1}" Height="56" Width="112"/>
</InlineUIContainer></Paragraph>
</FlowDocument>
</FlowDocumentScrollViewer>
<ListBox ItemTemplate="{DynamicResource ItemTemplate}" ItemsSource="{Binding Collection}" Margin="0,83.847,52,62.153" HorizontalAlignment="Right" Width="200"/>
</Grid>
</Window>
According to this Microsoft documentation in April 2009, this is not possible:
While there are many great features in flow documents, if your documents are generated from dynamic data, you have a bit of a problem: there is no support for data binding in flow documents. The flow document elements (Section, Table, Run, Paragraph and the like) are dependency objects, but don't define any dependency properties that would allow you to dynamically change or generate content.

Focus and TabIndex on UserControls

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" />

WPF Expander in user control getting rendered transparent in ListBox item

I’m having issues with a WPF Expander that I have in a user control that gets rendered in a ListBox. Essentially I’m trying to get PopUpButton behavior on each ListItem in my ListBox. When I open the Expander the content is rendering behind everything else as if it were transparent, or lower in the z-order. I’ve tried this with a WPF PopUp and Toggle Button as well (using techniques described int Karle Shivllet’s blog – Expander Control with Popup Content) to no avail.
Let me first describe what it is I’m trying to do. I have two controls that display a list of inputs that I need to configure for my application. For simplicity sake, one user control is used to configure inputs to a graph, and another control is used to control inputs to a simple excel grid. The inputs for the graph and grid each have properties that need to be configured on them. I’ve developed a simple user control called InputSelectControl that will render a ListBox containing the list of inputs to be configured for the graph or grid. Each ListItem in the ListBox consist of a TextBlock for the input’s name (e.g. Pressure, ECG, etc.) and a WPF Expander that , when clicke, displays a property editor for that input. Since the property editor presentation will be different depending on whether I’m dealing with graph inputs versus grid inputs, I’ve used a DependencyProperty on my InputSelectControl that is of type ControlTemplate. This allows my grid and graph to each supply the presentation they need for editing their input properties. Also note that I will have more than just a graph and a grid that need this behavior, thus the desire to make this a user control that can dynamically receive presentation behavior.
I’ve tried placing my Expander inside my property editor template, had have also tried experimenting with the ZIndex in various places, but always end up with the same behavior, the Expander popup displays behind the ListItems in my list.
Below is some code further describing my approach. Hopefully someone can help me out of this pickle.
XAML representing my Grid (could be graph, or something else) control that hold my InputSelectControl:
<UserControl x:Class="MyApp.GridView"
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"
xmlns:props="clr-namespace:PopupButtonDependencyProp" mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<UserControl.Resources>
<!-- Specify the control tempalte we want loaded into the
properies popup for a grid-->
<ControlTemplate x:Key="GridPropertyEditorTemplate" TargetType="ContentControl">
<props:GridInputPropertyEditor />
</ControlTemplate>
</UserControl.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<TextBlock Text="Hello Grid" Margin="5" />
<!-- Tell the InputSelectControl what template to load into Property
Window for each Grid Input item -->
<props:InputSelectControl Grid.Row="1"
DataContext="{Binding VmUsedInputs, Mode=OneWay}"
PropertyEditorTemplate="{StaticResource GridPropertyEditorTemplate}" />
</Grid>
</UserControl>
XAML representing my InputSelectControl that displays my list of inputs and a ContentControl place holder for each ListItem where I want my "Popup behavior" for editing properties:
<UserControl x:Class="MyApp.InputSelectControl"
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"
xmlns:props="clr-namespace:PopupButtonDependencyProp"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<Grid>
<!-- Listbox holding our inputs. Assuming whatever we're contained in has
set our DataContext to a valid Input collection-->
<ListBox x:Name="inputsUsed" Grid.Row="1" ItemsSource="{Binding}"
ScrollViewer.HorizontalScrollBarVisibility="Disabled"
ScrollViewer.VerticalScrollBarVisibility="Auto"
SelectionMode="Multiple" ClipToBounds="True">
<ListBox.ItemTemplate>
<DataTemplate>
<Border x:Name="border" CornerRadius="7">
<StackPanel VerticalAlignment="Stretch" Orientation="Horizontal">
<!-- Input label-->
<TextBlock Text="{Binding Path=Label}" FontWeight ="Bold"
FontSize ="12" FontStyle = "Normal"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch" Margin="5,0,5,0" />
<Expander x:Name="GridPropEditor" Header="Properties"
Height="Auto" Margin="5,0,0,0"
ToolTip="Open trace property dialog">
<!-- Properties button - The ContentControl below is rendering
the PropertyEditorTemplate that was set by whoever contains us -->
<ContentControl Template="{Binding PropertyEditorTemplate,
RelativeSource={RelativeSource AncestorType=props:InputSelectControl}}" />
</Expander>
</StackPanel>
</Border>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
</UserControl>
C# representing my DependencyProperty for injection the property editor template to present on popup.
/// <summary>
/// Interaction logic for InputSelectControl.xaml
/// </summary>
public partial class InputSelectControl : UserControl
{
#region Dependency Property stuff
/// <summary>
/// Dependency Property for control template to be rendered. This
/// lets us adorn the InputSelectControl with content in the Xaml.
/// The content can be different fore each instance of InputSelectControl.
/// </summary>
public static DependencyProperty PropertyEditorTemplateProperty =
DependencyProperty.Register("PropertyEditorTemplate",
typeof(ControlTemplate), typeof(InputSelectControl));
/// <summary>
/// PropertyEditorTemplate. This is how the property is set and get by WPF
/// </summary>
public ControlTemplate PropertyEditorTemplate
{
get { return GetValue(PropertyEditorTemplateProperty) as ControlTemplate; }
set { SetValue(PropertyEditorTemplateProperty, value); }
}
#endregion
/// <summary>
/// Constructor
/// </summary>
public InputSelectControl()
{
InitializeComponent();
}
}
XAML representing my GridInputPropertyEditor which is the template describing the presentation for editing Grid properties. This will be different for a Graph:
<UserControl x:Class="MyApp.GridInputPropertyEditor"
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"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<Canvas Panel.ZIndex=”99”>
<!-- Property Editor Control - Assumes DataContext holds to the properties
that need to be edited-->
<StackPanel Orientation="Vertical" Background="WhiteSmoke">
<!-- Lists the properties for a Grid to be edited. We could use
any layout we need here. -->
<ListBox ItemsSource="{Binding Properties}" Background="WhiteSmoke" >
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" Margin="5,5">
<TextBlock Text="{Binding Label}" FontWeight="Bold"/>
<TextBlock Text=":" />
<TextBox Text="{Binding Value}" Margin="10,0" Width="20" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</StackPanel>
</Canvas>
</UserControl>
Using Snoop I was able to figure out that if I set the Z-Index of the ListBox item to a high number my property editor comes to the foreground. If someone sees a better way around this let me know. Otherwise I could use some help coming up with a trigger to raise and lower the zindex based on the item selected.
Okay, after a few trials and tribulations I was able to come up with solution using code behind. I'd be interested in finding a way to do this with triggers, but I'm not sure it's possible with this approach.
Here's the update expander XAML:
<Expander x:Name="GridPropEditor" Header="Properties" Height="Auto" Margin="5,0,0,0"
ToolTip="Open trace property dialog"
Tag="{Binding RelativeSource={RelativeSource Mode=FindAncestor,
AncestorType={x:Type ListBoxItem}}}"
PreviewMouseDown="GridPropEditor_PreviewMouseDown"
Expanded="GridPropEditor_Expanded">
Here's the code behind I added to my xaml.cs file
//When an expander is expanded, collapse all the other expanders
private void GridPropEditor_Expanded(object sender, RoutedEventArgs e)
{
if (ExpandersSelected == null)
ExpandersSelected = new List<Expander>();
var expander = (sender as Expander);
var listbox = expander.Tag as ListBoxItem;
if (!ExpandersSelected.Contains(expander))
ExpandersSelected.Add(expander);
if (ExpandersSelected != null)
{
foreach(var x in ExpandersSelected)
{
if (x.Equals(expander))
{
listbox.SetValue(Panel.ZIndexProperty, 99);
continue;
}
var l = x.Tag as ListBoxItem;
x.IsExpanded = false;
l.SetValue(Panel.ZIndexProperty, 0);
}
}
}
The code behind solution closes any expanders that might already be opened and brings the currently expanding Expanders container to the foreground by setting the zindex to 99.
Again, if anyone has a better solution I'm all ears.

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