Definir o foco no TextBox no WPF do modelo de exibição


129

Eu tenho um TextBoxe um Buttonna minha opinião.

Agora, estou verificando uma condição ao clicar no botão e, se ela for falsa, exibindo a mensagem para o usuário e, em seguida, preciso definir o cursor no TextBoxcontrole.

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.

O CompanyAssociationé o nome da visualização.

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

O xaml é:

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

Quando você está usando caliburn.micro, esta é uma excelente solução.
matze8426

Respostas:


264

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

  1. Gostaria de 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 à interface do usuário no seu ViewModel. Você pode perguntar "Por quê?" mas essa é outra pergunta para postar no Stackoverflow :).

  2. A melhor maneira de rastrear problemas com o Focus é ... depurando o código-fonte .Net. Sem brincadeiras. Isso me salvou muito tempo várias vezes. Para habilitar a depuração do código-fonte .net, consulte o blog de Shawn Bruke .

  3. Finalmente, a abordagem geral que eu uso para definir o foco do ViewModel é Attached Properties. Escrevi uma propriedade anexada muito simples, que pode ser definida em qualquer UIElement. E pode ser associado à 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, no seu View (em XAML), você pode vincular essa propriedade ao seu ViewModel:

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

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

Felicidades.


5
Idéia legal. Preciso definir IsUserNameFocused como true e, em seguida, false novamente para que isso funcione, certo?
19410 Sam

19
Você também deve chamar Keyboard.Focus(uie);de seu OnIsFocusedPropertyChangedevento, se você quiser que o seu controle para receber o foco do teclado, bem como foco lógico
Rachel

6
Como isso deve ser usado? Se eu definir minha propriedade como true, o controle será focado. Mas sempre será focado novamente quando eu voltar a essa visão. A redefinição do OnIsFocusedPropertyChanged não altera isso. A redefinição diretamente após a configuração no ViewModel não concentra mais nada. Isso não funciona. O que esses 70 promotores fizeram exatamente?
ygoe 26/02

4
Também mudei o retorno de chamada para o seguinte: ...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 ... Às vezes, tenho que redefinir o 'IsFocused' para false no ViewModel, se quiser definir o foco várias vezes. Mas então funciona, onde outros métodos falharam.
Simon D.

3
depois que você definir o foco e outro controle obtiver o foco, definir o foco novamente não funcionará porque IsFocused ainda é verdadeiro. Precisa forçá-lo a falso e depois verdadeiro. public bool IsFocused { get { return _isFocused; } set { if (_isFocused == value) { _isFocused = false; OnPropertyChanged(); } _isFocused = value; OnPropertyChanged(); } }
walterhuang

75

Sei que essa pergunta já foi respondida mil vezes até agora, mas fiz algumas edições na contribuição de Anvaka que, em minha opinião, ajudará outras pessoas que tiveram problemas semelhantes.

Em primeiro lugar, alterei a propriedade anexada acima da seguinte forma:

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ê usou a propriedade anexada em qualquer outra guia fora da guia inicialmente visível, a propriedade anexada não funcionou até você focar o controle manualmente.

O outro obstáculo era criar uma maneira mais elegante de redefinir a propriedade subjacente para false quando ela perdia o foco. Foi aí que entraram os eventos de foco perdido.

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

Se houver uma maneira melhor de lidar com o problema de visibilidade, entre em contato.

Nota: Agradecemos a Apfelkuacha pela sugestão de colocar o BindsTwoWayByDefault no DependencyProperty. Eu tinha feito 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.


9
Isso funciona bem para mim, exceto que eu preciso adicionar uma verificação "if (e.Source == e.OriginalSource)" no GotFocus / LostFocus ou então ele empilhará fluxos (literalmente) quando usado no meu UserControl, que redireciona o foco para interno componente. Eu removi as verificações Visible, aceitando o fato de que ele funciona exatamente como o método .Focus (). Se .Focus () não funcionar, a ligação não deverá funcionar - e isso é bom para o meu cenário.
HelloSam 22/03/2013

