Set Focus on Textbox in Wpf from View Model

Set focus on TextBox in WPF from view model

Let me answer to your question in three parts.

  1. I'm wondering what is "cs.txtCompanyID" in your example? Is it a TextBox control? If yes, then you are on a wrong way. Generally speaking it's not a good idea to have any reference to UI in your ViewModel. You can ask "Why?" but this is another question to post on Stackoverflow :).

  2. The best way to track down issues with Focus is... debugging .Net source code. No kidding. It saved me a lot of time many times. To enable .net source code debugging refer to Shawn Bruke's blog.

  3. Finally, general approach that I use to set focus from ViewModel is Attached Properties. I wrote very simple attached property, which can be set on any UIElement. And it can be bound to ViewModel's property "IsFocused" for example. Here it is:

    public static class FocusExtension
    {
    public static bool GetIsFocused(DependencyObject obj)
    {
    return (bool) obj.GetValue(IsFocusedProperty);
    }

    public static void SetIsFocused(DependencyObject obj, bool value)
    {
    obj.SetValue(IsFocusedProperty, value);
    }

    public static readonly DependencyProperty IsFocusedProperty =
    DependencyProperty.RegisterAttached(
    "IsFocused", typeof (bool), typeof (FocusExtension),
    new UIPropertyMetadata(false, OnIsFocusedPropertyChanged));

    private static void OnIsFocusedPropertyChanged(
    DependencyObject d,
    DependencyPropertyChangedEventArgs e)
    {
    var uie = (UIElement) d;
    if ((bool) e.NewValue)
    {
    uie.Focus(); // Don't care about false values.
    }
    }
    }

    Now in your View (in XAML) you can bind this property to your ViewModel:

    <TextBox local:FocusExtension.IsFocused="{Binding IsUserNameFocused}" />

Hope this helps :). If it doesn't refer to the answer #2.

Cheers.

How to set Focus to a WPF Control using MVVM?

Generally, when we want to use a UI event while adhering to the MVVM methodology, we create an Attached Property:

public static DependencyProperty IsFocusedProperty = DependencyProperty.RegisterAttached("IsFocused", typeof(bool), typeof(TextBoxProperties), new UIPropertyMetadata(false, OnIsFocusedChanged));

public static bool GetIsFocused(DependencyObject dependencyObject)
{
return (bool)dependencyObject.GetValue(IsFocusedProperty);
}

public static void SetIsFocused(DependencyObject dependencyObject, bool value)
{
dependencyObject.SetValue(IsFocusedProperty, value);
}

public static void OnIsFocusedChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs dependencyPropertyChangedEventArgs)
{
TextBox textBox = dependencyObject as TextBox;
bool newValue = (bool)dependencyPropertyChangedEventArgs.NewValue;
bool oldValue = (bool)dependencyPropertyChangedEventArgs.OldValue;
if (newValue && !oldValue && !textBox.IsFocused) textBox.Focus();
}

This property is used like this:

<TextBox Attached:TextBoxProperties.IsFocused="{Binding IsFocused}" ... />

Then we can focus the TextBox from the view model by changing the IsFocused property to true:

IsFocused = false; // You may need to set it to false first if it is already true
IsFocused = true;

How to set focus to textbox using MVVM?

You can do this by adding a property to your ViewModel (or use an existing property) that indicates when the SetFocus should happen but the View should be responsible for actually setting the focus since that is purely View related.

You can do this with a DataTrigger.

View:

<Grid Name="LayoutRoot" DataContext="{StaticResource MyViewModelInstance}">
<Grid.Style>
<Style>
<Style.Triggers>
<DataTrigger Binding="{Binding UserShouldEditValueNow}" Value="True">
<Setter Property="FocusManager.FocusedElement" Value="{Binding ElementName=PropertySearch}"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Grid.Style>
<TextBox Name="PropertySearch" Text="{Binding UpdateSourceTrigger=PropertyChanged, Mode=TwoWay, Path=PropertySearch, ValidatesOnDataErrors=True}" Width="110" Height="25" Margin="10" />
</Grid>

ViewModel:

// When you think the view should set focus on a control
this.UserShouldEditValueNow = true;

The example above is simplified by just using a boolean ViewModel property "UserShouldEditValueNow". You can add a property like this to your ViewModel or use some other exising property that indicates this state.

Note: So why is it done this way in MVVM? One reason is, suppose the View author decided to replace the TextBox with a ComboBox, or even better, suppose your property was an integer value that had both a TextBox to view/edit the number and a Slider as another way to edit the same value, both controls bound to the same property... how would the ViewModel know which control to set focus on? (when it shouldn't even know what control, or controls, are bound to it in the first place) This way the View can select which control to focus by changing the ElementName binding target in the DataTrigger Setter.

Happy coding!

MVVM Focus To Textbox

I have documented a "pure MVVM" way to do this in my answer to a similar problem. The solution involves using Attached Properties and a framework for passing interface commands from the ViewModel back to the View.

Setting Focus on TextBox from ViewModel in WPF

If you have a UserControl then you also have CodeBehind.

Place this inside your codebehind and you will do fine.

this.Loaded += (o, e) => { Keyboard.Focus(textBox1) }

Place this inside your UserControl XAML if you wish to listen to validation errors.

<UserControl>
<Grid Validation.Error="OnValidationError">
<TextBox Text{Binding ..., NotifyOnValidationError=true } />
</Grid>
<UserControl>

Inside the CodeBehind of your UserControl you will have something like this:

public void OnValidationError(o , args)
{
if(o is TextBox)
{
(TextBox)o).Text = string.Empty;
}
}

