5
votes

I'm developing a custom control deriving from Control and defined using ControlTemplate. After stripping out all bells and whistles, the control displays just four TextBoxes:

<Style TargetType="{x:Type local:MyControl}">
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type local:MyControl}">
                <StackPanel Orientation="Horizontal">
                    <TextBox/>
                    <TextBox/>
                    <TextBox/>
                    <TextBox/>
                </StackPanel>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

The problem is that the control is able to receive keyboard focus and draw the focus rectangle around itself (not one of the TextBoxes, but around all of them). This adds an unnecessary tab stop when navigating through the controls in the Window.

Setting Focusible to false on the custom control (using a setter in the style) solves the problem, but such a solution is far from perfect, because a developer using that control could set Focusible to true which would ruin the tab stop behavior.

I could respond to the control's GotKeyboardFocus event and give focus to the first TextBox when the whole control receives focus, but this will not work properly when tabbing back (using Shift+Tab), the first TextBox would receive focus while in such a case the last TextBox should be focused first.

How can I prevent the whole custom control from accepting keyboard focus, but allow child TextBoxes to act normally as tab stops?

1
Have to tried setting the FocusVisualStyle to null?Kent Boogaart
Unfortunately it doesn't work, it simply hides the selection rectangle without changing the behavior, the user has to press Tab twice to get to the first TextBox.RaceRalph
Set Focusable to false too. Some other developer setting it back to true is their problem. Indeed, you should override the metadata for FocusableProperty such that it defaults to false for your control.Kent Boogaart
Correct me if I'm wrong, but the developer using my custom control shouldn't care about its internal structure and setting Focusable to True on the control should make all child TextBoxes focusable and setting it to False should make all child TextBoxes non-focusable.RaceRalph

1 Answers

0
votes

I would create an actual UserControl and encapsulate focus management there. because Xaml'ing through Style gets you only so far...

Here's a quick sample of a UserControl + it's usage/test in the main window. As you'll notice focused rectangle doesn't appear any more. For simplicity sake I didn't include DataBindings/ViewModel etc (as your' question wasn't about that)

<UserControl x:Class="WpfApplication1.MyControl"
         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
         IsVisibleChanged="OnIsVisibleChanged" >
<StackPanel Orientation="Horizontal">
    <TextBox x:Name="txtA">txtA</TextBox>
    <TextBox>txtB</TextBox>
    <TextBox>txtC</TextBox>
    <TextBox>txtD</TextBox>
</StackPanel>

Set focus on visiblitychanged to the first textbox in code behind

namespace WpfApplication1
{
public partial class MyControl : UserControl
{
    private void OnIsVisibleChanged(object sender, DependencyPropertyChangedEventArgs e)
    {
        if (IsVisible && IsEnabled)
        {
            Keyboard.Focus(null);
            Keyboard.Focus(txtA);
        }
    }

    public MyControl()
    {
        InitializeComponent();
    }
}

}

in my little test, I put the MyControl between two other textbozes, when run just tab:

<Window x:Class="WpfApplication1.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:c="clr-namespace:WpfApplication1"
    Title="MainWindow" Height="350" Width="525">

<StackPanel>
    <TextBox>txt1</TextBox>
    <c:MyControl/>
    <TextBox>txt2</TextBox>
</StackPanel>