c# wpf mvvm textbox focus

Establecer el foco en el cuadro de texto en WPF desde el modelo de vista(C#)



mvvm textbox (18)

Tengo un TextBox y un Button en mi opinión.

Ahora estoy comprobando una condición al hacer clic en el botón y si la condición resulta ser falsa, mostrar el mensaje al usuario y luego tengo que establecer el cursor en el control TextBox .

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); }

El código anterior está en ViewModel.

La CompanyAssociation es el nombre de la vista.

Pero el cursor no se está configurando en el TextBox .

El xaml es:

<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}"/>


Considero que la solución de Crucial para el problema de IsVisible es muy útil. No resolvió completamente mi problema, pero sí algo de código extra siguiendo el mismo patrón para el patrón IsEnabled.

Al método IsFocusedChanged agregué:

if (!fe.IsEnabled) { fe.IsEnabledChanged += fe_IsEnabledChanged; }

Y aquí está el controlador:

private static void fe_IsEnabledChanged(object sender, DependencyPropertyChangedEventArgs e) { var fe = (FrameworkElement)sender; if (fe.IsEnabled && (bool)((FrameworkElement)sender).GetValue(IsFocusedProperty)) { fe.IsEnabledChanged -= fe_IsEnabledChanged; fe.Focus(); } }


Creo que la mejor manera es mantener limpio el principio de MVVM, así que básicamente debes usar la Clase de Messenger provista con la MVVM Light y aquí te mostramos cómo usarla:

en su viewmodel (exampleViewModel.cs): escriba lo siguiente

Messenger.Default.Send<string>("focus", "DoFocus");

ahora en su View.cs (no el XAML el view.xaml.cs) escriba lo siguiente en el constructor

public MyView() { InitializeComponent(); Messenger.Default.Register<string>(this, "DoFocus", doFocus); } public void doFocus(string msg) { if (msg == "focus") this.txtcode.Focus(); }

ese método funciona bien y con menos código y manteniendo los estándares de MVVM


Déjame responder a tu pregunta en tres partes.

  1. Me pregunto qué es "cs.txtCompanyID" en tu ejemplo? ¿Es un control TextBox? Si es así, entonces estás en el camino equivocado. En general, no es una buena idea tener alguna referencia a la IU en su ViewModel. Puedes preguntar "¿Por qué?" pero esta es otra pregunta para publicar en :).

  2. La mejor forma de rastrear problemas con Focus es ... depurar el código fuente de .Net. En serio. Me ahorró muchas veces muchas veces. Para habilitar la depuración del código fuente de .net, consulte el blog de Shawn Bruke .

  3. Finalmente, el enfoque general que utilizo para establecer el foco desde ViewModel es Propiedades adjuntas. Escribí propiedad adjunta muy simple, que se puede configurar en cualquier UIElement. Y se puede vincular a la propiedad de ViewModel "IsFocused", por ejemplo. Aquí está:

    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. } } }

    Ahora en su Vista (en XAML) puede vincular esta propiedad a su ViewModel:

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

Espero que esto ayude :). Si no se refiere a la respuesta n. ° 2.

Aclamaciones.


El código brillante de Anvakas es para aplicaciones de escritorio de Windows. Si eres como yo y necesitas la misma solución para las aplicaciones de la Tienda Windows, este código puede ser útil:

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 PropertyMetadata(false, OnIsFocusedPropertyChanged)); private static void OnIsFocusedPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { if ((bool)e.NewValue) { var uie = d as Windows.UI.Xaml.Controls.Control; if( uie != null ) { uie.Focus(FocusState.Programmatic); } } } }


El problema es que una vez que IsUserNameFocused se establece en verdadero, nunca será falso. Esto lo resuelve manejando GotFocus y LostFocus para FrameworkElement.

Estaba teniendo problemas con el formato del código fuente, así que aquí hay un link


En mi caso, FocusExtension no funcionó hasta que cambié el método OnIsFocusedPropertyChanged. El original solo funcionaba en la depuración cuando un punto de interrupción detenía el proceso. En tiempo de ejecución, el proceso es demasiado rápido y no ocurre nada. Con esta pequeña modificación y la ayuda de nuestra amiga Task, esto está funcionando bien en ambos escenarios.

