I need to set focus to the content of a ContentPresenter
. I can assume the ContentTemplate
contains an IInputElement but not anything else about it.
Here is a much simplified example that illustrates the problem:
Main window:
<Window x:Class="FiedControlTest.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:custom="clr-namespace:Esatto.Wpf.CustomControls;assembly=Esatto.Wpf.CustomControls"
xmlns:local="clr-namespace:FiedControlTest">
<Window.Resources>
<Style TargetType="TextBox" BasedOn="{StaticResource {x:Type TextBox}}">
<Setter Property="Margin" Value="5"/>
<Style.Triggers>
<Trigger Property="IsFocused" Value="True">
<Setter Property="Background" Value="LightBlue"/>
</Trigger>
</Style.Triggers>
</Style>
</Window.Resources>
<StackPanel Orientation="Vertical">
<StackPanel Orientation="Horizontal">
<ComboBox ItemsSource="{Binding Path=Options}" Name="cbOptions" DisplayMemberPath="Description"/>
<Button Content="Set focus" Click="SetFocus"/>
</StackPanel>
<StackPanel Orientation="Horizontal">
<Label Content="TextBox:"/>
<TextBox Name="tbText" Text="A bare text box."/>
</StackPanel>
<StackPanel Orientation="Horizontal">
<Label Content="ContentPresenter:"/>
<ContentPresenter Content="TextBox in a ContentPresenter" Name="cpText">
<ContentPresenter.ContentTemplate>
<DataTemplate>
<TextBox Text="{Binding Mode=OneWay}" IsReadOnly="True"/>
</DataTemplate>
</ContentPresenter.ContentTemplate>
</ContentPresenter>
</StackPanel>
</StackPanel>
</Window>
Codebehind:
public partial class MainWindow : Window
{
public MainWindow()
{
this.DataContext = this;
Options = new ObservableCollection<Option>(new[]{
new Option(){TargetType=typeof(TextBox), Description="Bare Text Box"},
new Option(){TargetType=typeof(ContentPresenter), Description="Content Presenter"}
});
InitializeComponent();
cbOptions.SelectedIndex = 0;
}
private void SetFocus(object sender, RoutedEventArgs e)
{
var opt = cbOptions.SelectedItem as Option;
if (opt.TargetType == typeof(TextBox))
tbText.Focus();
if (opt.TargetType == typeof(ContentPresenter))
cpText.Focus();
}
public ObservableCollection<Option> Options { get; set; }
public class Option
{
public Type TargetType { get; set; }
public string Description { get; set; }
}
}
There's not much there. The bare TextBox
takes focus as expected; the TextBox
presented by the ContentPresenter
does not.
I have tried adding Focusable="True"
to the ContentPresenter
but it doesn't have any visible effect. I've tried doing using Keyboard.SetFocus
instead of UIElement.Focus
but the behavior doesn't change.
How is this done?
In fact what you set focus is the
ContentPresenter
, not the innerTextBox
. So you can useVisualTreeHelper
to find the child visual element (the TextBox in this case) and set focus for it. However withIsReadOnly
beingtrue
, you won't see any caret blinking (which may also be what you want). To show it in readonly mode, we can just setIsReadOnlyCaretVisible
totrue
:Here the edited XAML code with
IsReadOnlyCaretVisible
added:Note that the above code can only be applied in your specific case where you use a
TextBox
as the root visual of ContentTemplate of a ContentPresenter. In general case, you will need some recursive method to find the child visual (based onVisualTreeHelper
), you can search more for this, I don't want to include it here because it's a very well-known problem/code in WPF to find visual child in WPF. (the italic phrase can be used as keywords to search for more).