Definir foco na checkbox de texto no WPF do modelo de exibição (c #)

Eu tenho um TextBox e um Button na minha opinião.

Agora eu estou verificando uma condição após o clique do botão e se a condição acaba por ser falsa, exibindo a mensagem para o usuário e, em seguida, eu tenho que definir o cursor para o controle 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); } 

O código acima está no ViewModel.

A CompanyAssociation é o nome da visualização.

Mas o cursor não está sendo definido no TextBox .

O xaml é:

  

Deixe-me responder a sua pergunta em três partes.

  1. Eu estou querendo saber o que é “cs.txtCompanyID” no seu exemplo? É um controle TextBox? Se sim, então você está no caminho errado. De um modo geral, não é uma boa ideia ter qualquer referência à UI no seu ViewModel. Você pode perguntar “Por quê?” mas esta é outra questão para postar no Stackoverflow :).

  2. A melhor maneira de rastrear problemas com o Focus é … depurar o código-fonte .Net. Sem brincadeiras. Isso me poupou muito tempo muitas vezes. Para habilitar a debugging de código-fonte .net, consulte o blog de Shawn Bruke .

  3. Finalmente, a abordagem geral que uso para definir o foco do ViewModel é Attached Properties. Eu escrevi propriedade anexada muito simples, que pode ser definida em qualquer UIElement. E pode ser ligado à propriedade “IsFocused” do ViewModel, por exemplo. Aqui 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. } } } 

    Agora na sua View (em XAML) você pode ligar esta propriedade ao seu ViewModel:

      

Espero que isto ajude :). Se não se referir à resposta # 2.

Felicidades.

Sei que esta pergunta já foi respondida mil vezes, mas fiz algumas edições à contribuição de Anvaka que, acredito, ajudará outras pessoas que tiveram problemas parecidos.

Primeiramente, eu mudei a propriedade anexada acima assim:

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

Meu motivo para adicionar as referências de visibilidade foram as guias. Aparentemente, se você usasse a propriedade anexada em qualquer outra guia fora da guia inicialmente visível, a propriedade anexada não funcionaria até que você focasse manualmente o controle.

O outro obstáculo era criar uma maneira mais elegante de redefinir a propriedade subjacente para falso quando perdesse o foco. É aí que entram os events de foco perdidos.

  

Se houver uma maneira melhor de lidar com o problema de visibilidade, avise-nos.

Nota: Obrigado ao Apfelkuacha pela sugestão de colocar o BindsTwoWayByDefault no DependencyProperty. Eu fiz isso há muito tempo no meu próprio código, mas nunca atualizei este post. O Mode = TwoWay não é mais necessário no código WPF devido a essa alteração.

Eu acho que a melhor maneira é manter o princípio MVVM limpo, então basicamente você deve usar a class Messenger fornecida com o MVVM Light e aqui está como usá-lo:

no seu viewmodel (exampleViewModel.cs): escreva o seguinte

  Messenger.Default.Send("focus", "DoFocus"); 

agora em seu View.cs (não o XAML o view.xaml.cs) escreva o seguinte no construtor

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

esse método está bem e com menos código e mantendo os padrões MVVM

Nenhum desses funcionou exatamente para mim, mas para o benefício de outros, foi o que acabei escrevendo com base em alguns dos códigos já fornecidos aqui.

O uso seria o seguinte:

  

E a implementação seria a seguinte:

 ///  /// Behavior allowing to put focus on element from the view model in a MVVM implementation. ///  public static class FocusBehavior { #region Dependency Properties ///  /// IsFocused dependency property. ///  public static readonly DependencyProperty IsFocusedProperty = DependencyProperty.RegisterAttached("IsFocused", typeof(bool?), typeof(FocusBehavior), new FrameworkPropertyMetadata(IsFocusedChanged)); ///  /// Gets the IsFocused property value. ///  /// The element. /// Value of the IsFocused property or null if not set. public static bool? GetIsFocused(DependencyObject element) { if (element == null) { throw new ArgumentNullException("element"); } return (bool?)element.GetValue(IsFocusedProperty); } ///  /// Sets the IsFocused property value. ///  /// The element. /// The value. 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 ///  /// Determines whether the value of the dependency property IsFocused has change. ///  /// The dependency object. /// The  instance containing the event data. 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; } } ///  /// Sets the focus when the framework element is loaded and ready to receive input. ///  /// The sender. /// The  instance containing the event data. 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 } 

Este é um tópico antigo, mas parece não haver uma resposta com código que resolva os problemas com a resposta aceita por Anavanka: ele não funciona se você definir a propriedade no viewmodel como false, ou se você definir sua propriedade para true, o usuário clica manualmente em outra coisa e, em seguida, você o define como verdadeiro novamente. Eu também não consegui que a solução da Zamotic funcionasse de forma confiável nesses casos.