private static void OnIsFocusedPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { var uie = (UIElement)d; if ((bool)e.NewValue) { var action = new Action(() => uie.Dispatcher.BeginInvoke((Action)(() => uie.Focus()))); Task.Factory.StartNew(action); } }


En primer lugar, me gustaría agradecer a Avanka por ayudarme a resolver mi problema de enfoque. Sin embargo, hay un error en el código que publicó, es decir, en la línea: if (e.OldValue == null)

El problema que tuve fue que si primero haces clic en tu vista y enfocas el control, e.oldValue ya no es nulo. Luego, cuando configura la variable para enfocar el control por primera vez, esto da como resultado que los controladores de pérdida de foco y desenfoque no se configuren. Mi solución a esto fue la siguiente:

public static class ExtensionFocus { static ExtensionFocus() { BoundElements = new List<string>(); } public static readonly DependencyProperty IsFocusedProperty = DependencyProperty.RegisterAttached("IsFocused", typeof(bool?), typeof(ExtensionFocus), new FrameworkPropertyMetadata(false, IsFocusedChanged)); private static List<string> BoundElements; public static bool? GetIsFocused(DependencyObject element) { if (element == null) { throw new ArgumentNullException("ExtensionFocus GetIsFocused called with null element"); } return (bool?)element.GetValue(IsFocusedProperty); } public static void SetIsFocused(DependencyObject element, bool? value) { if (element == null) { throw new ArgumentNullException("ExtensionFocus SetIsFocused called with null element"); } element.SetValue(IsFocusedProperty, value); } private static void IsFocusedChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { var fe = (FrameworkElement)d; // OLD LINE: // if (e.OldValue == null) // TWO NEW LINES: if (BoundElements.Contains(fe.Name) == false) { BoundElements.Add(fe.Name); fe.LostFocus += OnLostFocus; fe.GotFocus += OnGotFocus; } if (!fe.IsVisible) { fe.IsVisibleChanged += new DependencyPropertyChangedEventHandler(fe_IsVisibleChanged); } if ((bool)e.NewValue) { fe.Focus(); } } private static void fe_IsVisibleChanged(object sender, DependencyPropertyChangedEventArgs e) { var fe = (FrameworkElement)sender; if (fe.IsVisible && (bool)((FrameworkElement)sender).GetValue(IsFocusedProperty)) { fe.IsVisibleChanged -= fe_IsVisibleChanged; fe.Focus(); } } private static void OnLostFocus(object sender, RoutedEventArgs e) { if (sender != null && sender is Control s) { s.SetValue(IsFocusedProperty, false); } } private static void OnGotFocus(object sender, RoutedEventArgs e) { if (sender != null && sender is Control s) { s.SetValue(IsFocusedProperty, true); } } }


Este es un hilo viejo, pero no parece haber una respuesta con código que resuelva los problemas con la respuesta aceptada de Anavanka: no funciona si configuras la propiedad en el modelo de vista en falso, o si configuras tu propiedad en cierto, el usuario hace clic manualmente en otra cosa, y luego lo configura a verdadero otra vez. No pude conseguir que la solución de Zamotic funcione de manera confiable en estos casos tampoco.

Reunir algunas de las discusiones anteriores me da el siguiente código que aborda estos problemas, creo:

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); } }

Habiendo dicho eso, esto todavía es complejo para algo que se puede hacer en una línea en código subyacente, y CoerceValue realmente no está destinado a ser usado de esta manera, así que quizás codebehind sea el camino a seguir.


Nadie parece haber incluido el último paso para facilitar la actualización de atributos mediante variables vinculadas. Esto es lo que se me ocurrió. Avíseme si hay una mejor manera de hacerlo.

XAML