WPF-MVVM: Setting UI control focus from ViewModel

Use the IsFocused Attached Property as suggested in the Answer here: Set focus on textbox in WPF from view model (C#)

Then you can simply bind to a property in your viewmodel.

How to read TextBox focus from ViewModel - MVVM Light

There are several ways how you can achieve this, some of them:

1) Use behavior:

  • You need System.Windows.Interactivity.dll
  • Behavior (setting IsFocused property will not make element focused, you need slightly extend behavior in order to achieve this)

    public class FocusChangedBehavior : Behavior<UIElement>
    {
    public static readonly DependencyProperty IsFocusedProperty =
    DependencyProperty.Register(
    nameof(IsFocused),
    typeof(bool),
    typeof(FocusChangedBehavior),
    new FrameworkPropertyMetadata(default(bool),
    FrameworkPropertyMetadataOptions.BindsTwoWayByDefault));

    public bool IsFocused
    {
    get { return (bool)this.GetValue(IsFocusedProperty); }
    set { this.SetValue(IsFocusedProperty, value); }
    }

    /// <inheritdoc />
    protected override void OnAttached()
    {
    this.AssociatedObject.GotFocus += this.AssociatedObjectFocused;
    this.AssociatedObject.LostFocus += this.AssociatedObjectUnfocused;
    }

    /// <inheritdoc />
    protected override void OnDetaching()
    {
    this.AssociatedObject.GotFocus -= this.AssociatedObjectFocused;
    this.AssociatedObject.LostFocus -= this.AssociatedObjectUnfocused;
    }

    private void AssociatedObjectFocused(object sender, RoutedEventArgs e)
    {
    this.IsFocused = true;
    }

    private void AssociatedObjectUnfocused(object sender, RoutedEventArgs e)
    {
    this.IsFocused = false;
    }
    }
  • In XAML you bind IsFocused to property in ViewModel.


    xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"

    <TextBox x:Name="textBox1" Text="Text Box 1">
    <i:Interaction.Behaviors>
    <local:FocusChangedBehavior IsFocused="{Binding IsFocusedTxt1}" />
    </i:Interaction.Behaviors>
    </TextBox>

    <TextBox x:Name="textBox2" Text="Text Box 2">
    <i:Interaction.Behaviors>
    <local:FocusChangedBehavior IsFocused="{Binding IsFocusedTxt2}" />
    </i:Interaction.Behaviors>
    </TextBox>
  • Finally in View-Model create properties

    public bool IsFocusedTxt1 { get; set; }

    public bool IsFocusedTxt2 { get; set; }




2) Alternatively you could you use EventTrigger in the XAML

  • You need System.Windows.Interactivity.dll and MicrosoftExpressionInteractions (For the ActionCommand)
  • Event Triggers:

    <TextBox x:Name="textBox1" Text="Text Box 1">
    <i:Interaction.Triggers>
    <i:EventTrigger EventName="GotFocus">
    <i:InvokeCommandAction Command="{Binding NotifyFocusedReceivedTxt1Command}" />
    </i:EventTrigger>
    </i:Interaction.Triggers>
    </TextBox>
  • In ViewModel create command NotifyFocusedReceivedTxt1Command

    public ICommand NotifyFocusedReceivedTxt1Command { get; }

    // in constructor
    this.NotifyFocusedReceivedTxt1Command = new ActionCommand(this.FocusedReceivedTxt1);

    // and method
    private void FocusedReceivedTxt1()
    {
    // Your logic
    }
  • Also, if you don't want introduce many command/properties you could use same command and pass different textboxes by setting CommandParameter (slightly breaks MVVM, but not critically)

    <TextBox x:Name="textBox1" Text="Text Box 1">
    <i:Interaction.Triggers>
    <i:EventTrigger EventName="GotFocus">
    <i:InvokeCommandAction Command="{Binding NotifyFocusedReceivedCommand}"
    CommandParameter="{Binding ., ElementName=textBox1}" />
    </i:EventTrigger>
    </i:Interaction.Triggers>
    </TextBox>

    <TextBox x:Name="textBox2" Text="Text Box 2">
    <i:Interaction.Triggers>
    <i:EventTrigger EventName="GotFocus">
    <i:InvokeCommandAction Command="{Binding NotifyFocusedReceivedCommand}"
    CommandParameter="{Binding ., ElementName=textBox2}" />
    </i:EventTrigger>
    </i:Interaction.Triggers>
    </TextBox>

    and

    public ICommand NotifyFocusedReceivedCommand { get; }

    // in constructor
    this.NotifyFocusedReceivedCommand = new ActionCommand(this.FocusedReceived);

    // and method
    private void FocusedReceived(object control)
    {
    var txt = (TextBox)control;
    bool isFocused = txt.IsFocused;
    string name = txt.Name;
    }


Related Topics



Leave a reply



Submit