1
Estou usando isso no WF 4.5. Em IsFocusedChanged, tenho um cenário (uma atividade é recarregada) em que e.NewValue é nulo e gera uma exceção, portanto verifique isso primeiro. Tudo funciona bem com essa pequena mudança.
Olaru Mircea

1
Graças a isso wprks Ótimo :) Acabei de adicionar '{BindsTwoWayByDefault = true}' em 'FrameworkPropertyMetadata' para definir o modo padrão como TwoWayBinding, para que não seja necessário em todas as
ligações

1
Sei que essa é uma resposta antiga, mas estou enfrentando uma situação em que a propriedade IsEnabled do controle para a qual quero mudar o foco está ligada a um conversor de vários valores. Aparentemente, o manipulador de eventos GotFocus é chamado antes do conversor de vários valores ... o que significa que o controle, nesse momento, é desativado, assim que o GotFocus é concluído, LostFocus é chamado (acho que porque o controle ainda está desativado) . Alguma idéia de como lidar com isso?
Mark Olbert

1
O @MarkOlbert usa fe.Dispatcher.BeginInvoke(new Action(() => { fe.Focus(); }), DispatcherPriority.Loaded);que é atualizado após o carregamento. Mais informações aqui: telerik.com/forums/isfocused-property#OXgFYZFOg0WZ2rxidln61Q
Apfelkuacha

32

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

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

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

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

 public MyView()
        {
            InitializeComponent();

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

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


9
Bem, se você quiser manter o princípio MVVM limpo, não estaria escrevendo código em seu código em primeiro lugar. Eu acredito que a abordagem de propriedade anexa é muito mais limpa. Ele não apresenta muitas seqüências mágicas no seu modelo de exibição também.
Ε Г И І И О

32
El Nino: De onde exatamente você tirou a idéia de que não deveria haver nada na sua visão por trás do código? Qualquer coisa relacionada à interface do usuário deve estar no code-behind da exibição. Definir o foco dos elementos da interface do usuário Definitivamente deve estar no code-behind da exibição. Deixe o modelo de exibição descobrir quando enviar a mensagem; deixe a visualização descobrir o que fazer com a mensagem. É isso que a MV-VM faz: separa as preocupações do modelo de dados, lógica de negócios e interface do usuário.
Kyle Hale

Com base nessa sugestão, implementei meu próprio ViewCommandManager que lida com a chamada de comandos em modos de exibição conectados. É basicamente a outra direção dos comandos regulares, nesses casos em que um ViewModel precisa executar alguma ação em suas View (s). Ele usa reflexão como comandos ligados a dados e WeakReferences para evitar vazamentos de memória. dev.unclassified.de/source/viewcommand (também em CodeProject)
ygoe 01/02

Eu usei esse método para imprimir WPF FlowDocuments. Trabalhou bem. Obrigado
Gordon Slysz

Eu quero um no Silverlight? Podemos usá-lo?
Bigeyes

18

Nenhuma delas funcionou exatamente para mim, mas para o benefício de outras pessoas, foi isso que acabei escrevendo com base em alguns dos códigos já fornecidos aqui.

O uso seria o seguinte:

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

E a implementação seria a seguinte:

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

11

Esse é um encadeamento antigo, mas não parece haver uma resposta com código que resolva os problemas da resposta aceita pelo Anavanka: não funcionará se você definir a propriedade no viewmodel como false ou se você configurar sua propriedade como true, o usuário clica manualmente em outra coisa e você a define como true novamente. Também não consegui que a solução da Zamotic funcionasse de maneira confiável nesses casos.

Reunir algumas das discussões acima fornece o código abaixo, que aborda esses problemas, eu acho:

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 no codebehind, e o CoerceValue não deve ser usado dessa maneira, então talvez o codebehind seja o caminho a seguir.


1
Isso funciona de forma consistente, enquanto a resposta aceita não. Obrigado!
NathanAldenSr

4

No meu caso, o FocusExtension não funcionou até eu alterar o método OnIsFocusedPropertyChanged. O original estava trabalhando apenas na depuração quando um ponto de interrupção interrompeu o processo. Em tempo de execução, o processo é muito rápido e nada aconteceu. Com esta pequena modificação e a ajuda do nosso amigo Task, isso está funcionando bem nos dois 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);
  }
}