Reunir algumas das discussões acima me dá o código abaixo que aborda esses problemas:

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

Dito isto, isso ainda é complexo para algo que pode ser feito em uma linha em codebehind, e CoerceValue não é realmente destinado a ser usado dessa maneira, então talvez codebehind seja o caminho a percorrer.

No meu caso, o FocusExtension não funcionou até eu alterar o método OnIsFocusedPropertyChanged. O original estava trabalhando apenas na debugging quando um ponto de interrupção interrompia o processo. Em tempo de execução, o processo é muito rápido e nada aconteceu. Com essa pequena modificação e a ajuda do nosso amigo Task, isso está funcionando bem em ambos os cenários.

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

Anvakas código shiny é para aplicativos do Windows Desktop. Se você é como eu e precisava da mesma solução para aplicativos da Windows Store, esse código pode 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); } } } } 

O problema é que, uma vez que o IsUserNameFocused esteja configurado como true, ele nunca será falso. Isso resolve isso manipulando GotFocus e LostFocus para o FrameworkElement.

Eu estava tendo problemas com a formatação do código-fonte, então aqui está um link

Para aqueles que tentam usar a solução de Anvaka acima, eu estava tendo problemas com a binding apenas trabalhando pela primeira vez, já que o foco perdido não atualizaria a propriedade para false. Você pode definir manualmente a propriedade como false e, em seguida, true todas as vezes, mas uma solução melhor poderia ser fazer algo assim em sua propriedade:

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

Dessa forma, você só precisa defini-lo como verdadeiro e ele terá foco.

Eu uso o WPF / Caliburn Micro e descobri que o “dfaivre” fez uma solução geral e viável aqui: http://caliburnmicro.codeplex.com/discussions/222892

Eu encontrei a solução da Crucial para o problema IsVisible muito útil. Não resolveu completamente o meu problema, mas algum código extra seguindo o mesmo padrão para o padrão IsEnabled fez.

Para o método IsFocusedChanged, adicionei:

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

E aqui está o manipulador:

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

Para o Silverlight:

 using System.Windows; using System.Windows.Controls; using System.Windows.Interactivity; namespace MyProject.Behaviors { public class FocusBehavior : Behavior { 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"      

OU

      

Para definir o foco, basta fazê-lo no código:

 EmailFocus = true; 

Lembre-se de que este plug-in faz parte de uma página html, portanto, outros controles na página podem ter o foco

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

Você pode usar o padrão de design ViewCommand . Ele descreve um método para o padrão de design do MVVM controlar uma View de um ViewModel com comandos.

Eu o implementei com base na sugestão do Rei A.Majid de usar a class MVVM Light Messenger. A class ViewCommandManager manipula comandos de chamada em exibições conectadas. É basicamente a outra direção dos Comandos regulares, para esses casos quando um ViewModel precisa fazer alguma ação em sua Visualização. Ele usa reflexões como comandos ligados a dados e WeakReferences para evitar vazamentos de memory.

http://dev.unclassified.de/source/viewcommand (também publicado no CodeProject)

Eu encontrei a solução editando o código conforme segue. Não há necessidade de definir a propriedade Binding primeiro False e depois True.

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

Ninguém parece ter incluído a etapa final para facilitar a atualização de atributos por meio de variables ​​vinculadas. Aqui está o que eu inventei. Deixe-me saber se existe uma maneira melhor de fazer isso.

XAML

    

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

Primeiramente, gostaria de agradecer a Avanka por me ajudar a resolver meu problema de foco. Existe no entanto um erro no código que ele postou, nomeadamente na linha: if (e.OldValue == null)

O problema que tive foi que, se você clicar primeiro em sua visualização e focar o controle, e.oldValue não será mais nulo. Então, quando você definir a variável para focar o controle pela primeira vez, isso fará com que os manipuladores lostfocus e gotfocus não sejam definidos. Minha solução para isso foi a seguinte:

 public static class ExtensionFocus { static ExtensionFocus() { BoundElements = new List(); } public static readonly DependencyProperty IsFocusedProperty = DependencyProperty.RegisterAttached("IsFocused", typeof(bool?), typeof(ExtensionFocus), new FrameworkPropertyMetadata(false, IsFocusedChanged)); private static List 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); } } } 

Apenas faça isso:

    ... 
 public class DummyViewModel : ViewModelBase { private bool isfocused= false; public bool IsFocused { get { return isfocused; } set { isfocused= value; OnPropertyChanged("IsFocused"); } } } 
 System.Windows.Forms.Application.DoEvents(); Keyboard.Focus(tbxLastName);