Set focus on TextBox in WPF from view model
Solution 1
Let me answer to your question in three parts.
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 :).
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.
-
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.
Solution 2
I know this question has been answered a thousand times over by now, but I made some edits to Anvaka's contribution that I think will help others that had similar issues that I had.
Firstly, I changed the above Attached Property like so:
public static class FocusExtension
{
public static readonly DependencyProperty IsFocusedProperty =
DependencyProperty.RegisterAttached("IsFocused", typeof(bool?), typeof(FocusExtension), new FrameworkPropertyMetadata(IsFocusedChanged){BindsTwoWayByDefault = true});
public static bool? GetIsFocused(DependencyObject element)
{
if (element == null)
{
throw new ArgumentNullException("element");
}
return (bool?)element.GetValue(IsFocusedProperty);
}
public static void SetIsFocused(DependencyObject element, bool? value)
{
if (element == null)
{
throw new ArgumentNullException("element");
}
element.SetValue(IsFocusedProperty, value);
}
private static void IsFocusedChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var fe = (FrameworkElement)d;
if (e.OldValue == null)
{
fe.GotFocus += FrameworkElement_GotFocus;
fe.LostFocus += FrameworkElement_LostFocus;
}
if (!fe.IsVisible)
{
fe.IsVisibleChanged += new DependencyPropertyChangedEventHandler(fe_IsVisibleChanged);
}
if (e.NewValue != null && (bool)e.NewValue)
{
fe.Focus();
}
}
private static void fe_IsVisibleChanged(object sender, DependencyPropertyChangedEventArgs e)
{
var fe = (FrameworkElement)sender;
if (fe.IsVisible && (bool)fe.GetValue(IsFocusedProperty))
{
fe.IsVisibleChanged -= fe_IsVisibleChanged;
fe.Focus();
}
}
private static void FrameworkElement_GotFocus(object sender, RoutedEventArgs e)
{
((FrameworkElement)sender).SetValue(IsFocusedProperty, true);
}
private static void FrameworkElement_LostFocus(object sender, RoutedEventArgs e)
{
((FrameworkElement)sender).SetValue(IsFocusedProperty, false);
}
}
My reason for adding the visibility references were tabs. Apparently if you used the attached property on any other tab outside of the initially visible tab, the attached property didn't work until you manually focused the control.
The other obstacle was creating a more elegant way of resetting the underlying property to false when it lost focus. That's where the lost focus events came in.
<TextBox
Text="{Binding Description}"
FocusExtension.IsFocused="{Binding IsFocused}"/>
If there's a better way to handle the visibility issue, please let me know.
Note: Thanks to Apfelkuacha for the suggestion of putting the BindsTwoWayByDefault in the DependencyProperty. I had done that long ago in my own code, but never updated this post. The Mode=TwoWay is no longer necessary in the WPF code due to this change.
Solution 3
I think the best way is to keep the MVVM principle clean, so basically you must use the Messenger Class provided with the MVVM Light and here is how to use it:
in your viewmodel(exampleViewModel.cs):write the following
Messenger.Default.Send<string>("focus", "DoFocus");
now in your View.cs(not the XAML the view.xaml.cs) write the following in the constructor
public MyView()
{
InitializeComponent();
Messenger.Default.Register<string>(this, "DoFocus", doFocus);
}
public void doFocus(string msg)
{
if (msg == "focus")
this.txtcode.Focus();
}
that method owrks just fine and with less code and maintaining MVVM standards
Solution 4
None of these worked for me exactly, but for the benefit of others, this is what I ended up writing based on some of the code already provided here.
Usage would be as follows:
<TextBox ... h:FocusBehavior.IsFocused="True"/>
And the implementation would be as follows:
/// <summary>
/// Behavior allowing to put focus on element from the view model in a MVVM implementation.
/// </summary>
public static class FocusBehavior
{
#region Dependency Properties
/// <summary>
/// <c>IsFocused</c> dependency property.
/// </summary>
public static readonly DependencyProperty IsFocusedProperty =
DependencyProperty.RegisterAttached("IsFocused", typeof(bool?),
typeof(FocusBehavior), new FrameworkPropertyMetadata(IsFocusedChanged));
/// <summary>
/// Gets the <c>IsFocused</c> property value.
/// </summary>
/// <param name="element">The element.</param>
/// <returns>Value of the <c>IsFocused</c> property or <c>null</c> if not set.</returns>
public static bool? GetIsFocused(DependencyObject element)
{
if (element == null)
{
throw new ArgumentNullException("element");
}
return (bool?)element.GetValue(IsFocusedProperty);
}
/// <summary>
/// Sets the <c>IsFocused</c> property value.
/// </summary>
/// <param name="element">The element.</param>
/// <param name="value">The value.</param>
public static void SetIsFocused(DependencyObject element, bool? value)
{
if (element == null)
{
throw new ArgumentNullException("element");
}
element.SetValue(IsFocusedProperty, value);
}
#endregion Dependency Properties
#region Event Handlers
/// <summary>
/// Determines whether the value of the dependency property <c>IsFocused</c> has change.
/// </summary>
/// <param name="d">The dependency object.</param>
/// <param name="e">The <see cref="System.Windows.DependencyPropertyChangedEventArgs"/> instance containing the event data.</param>
private static void IsFocusedChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
// Ensure it is a FrameworkElement instance.
var fe = d as FrameworkElement;
if (fe != null && e.OldValue == null && e.NewValue != null && (bool)e.NewValue)
{
// Attach to the Loaded event to set the focus there. If we do it here it will
// be overridden by the view rendering the framework element.
fe.Loaded += FrameworkElementLoaded;
}
}
/// <summary>
/// Sets the focus when the framework element is loaded and ready to receive input.
/// </summary>
/// <param name="sender">The sender.</param>
/// <param name="e">The <see cref="System.Windows.RoutedEventArgs"/> instance containing the event data.</param>
private static void FrameworkElementLoaded(object sender, RoutedEventArgs e)
{
// Ensure it is a FrameworkElement instance.
var fe = sender as FrameworkElement;
if (fe != null)
{
// Remove the event handler registration.
fe.Loaded -= FrameworkElementLoaded;
// Set the focus to the given framework element.
fe.Focus();
// Determine if it is a text box like element.
var tb = fe as TextBoxBase;
if (tb != null)
{
// Select all text to be ready for replacement.
tb.SelectAll();
}
}
}
#endregion Event Handlers
}
Solution 5
This is an old thread, but there doesn't seem to be an answer with code that addresses the issues with Anavanka's accepted answer: it doesn't work if you set the property in the viewmodel to false, or if you set your property to true, the user manually clicks on something else, and then you set it to true again. I couldn't get Zamotic's solution to work reliably in these cases either.
Pulling together some of the discussions above gives me the code below which does address these issues I think:
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, null, OnCoerceValue));
private static object OnCoerceValue(DependencyObject d, object baseValue)
{
if ((bool)baseValue)
((UIElement)d).Focus();
else if (((UIElement) d).IsFocused)
Keyboard.ClearFocus();
return ((bool)baseValue);
}
}
Having said that, this is still complex for something that can be done in one line in codebehind, and CoerceValue isn't really meant to be used in this way, so maybe codebehind is the way to go.
Comments
-
priyanka.sarkar over 3 years
I have a
TextBox
and aButton
in my view.Now I am checking a condition upon button click and if the condition turns out to be false, displaying the message to the user, and then I have to set the cursor to the
TextBox
control.if (companyref == null) { var cs = new Lipper.Nelson.AdminClient.Main.Views.ContactPanels.CompanyAssociation(); MessageBox.Show("Company does not exist.", "Error", MessageBoxButton.OK, MessageBoxImage.Exclamation); cs.txtCompanyID.Focusable = true; System.Windows.Input.Keyboard.Focus(cs.txtCompanyID); }
The above code is in the ViewModel.
The
CompanyAssociation
is the view name.But the cursor is not getting set in the
TextBox
.The xaml is:
<igEditors:XamTextEditor Name="txtCompanyID" KeyDown="xamTextEditorAllowOnlyNumeric_KeyDown" ValueChanged="txtCompanyID_ValueChanged" Text="{Binding Company.CompanyId, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Width="{Binding ActualWidth, ElementName=border}" Grid.Column="1" Grid.Row="0" VerticalAlignment="Top" HorizontalAlignment="Stretch" Margin="0,5,0,0" IsEnabled="{Binding Path=IsEditable}"/> <Button Template="{StaticResource buttonTemp1}" Command="{Binding ContactCommand}" CommandParameter="searchCompany" Content="Search" Width="80" Grid.Row="0" Grid.Column="2" VerticalAlignment="Top" Margin="0" HorizontalAlignment="Left" IsEnabled="{Binding Path=IsEditable}"/>
-
matze8426 almost 6 yearsWhen you are using caliburn.micro this is an excellent solution.
-
-
Sam about 14 yearsCool idea. I need to set IsUserNameFocused to true, then false again to get this working, is this right?
-
Anvaka about 14 yearsThank you Sam. It depends. Sometimes it's enough to set it to true only once.
-
Josh G almost 13 yearsOP is using WPF. Focus code for WinForms is not going to help.
-
Rob almost 13 years@Anvaka how do you do the binding in your view model? is it a property or an ICommand or somthing else, I can't seem to get my head around this bit.
-
Anvaka almost 13 years@Rob it's just a property, named "IsUserNameFocused".
-
Hiren Gondaliya over 12 yearsWhy have you a if statement? the _isFocused once set to false will just be changed to value on the next line.
-
Rachel over 12 yearsYou should also call
Keyboard.Focus(uie);
from yourOnIsFocusedPropertyChanged
event if you want your control to receive Keyboard Focus as well as Logical Focus -
RichardOD about 12 years@Tyrsius You can round this issue by getting the dependency property to Coerce, see here- social.msdn.microsoft.com/Forums/en-US/wpf/thread/…
-
Ε Г И І И О about 12 yearsWell if you want to keep the MVVM principle clean, you wouldn't be writing code in your code behind in the first place. I believe the attached property approach is much cleaner. It doesn't introduce a lot of magic strings in your view model as well.
-
Admin over 11 yearsI chhanged "object fe = (FrameworkElement)d;" to "FrameworkElement fe = (FrameworkElement)d;" so the intellisense works
-
user3245801 about 11 yearsEl Nino: Where exactly did you get the idea there shouldn't be anything in your view code-behind? Anything that is UI-related should be in the view's code-behind. Setting focus of UI elements should Definitely be in the view's code-behind. Let the viewmodel figure out when to send the message; let the view figure out what to do with the message. That is what M-V-VM does: separates the concerns of data model, business logic, and UI.
-
ygoe about 11 yearsHow is this supposed to be used? If I set my property to true, the control is focused. But it will always be focused again when I come back to this view. Resetting it from OnIsFocusedPropertyChanged doesn't change this. Resetting it directly after setting it from the ViewModel does not focus anything anymore. It doesn't work. What have those 70 upvoters done exactly?
-
ygoe about 11 yearsStill doesn't solve the problem. The element stays focused every time I come back to it.
-
HelloSam about 11 yearsThis works well for me except I need to add a "if (e.Source == e.OriginalSource)" check in the GotFocus/LostFocus or else it stackoverflows (literally) when used on my UserControl, which does redirect the focus to inner component. I removed the Visible checks, accepting the fact that it works just like .Focus() method. If .Focus() doesn't work, the binding shouldn't work - and that's ok for my scenario.
-
WiiMaxx almost 11 years@Anvaka i tryed something based on this solution but i got stucked please take a look at it Click HERE
-
Tore Aurstad almost 11 yearsI wrestled with setting the focus of a textbox in a dialog I display in a WPF application displaying the dialog as a UserControl. I managed to finally make the focusing working, after trying out many different approaches. Your code above worked after I adjusted it to use Dispatcher.CurrentDispatcher.BeginInvoke to do the Focus call: Dispatcher.CurrentDispatcher.BeginInvoke((Action)(() => { uie.Focus(); //Don't care about false values }));
-
Owen Johnson over 10 yearsyou can change
obj.SetValue(IsFocusedProperty, value);
toobj.SetValue(IsFocusedProperty, false);
and not have to set false and true again. -
Simon D. over 10 yearsI also changed the callback to this:
...if ((bool)e.NewValue && uie.Dispatcher != null) { uie.Dispatcher.BeginInvoke(DispatcherPriority.Normal, (Action)(() => uie.Focus())); // invoke behaves nicer, if e.g. you have some additional handler attached to 'GotFocus' of UIE. uie.SetValue(IsFocusedProperty, false); // reset bound value if possible, to allow setting again ...
Sometimes I even have to reset the 'IsFocused' to false in the ViewModel, if I want to set the focus multiple times. But then it works, where some other methods failed. -
ygoe over 10 yearsBased on this suggestion, I've implemented my own ViewCommandManager that handles invoking commands in connected views. It's basically the other direction of regular Commands, for these cases when a ViewModel needs to do some action in its View(s). It uses reflection like data-bound commands and WeakReferences to avoid memory leaks. dev.unclassified.de/source/viewcommand (also on CodeProject)
-
Gordon Slysz about 10 yearsI used this method to print out WPF FlowDocuments. Worked nicely. Thanks
-
Contango over 9 yearsThis worked beautifully for me. I'm using Visual Studio 2013 / MVVM Light. To effectively remove the visible focus from all textboxes, you can set the focus to something like a TextBlock which isn't normally editable.
-
Asheh over 9 yearsYOU ABSOLUTE GENIUS. Spent a whole day on this. Thanks!! I have been trying to find a way to focus an element and hide it when it doesn't have focus. But this really screws up the dependency properties. This solution worked for me.
-
Olaru Mircea about 9 yearsI am using this in WF 4.5. On IsFocusedChanged i have a scenario (an Activity gets reloaded ) where e.NewValue is null and throws a exception so check that first. Everything works fine with this minor change.
-
R00st3r over 7 yearsThanks this wprks Great :) I just added ' {BindsTwoWayByDefault = true}' at 'FrameworkPropertyMetadata' to set the default mode to TwoWayBinding so it is not needed on every Binding
-
NathanAldenSr over 7 yearsThis works consistently, whereas the accepted answer does not. Thanks!
-
Bigeyes over 7 yearsI want one in Silverlight? Can we use it?
-
M3SSYM4RV1N over 6 yearsStill an amazing solution 8 years later. Thanks!
-
scsfdev over 6 yearsAlthough it is good to know how to make use of dependency injection from view model, I think sending out the message (this solution) is more straight forward and easy.
-
Mark Olbert about 6 yearsI realize this is an old answer, but I'm running into a situation where the IsEnabled property of the control I want to shift the focus to is tied to a multi-value converter. Apparently, the GotFocus event handler gets called before the multi-value converter does...which means the control, at that point, is disabled, so as soon as GotFocus completes, LostFocus gets called (I guess because the control is still disabled). Any thoughts on how to handle that?
-
walterhuang almost 6 yearsafter you set the focus and another control gets the focus, to set the focus again won't work because IsFocused is still true. Need to force it to false and then true.
public bool IsFocused { get { return _isFocused; } set { if (_isFocused == value) { _isFocused = false; OnPropertyChanged(); } _isFocused = value; OnPropertyChanged(); } }
-
Michael K over 5 yearsI used this in a pure MVVM solution. Works great, thanks
-
Apfelkuacha over 5 years@MarkOlbert use
fe.Dispatcher.BeginInvoke(new Action(() => { fe.Focus(); }), DispatcherPriority.Loaded);
that it is updated after it is loaded. More info here: telerik.com/forums/isfocused-property#OXgFYZFOg0WZ2rxidln61Q -
Daniel Žeimo over 5 yearsVery good solution, helped a lot. Only problem that I had was when I returned from one window to another it threw null exception so I added check for null in
if (e.NewValue != null && (bool)e.NewValue)
and now works well -
Joshua Frank over 4 yearsThis doesn't work for me. When I try it, the control gets the focus but doesn't show the caret. Or maybe it's losing the focus and not showing it for that reason, but either way the focus doesn't go to the right place.
-
Welcor over 4 yearsthis solution is easier to use in the viewModel. Just set the bound property to true once. after that reusing Notify property change on the property will get it into focus again. The accepted answer instead would need set true -> notify -> set false -> notify every time, which is quite annoying. the more i have to work with wpf mvvm, the more i love the time with win forms and code behind... single line solutions where so awesome..
-
Welcor over 4 yearswhile this works better in viewModel, it crashes when comboBox with isEditable=true is used. the accepted answer does not crash in this case
-
user2430797 about 4 yearsI like this. This works well if you want to set the initial focus.
-
Marinpietri almost 2 yearsThks so much !!! it's works fine