8

I have created a basic application using Prism & MVVM. So far, it only consists of the Shell, and one View/ViewModel.

During application load, I am loading the View into my main region and this displays on screen. This works, but I cannot get the textbox on the view to focus. It looks like the cursor is in the box (although it's not flashing), but it doesn't accept text input until I click on the textbox.

I've recreated this in a new project, where all I've done is install prism/prism.unityextensions, set up the shell and the view, and loaded the view into the shell region. Neither xaml file has anything in the code behind.

Shell

<Window x:Class="MVVMFocusTest.Shell"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:prism="http://www.codeplex.com/prism"
        Title="MainWindow" Height="350" Width="525">
    <Grid>
        <DockPanel LastChildFill="True">            
            <ContentControl Name="MainRegion" DockPanel.Dock="Top" prism:RegionManager.RegionName="MainRegion" />
        </DockPanel>
    </Grid>
</Window>

View1

<UserControl x:Class="MVVMFocusTest.View1"
             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">
    <StackPanel>
        <Grid FocusManager.FocusedElement="{Binding ElementName=Username}">
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="100" />
                <ColumnDefinition Width="*" />
            </Grid.ColumnDefinitions>
            <Grid.RowDefinitions>
                <RowDefinition />
                <RowDefinition Height="Auto" />               
            </Grid.RowDefinitions>

            <Label Grid.Row="0" Grid.Column="0">Username</Label>
            <TextBox Name="Username" Grid.Row="0" Grid.Column="1" ToolTip="Enter Username" TabIndex="0" />
            <Label Grid.Row="1" Grid.Column="0">Password</Label>
            <PasswordBox Grid.Row="1" Grid.Column="1" Name="LoginPassword" PasswordChar="*" ToolTip="Enter Password" TabIndex="1" />

        </Grid>
    </StackPanel>
</UserControl>

Can anyone point out what I'm doing wrong? As far as I'm aware, the FocusManager.FocusedElement="{Binding ElementName=Username}" should be sufficient to set the focus.

Obsidian Phoenix
  • 4,083
  • 1
  • 22
  • 60
  • What i would do is create a dummy converter and place it in the binding FocusManager.FocusedElement="{Binding ElementName=Username , Path=. , Converter={StaticResource SomeDummyConverter}}" now place a BreakPoint in the converter's convert function and observer your TextBox element , it might be disabled (IsEnabled = False) while you set focus . – eran otzap Nov 30 '13 at 09:39

2 Answers2

19

As per FocusManager documentation -

Logical focus pertains to the FocusManager.FocusedElement within a specific focus scope.

So, its not necessary that element with logical focus will have keyboard focus as well but vice versa is true i.e. element with keyboard focus will surely have a logical focus as well.

As stated in documentation FocusManager.FocusedElement guarantees logical focus and not keyboard focus. So what you can do is create an attach behaviour similar to FocusManager.FocusedElement which will set keyboard focus on an element.

You can refer to this for setting keyboard focus using attached behaviour - Setting keyboard focus in WPF.

Code from that article -

namespace Invoices.Client.Wpf.Behaviors
{
    using System.Windows;
    using System.Windows.Input;

    public static class KeyboardFocus
    {
        public static readonly DependencyProperty OnProperty;

        public static void SetOn(UIElement element, FrameworkElement value)
        {
            element.SetValue(OnProperty, value);
        }

        public static FrameworkElement GetOn(UIElement element)
        {
            return (FrameworkElement)element.GetValue(OnProperty);
        }

        static KeyboardFocus()
        {
            OnProperty = DependencyProperty.RegisterAttached("On", typeof(FrameworkElement), typeof(KeyboardFocus), new PropertyMetadata(OnSetCallback));
        }

        private static void OnSetCallback(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs dependencyPropertyChangedEventArgs)
        {
            var frameworkElement = (FrameworkElement)dependencyObject;
            var target = GetOn(frameworkElement);

            if (target == null)
                return;

            frameworkElement.Loaded += (s, e) => Keyboard.Focus(target);
        }
    }
}

Use in XAML -

<UserControl xmlns:behaviors="clr-namespace:Invoices.Client.Wpf.Behaviors">
    <Grid behaviors:KeyboardFocus.On="{Binding ElementName=TextBoxToFocus}">
        <TextBox x:Name="TextBoxToFocus" />
    </Grid>
</UserControl>
Andrew Roberts
  • 573
  • 6
  • 7
Rohit Vats
  • 79,502
  • 12
  • 161
  • 185
  • 2
    Preferred approach, IMO. Keeps it in XAML. – DonBoitnott Aug 01 '18 at 13:12
  • 1
    This is awesome. I would also extract the target retrieval & Keyboard.Focus(target) to an 'OnLoaded' method and add textBox.CaretIndex = textBox.Text.Length to improve functionality upon update. – GeorgiG Aug 19 '19 at 06:05
6

FocusManager.FocusedElement="{Binding ElementName=Username}" sets logical focus but not physical focus.

Physical focus is the normal focus, logical focus is kinda a second focus which is still a little bit buggy in wpf 4.0.

I would suggest you to use Keyboard.Focus(this.Username).

sirdank
  • 3,351
  • 3
  • 25
  • 58
dev hedgehog
  • 8,698
  • 3
  • 28
  • 55
  • I have no idea what you talking about futhmore if you are about to claim that somebody posted something wrong always write why is that so. There is nothing wrong in my answer. I will quote msdn for you Eran. "The FocusedElement is the element which has logical focus for a specific focus scope. This object may or may not have keyboard focus. Keyboard focus refers to the element that receives keyboard input." http://msdn.microsoft.com/en-us/library/system.windows.input.focusmanager.focusedelement(v=vs.110).aspx (See the part with logical focus, logical focus is not real focus) – dev hedgehog Nov 30 '13 at 09:56
  • That worked when adding it to the Loaded event in the codebehind for the View. Thanks. However, is there a way to do this via markup in the xaml? I can live with it in codebehind, but I'd prefer to avoid it if at all possible. – Obsidian Phoenix Nov 30 '13 at 10:42
  • @ObsidianPhoenix THere is approach even with FocusManager. Without any attached properties or code behind. I didnt know what kind of situation you stuck at so I suggested to you to use Keyboard.Focus(..) method but there are cases where focusmanager works too. – dev hedgehog Nov 30 '13 at 11:08