3

O problema é que, quando o IsUserNameFocused estiver definido como true, ele nunca será falso. Isso resolve o problema, 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


1
Alterei "objeto fe = (FrameworkElement) d;" para "FrameworkElement fe = (FrameworkElement) d;" por isso as obras intellisense

Ainda não resolve o problema. O elemento permanece focado toda vez que eu volto a ele.
ygoe 26/02

3

O código brilhante Anvakas é para aplicativos da área de trabalho do Windows. 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);
            }
        }
    }
}

1

Para aqueles que tentaram usar a solução da Anvaka acima, eu estava tendo problemas com a ligação funcionando apenas pela primeira vez, pois o lostfocus não atualizaria a propriedade como false. Você pode definir manualmente a propriedade como false e true sempre, mas uma solução melhor pode 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 configurá-lo como verdadeiro, e ele terá foco.


Por que você tem uma declaração if? o _isFocused, uma vez definido como false, será alterado apenas para o valor na próxima linha.
Damien McGivern

1
@ Tyrsius Você pode contornar esse problema obtendo a propriedade dependency para Coerce, consulte aqui- social.msdn.microsoft.com/Forums/en-US/wpf/thread/…
RichardOD


1

Encontrei a solução editando o código conforme a seguir. Não há necessidade de definir a propriedade Binding primeiro False e 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);
        }
    }
}

0

Para o 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>

OU

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

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

0

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

Eu o implementei com base na sugestão do rei A.Majid de usar a classe MVVM Light Messenger. A classe ViewCommandManager manipula a chamada de comandos nas visualizações conectadas. É basicamente a outra direção dos comandos regulares, para esses casos em que um ViewModel precisa executar alguma ação em seu View. Ele usa reflexão como comandos ligados a dados e WeakReferences para evitar vazamentos de memória.

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


0

Parece que ninguém incluiu a etapa final para facilitar a atualização de atributos por meio de variáveis ​​ligadas. Aqui está o que eu criei. Deixe-me saber se existe uma maneira melhor de fazer isso.

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

    }

0

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

O problema que tive foi que, se você clicar pela primeira vez na visualização e focar o controle, o e.oldValue não será mais nulo. Então, quando você define a variável para focar o controle pela primeira vez, isso resulta nos manipuladores de foco perdido e foco automático não sendo definidos. Minha solução para isso foi a seguinte:

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

0

Apenas faça o seguinte:

<Window x:class...
   ...
   ...
   FocusManager.FocusedElement="{Binding ElementName=myTextBox}"
>
<Grid>
<TextBox Name="myTextBox"/>
...

Eu gosto disso. Isso funciona bem se você deseja definir o foco inicial.
user2430797 20/02

0

Após implementar a resposta aceita, deparei-me com um problema que, ao navegar pelas visualizações com o Prism, o TextBox ainda não ficava em foco. Uma pequena alteração no manipulador PropertyChanged resolveu

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

0

Uma abordagem alternativa baseada na resposta @Sheridan aqui

 <TextBox Text="{Binding SomeText, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">
        <TextBox.Style>
            <Style>
                <Style.Triggers>
                    <DataTrigger Binding="{Binding SomeTextIsFocused, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Value="True">
                        <Setter Property="FocusManager.FocusedElement" Value="{Binding RelativeSource={RelativeSource Self}}" />
                    </DataTrigger>
                </Style.Triggers>
            </Style>
        </TextBox.Style>
    </TextBox>

No seu modelo de visualização, configure sua encadernação da maneira usual e defina SomeTextIsFocused como true para definir o foco na sua caixa de texto


-1

Encontrei Crucial a solução da para o problema do 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.

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

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

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

1
OP está usando WPF. O código de foco do WinForms não vai ajudar.
21711 Josh G
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.