12

I have an MVVM setup with a mainwindow that contains a ContentControl. I set this to a particular viewmodel which then maps to a view. A view is a usercontrol. I want to be able to set the default keyboard focus to a default element in the usercontrol(View) when it loads so the application can eventually be driven just by using up, down, left, right and enter. Some of my failed attempts are setting

FocusManager.FocusedElement="{Binding ElementName=DefaultElement}"

in my content control tag. This sets the logical focus but not the keyboard focus

I'd rather keep the solution in xaml if possable but have tried placing the following in code behind.

Keyboard.Focus(DefaultElement);  

This does not work but if I popup a message box first it does. I'm a little confused as to why.

MessageBox.Show(Keyboard.FocusedElement.ToString());
Keyboard.Focus(DefaultElement);

EDIT:::: I just placed this in my onloaded event of my user control. It seems to work but can anyone see any issues that might arrise at this priority level. I.E a circumstance when the action will never run?

Dispatcher.BeginInvoke(
            DispatcherPriority.ContextIdle,
            new Action(delegate()
                {
                    Keyboard.Focus(DefaultElement);
                }));
Oli
  • 2,996
  • 3
  • 28
  • 50

3 Answers3

30

It seems that this wpf the you have to implement a workaround on a case by case basis. The solution that seemed to work best, most of the time for me was to insert the focus code inside the dispatcher when OnVisible was changed. This sets the focus not only when the View/Usercontrol loads but also if you a changing Views by way of Visibility. If you Hide and then Show a ContentControl that is mapped to your ViewModels then the Loaded event won't fire and you'll be forced to Mouse input, or tabbing (Not so good if you want to navigate your app with a remote control). VisibilityChanged will always fire however. This is what I ended up with for my listbox.

private void ItemsFlowListBox_IsVisibleChanged(object sender, DependencyPropertyChangedEventArgs e)
    {
        if ((bool)e.NewValue == true)
        {
            Dispatcher.BeginInvoke(
            DispatcherPriority.ContextIdle,
            new Action(delegate()
            {
                ItemsFlowListBox.Focus();
                ItemsFlowListBox.ScrollIntoView(ItemsFlowListBox.SelectedItem);
            }));
        }
    }
Oli
  • 2,996
  • 3
  • 28
  • 50
1

I had the same symptom for a WPF UserControl hosted in a Winforms application. Just wanted to note I was about to try this solution when I found a normal TabIndex in the Winforms app fixed it

Per How to set which control gets the focus on application start

"The one with the minimum tab index automatically gets the focus (assuming the TabStop property is set to true). Just set the tab indices appropriately."

StayOnTarget
  • 11,743
  • 10
  • 52
  • 81
Tom Jones
  • 11
  • 2
0

It's a tricky one with no easy answer. I'm currently doing this, although I'm not sure I like it:

    public MyView()
    {
        InitializeComponent();

        // When DataContext changes hook the txtName.TextChanged event so we can give it initial focus
        DataContextChanged +=
            (sender, args) =>
                {
                    txtName.TextChanged += OnTxtNameOnTextChanged;
                };
    }

    private void OnTxtNameOnTextChanged(object o, TextChangedEventArgs eventArgs)
    {
        // Setting focus will select all text in the TextBox due to the global class handler on TextBox
        txtName.Focus();
        // Now unhook the event handler, since it's no longer required
        txtName.TextChanged -= OnTxtNameOnTextChanged;
    }

And in case you're wondering what the global class handler does, it's this:

    protected override void OnStartup(StartupEventArgs e)
    {
        ...

        // Register a global handler for this app-domain to select all text in a textBox when
        // the textBox receives keyboard focus.
        EventManager.RegisterClassHandler(
            typeof (TextBox), UIElement.GotKeyboardFocusEvent,
            new RoutedEventHandler((sender, args) => ((TextBox) sender).SelectAll()));

which auto selects TextBox text when receiving keyboard focus.

Phil
  • 42,255
  • 9
  • 100
  • 100
  • Thanks Phil but this didn't seem to work for me in my MVVM situation. It appears that after loading the view the focus is reset so the focus request has to be delayed. This seems a valid solution in your context though. – Oli Mar 08 '12 at 21:11