<TextBox x:Name="txtLabel" Text="{Binding Label}" local:FocusExtension.IsFocused="{Binding txtLabel_IsFocused, Mode=TwoWay}" /> <Button x:Name="butEdit" Content="Edit" Height="40" IsEnabled="{Binding butEdit_IsEnabled}" Command="{Binding cmdCapsuleEdit.Command}" />

ViewModel

public class LoginModel : ViewModelBase { public string txtLabel_IsFocused { get; set; } public string butEdit_IsEnabled { get; set; } public void SetProperty(string PropertyName, string value) { System.Reflection.PropertyInfo propertyInfo = this.GetType().GetProperty(PropertyName); propertyInfo.SetValue(this, Convert.ChangeType(value, propertyInfo.PropertyType), null); OnPropertyChanged(PropertyName); } private void Example_function(){ SetProperty("butEdit_IsEnabled", "False"); SetProperty("txtLabel_IsFocused", "True"); } }


Ninguno de estos funcionó para mí exactamente, pero para el beneficio de otros, esto es lo que terminé escribiendo basado en algunos de los códigos que ya proporcioné aquí.

El uso sería el siguiente:

<TextBox ... h:FocusBehavior.IsFocused="True"/>

Y la implementación sería la siguiente:

/// <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 }


Para Silverlight:

using System.Windows; using System.Windows.Controls; using System.Windows.Interactivity; namespace MyProject.Behaviors { public class FocusBehavior : Behavior<Control> { protected override void OnAttached() { this.AssociatedObject.Loaded += AssociatedObject_Loaded; base.OnAttached(); } private void AssociatedObject_Loaded(object sender, RoutedEventArgs e) { this.AssociatedObject.Loaded -= AssociatedObject_Loaded; if (this.HasInitialFocus || this.IsFocused) { this.GotFocus(); } } private void GotFocus() { this.AssociatedObject.Focus(); if (this.IsSelectAll) { if (this.AssociatedObject is TextBox) { (this.AssociatedObject as TextBox).SelectAll(); } else if (this.AssociatedObject is PasswordBox) { (this.AssociatedObject as PasswordBox).SelectAll(); } else if (this.AssociatedObject is RichTextBox) { (this.AssociatedObject as RichTextBox).SelectAll(); } } } public static readonly DependencyProperty IsFocusedProperty = DependencyProperty.Register( "IsFocused", typeof(bool), typeof(FocusBehavior), new PropertyMetadata(false, (d, e) => { if ((bool)e.NewValue) { ((FocusBehavior)d).GotFocus(); } })); public bool IsFocused { get { return (bool)GetValue(IsFocusedProperty); } set { SetValue(IsFocusedProperty, value); } } public static readonly DependencyProperty HasInitialFocusProperty = DependencyProperty.Register( "HasInitialFocus", typeof(bool), typeof(FocusBehavior), new PropertyMetadata(false, null)); public bool HasInitialFocus { get { return (bool)GetValue(HasInitialFocusProperty); } set { SetValue(HasInitialFocusProperty, value); } } public static readonly DependencyProperty IsSelectAllProperty = DependencyProperty.Register( "IsSelectAll", typeof(bool), typeof(FocusBehavior), new PropertyMetadata(false, null)); public bool IsSelectAll { get { return (bool)GetValue(IsSelectAllProperty); } set { SetValue(IsSelectAllProperty, value); } } } }

LoginViewModel.cs:

public class LoginModel : ViewModelBase { .... private bool _EmailFocus = false; public bool EmailFocus { get { return _EmailFocus; } set { if (value) { _EmailFocus = false; RaisePropertyChanged("EmailFocus"); } _EmailFocus = value; RaisePropertyChanged("EmailFocus"); } } ... }

Login.xaml:

xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity" xmlns:beh="clr-namespace:MyProject.Behaviors" <TextBox Text="{Binding Email, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"> <i:Interaction.Behaviors> <beh:FocusBehavior IsFocused="{Binding EmailFocus}" IsSelectAll="True"/> </i:Interaction.Behaviors> </TextBox>

O

<TextBox Text="{Binding Email, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"> <i:Interaction.Behaviors> <beh:FocusBehavior HasInitialFocus="True" IsSelectAll="True"/> </i:Interaction.Behaviors> </TextBox>

Para establecer el foco solo debe hacerlo en el código:

EmailFocus = true;

Recuerde que este complemento es parte de una página html, por lo que otros controles en la página pueden tener el foco

if (!Application.Current.IsRunningOutOfBrowser) { System.Windows.Browser.HtmlPage.Plugin.Focus(); }


Para aquellos que intentaron usar la solución de Anvaka anterior, tuve problemas con el enlace que solo funcionaba la primera vez, ya que lostfocus no actualizaba la propiedad a falso. Puede establecer manualmente la propiedad en falso y luego en verdadero todo el tiempo, pero una mejor solución podría ser hacer algo como esto en su propiedad:

bool _isFocused = false; public bool IsFocused { get { return _isFocused ; } set { _isFocused = false; _isFocused = value; base.OnPropertyChanged("IsFocused "); } }

De esta forma, solo necesitas configurarlo en verdadero, y se enfocará.


Podría usar el patrón de diseño ViewCommand . Describe un método para que el patrón de diseño de MVVM controle una Vista desde un ViewModel con comandos.

Lo he implementado en base a la sugerencia del Rey A.Majid de usar la clase MVVM Light Messenger. La clase ViewCommandManager maneja comandos de invocación en vistas conectadas. Básicamente es la otra dirección de Comandos regulares, para estos casos cuando un modelo de vista necesita hacer alguna acción en su vista. Utiliza la reflexión como comandos de datos y WeakReferences para evitar fugas de memoria.

http://dev.unclassified.de/source/viewcommand (también publicado en CodeProject)


Sé que esta pregunta ya fue respondida miles de veces, pero hice algunos cambios en la contribución de Anvaka que creo que ayudarán a otros que tenían problemas similares a los que yo tenía.

En primer lugar, cambié la propiedad adjunta anterior de la siguiente manera:

public static class FocusExtension { public static readonly DependencyProperty IsFocusedProperty = DependencyProperty.RegisterAttached("IsFocused", typeof(bool?), typeof(FocusExtension), new FrameworkPropertyMetadata(IsFocusedChanged)); 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 ((bool)e.NewValue) { fe.Focus(); } } private static void fe_IsVisibleChanged(object sender, DependencyPropertyChangedEventArgs e) { var fe = (FrameworkElement)sender; if (fe.IsVisible && (bool)((FrameworkElement)sender).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); } }

Mi razón para agregar las referencias de visibilidad eran pestañas. Aparentemente, si utilizó la propiedad adjunta en cualquier otra pestaña fuera de la pestaña inicialmente visible, la propiedad adjunta no funcionó hasta que no centró el control manualmente.

El otro obstáculo fue crear una forma más elegante de restablecer la propiedad subyacente a falso cuando perdió el foco. Ahí es donde entraron los eventos de foco perdido junto con asegurarse de establecer el enlace en el modo xaml a twoway.

<TextBox Text="{Binding Description}" FocusExtension.IsFocused="{Binding IsFocused, Mode=TwoWay}"/>

Si hay una forma mejor de manejar el problema de visibilidad, házmelo saber.



Encontré la solución editando el código de la siguiente manera. No es necesario establecer la propiedad de Enlazado primero Falso y luego Verdadero.

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) { if (d != null && d is Control) { var _Control = d as Control; if ((bool)e.NewValue) { // To set false value to get focus on control. if we don''t set value to False then we have to set all binding //property to first False then True to set focus on control. OnLostFocus(_Control, null); _Control.Focus(); // Don''t care about false values. } } } private static void OnLostFocus(object sender, RoutedEventArgs e) { if (sender != null && sender is Control) { (sender as Control).SetValue(IsFocusedProperty, false); } } }


System.Windows.Forms.Application.DoEvents(); Keyboard.Focus(tbxLastName);


public class DummyViewModel : ViewModelBase { private bool isfocused= false; public bool IsFocused { get { return isfocused; } set { isfocused= value; OnPropertyChanged("IsFocused"); } } }