Définir le focus sur TextBox dans WPF à partir du modèle de vue


129

J'ai un TextBoxet un Buttonà mon avis.

Maintenant, je vérifie une condition lors d'un clic sur le bouton et si la condition s'avère être fausse, j'affiche le message à l'utilisateur, puis je dois placer le curseur sur le TextBoxcontrôle.

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

Le code ci-dessus est dans le ViewModel.

Le CompanyAssociationest le nom de la vue.

Mais le curseur n'est pas défini dans le fichier TextBox.

Le xaml est:

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

Lorsque vous utilisez caliburn.micro c'est une excellente solution.
matze8426 du

Réponses:


264

Permettez-moi de répondre à votre question en trois parties.

  1. Je me demande ce qu'est "cs.txtCompanyID" dans votre exemple? S'agit-il d'un contrôle TextBox? Si oui, alors vous êtes sur la mauvaise voie. De manière générale, ce n'est pas une bonne idée d'avoir une référence à l'interface utilisateur dans votre ViewModel. Vous pouvez demander "Pourquoi?" mais c'est une autre question à publier sur Stackoverflow :).

  2. La meilleure façon de dépister les problèmes avec Focus est ... de déboguer le code source .Net. Sans blague. Cela m'a fait gagner beaucoup de temps à plusieurs reprises. Pour activer le débogage du code source .net, consultez le blog de Shawn Bruke .

  3. Enfin, l'approche générale que j'utilise pour définir le focus à partir de ViewModel est les propriétés attachées. J'ai écrit une propriété jointe très simple, qui peut être définie sur n'importe quel UIElement. Et il peut être lié à la propriété "IsFocused" de ViewModel par exemple. C'est ici:

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

    Maintenant, dans votre View (en XAML), vous pouvez lier cette propriété à votre ViewModel:

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

J'espère que cela t'aides :). Si cela ne fait pas référence à la réponse n ° 2.

À votre santé.


5
Bonne idée. Je dois définir IsUserNameFocused sur true, puis à nouveau sur false pour que cela fonctionne, est-ce correct?
Sam

19
Vous devez également appeler Keyboard.Focus(uie);depuis votre OnIsFocusedPropertyChangedévénement si vous souhaitez que votre contrôle reçoive le focus clavier ainsi que le focus logique
Rachel

6
Comment cela est-il censé être utilisé? Si je définis ma propriété sur true, le contrôle est focalisé. Mais il sera toujours à nouveau concentré lorsque je reviendrai sur ce point de vue. Le réinitialiser à partir d'OnIsFocusedPropertyChanged ne change pas cela. Le réinitialiser directement après l'avoir défini à partir du ViewModel ne fait plus la mise au point. Ça ne marche pas. Qu'ont fait ces 70 votants positifs exactement?
ygoe le

4
J'ai également changé le rappel en ceci: ...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 ... Parfois, je dois même réinitialiser le 'IsFocused' sur false dans le ViewModel, si je veux définir le focus plusieurs fois. Mais alors cela fonctionne, là où certaines autres méthodes ont échoué.
Simon D.

3
une fois que vous avez défini le focus et qu'un autre contrôle obtient le focus, le redéfinir le focus ne fonctionnera pas car IsFocused est toujours vrai. Besoin de le forcer à faux puis à vrai. public bool IsFocused { get { return _isFocused; } set { if (_isFocused == value) { _isFocused = false; OnPropertyChanged(); } _isFocused = value; OnPropertyChanged(); } }
walterhuang

75

Je sais que cette question a reçu une réponse mille fois maintenant, mais j'ai apporté quelques modifications à la contribution d'Anvaka qui, je pense, aideront d'autres personnes qui ont eu des problèmes similaires.

Tout d'abord, j'ai changé la propriété attachée ci-dessus comme suit:

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

Ma raison d'ajouter les références de visibilité était les onglets. Apparemment, si vous utilisiez la propriété jointe sur un autre onglet en dehors de l'onglet initialement visible, la propriété jointe ne fonctionnait pas tant que vous ne mettiez pas en évidence manuellement le contrôle.

L'autre obstacle était de créer une manière plus élégante de réinitialiser la propriété sous-jacente à false lorsqu'elle perdait le focus. C'est là que les événements de perte de concentration sont intervenus.

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

S'il existe une meilleure façon de gérer le problème de visibilité, veuillez me le faire savoir.

Remarque: Merci à Apfelkuacha pour la suggestion de placer le BindsTwoWayByDefault dans DependencyProperty. Je l'avais fait il y a longtemps dans mon propre code, mais je n'ai jamais mis à jour ce message. Le Mode = TwoWay n'est plus nécessaire dans le code WPF en raison de ce changement.


9
Cela fonctionne bien pour moi, sauf que je dois ajouter une vérification "if (e.Source == e.OriginalSource)" dans le GotFocus / LostFocus ou bien il se superpose (littéralement) lorsqu'il est utilisé sur mon UserControl, ce qui redirige le focus vers l'intérieur composant. J'ai supprimé les vérifications visibles, en acceptant le fait que cela fonctionne comme la méthode .Focus (). Si .Focus () ne fonctionne pas, la liaison ne devrait pas fonctionner - et c'est correct pour mon scénario.
HelloSam

1
J'utilise ceci dans WF 4.5. Sur IsFocusedChanged, j'ai un scénario (une activité est rechargée) où e.NewValue est nul et lève une exception, alors vérifiez d'abord cela. Tout fonctionne bien avec ce changement mineur.
Olaru Mircea

1
Merci ce wprks Génial :) Je viens d'ajouter '{BindsTwoWayByDefault = true}' à 'FrameworkPropertyMetadata' pour définir le mode par défaut sur TwoWayBinding donc il n'est pas nécessaire sur chaque liaison
R00st3r

1
Je me rends compte que c'est une vieille réponse, mais je rencontre une situation dans laquelle la propriété IsEnabled du contrôle sur lequel je souhaite déplacer le focus est liée à un convertisseur à valeurs multiples. Apparemment, le gestionnaire d'événements GotFocus est appelé avant le convertisseur à valeurs multiples ... ce qui signifie que le contrôle, à ce stade, est désactivé, donc dès que GotFocus se termine, LostFocus est appelé (je suppose que le contrôle est toujours désactivé) . Des réflexions sur la façon de gérer cela?
Mark Olbert

1
@MarkOlbert utilise fe.Dispatcher.BeginInvoke(new Action(() => { fe.Focus(); }), DispatcherPriority.Loaded);qu'il est mis à jour après son chargement. Plus d'informations ici: telerik.com/forums/isfocused-property#OXgFYZFOg0WZ2rxidln61Q
Apfelkuacha

32

Je pense que le meilleur moyen est de garder le principe MVVM propre, donc en gros, vous devez utiliser la classe Messenger fournie avec le MVVM Light et voici comment l'utiliser:

dans votre viewmodel (exampleViewModel.cs): écrivez ce qui suit

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

maintenant dans votre View.cs (pas le XAML le view.xaml.cs) écrivez ce qui suit dans le constructeur

 public MyView()
        {
            InitializeComponent();

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

cette méthode fonctionne très bien et avec moins de code et en maintenant les normes MVVM


9
Eh bien, si vous voulez garder le principe MVVM propre, vous n'écririez pas de code dans votre code en premier lieu. Je pense que l'approche de la propriété attachée est beaucoup plus propre. Cela n'introduit pas non plus beaucoup de chaînes magiques dans votre modèle de vue.
Ε Г И І И О

32
El Nino: D'où vient exactement l'idée qu'il ne devrait rien y avoir dans votre vue code-behind? Tout ce qui est lié à l'interface utilisateur doit être dans le code-behind de la vue. Réglage de mise au point d'éléments UI devrait certainement être dans le code-behind de la vue. Laissez le viewmodel déterminer quand envoyer le message; laissez la vue déterminer quoi faire avec le message. C'est ce que fait MV-VM: sépare les préoccupations du modèle de données, de la logique métier et de l'interface utilisateur.
Kyle Hale

Sur la base de cette suggestion, j'ai implémenté mon propre ViewCommandManager qui gère l'appel de commandes dans les vues connectées. C'est fondamentalement l'autre sens des commandes régulières, pour ces cas où un ViewModel a besoin d'effectuer une action dans sa ou ses vues. Il utilise la réflexion comme les commandes liées aux données et les WeakReferences pour éviter les fuites de mémoire. dev.unclassified.de/source/viewcommand (également sur CodeProject)
ygoe

J'ai utilisé cette méthode pour imprimer des FlowDocuments WPF. A bien fonctionné. Merci
Gordon Slysz

J'en veux un dans Silverlight? Pouvons-nous l'utiliser?
Bigeyes

18

Aucun de ceux-ci n'a fonctionné pour moi exactement, mais pour le bénéfice des autres, c'est ce que j'ai fini par écrire en me basant sur une partie du code déjà fourni ici.

L'utilisation serait la suivante:

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

Et la mise en œuvre serait la suivante:

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

Ceci est un ancien fil de discussion, mais il ne semble pas y avoir de réponse avec un code qui résout les problèmes avec la réponse acceptée d'Anavanka: cela ne fonctionne pas si vous définissez la propriété dans le modèle de vue sur false, ou si vous définissez votre propriété sur true, l'utilisateur clique manuellement sur autre chose, puis vous le redéfinissez sur true. Je n'ai pas non plus réussi à faire fonctionner la solution de Zamotic de manière fiable dans ces cas.

Rassembler certaines des discussions ci-dessus me donne le code ci-dessous qui résout ces problèmes, je pense:

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

Cela dit, cela reste complexe pour quelque chose qui peut être fait en une seule ligne dans codebehind, et CoerceValue n'est pas vraiment destiné à être utilisé de cette manière, alors peut-être que codebehind est la voie à suivre.


1
Cela fonctionne de manière cohérente, contrairement à la réponse acceptée. Merci!
NathanAldenSr

4

Dans mon cas, FocusExtension n'a pas fonctionné jusqu'à ce que je change la méthode OnIsFocusedPropertyChanged. L'original ne fonctionnait qu'en débogage lorsqu'un point d'arrêt a arrêté le processus. Au moment de l'exécution, le processus est trop rapide et rien ne s'est passé. Avec cette petite modification et l'aide de notre ami Task, cela fonctionne bien dans les deux scénarios.

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

Le problème est qu'une fois que IsUserNameFocused est défini sur true, il ne sera jamais faux. Cela résout le problème en gérant GotFocus et LostFocus pour FrameworkElement.

J'avais des problèmes avec le formatage du code source alors voici un lien


1
J'ai changé "object fe = (FrameworkElement) d;" à "FrameworkElement fe = (FrameworkElement) d;" pour que l'intellisense fonctionne

Ne résout toujours pas le problème. L'élément reste concentré à chaque fois que j'y reviens.
ygoe

3

Le code brillant d'Anvakas est destiné aux applications Windows Desktop. Si vous êtes comme moi et que vous avez besoin de la même solution pour les applications du Windows Store, ce code peut être utile:

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

Pour ceux qui essayaient d'utiliser la solution d'Anvaka ci-dessus, j'avais des problèmes avec la liaison ne fonctionnant que la première fois, car lostfocus ne mettrait pas à jour la propriété sur false. Vous pouvez définir manuellement la propriété sur false, puis sur true à chaque fois, mais une meilleure solution pourrait être de faire quelque chose comme ceci dans votre propriété:

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

De cette façon, vous n'aurez besoin que de le définir sur true, et il obtiendra le focus.


Pourquoi avez-vous une déclaration if? le _isFocused une fois mis à false sera simplement changé en valeur sur la ligne suivante.
Damien McGivern

1
@Tyrsius Vous pouvez contourner ce problème en obtenant la propriété de dépendance à Coerce, voir ici- social.msdn.microsoft.com/Forums/en-US/wpf/thread/…
RichardOD


1

J'ai trouvé une solution en éditant le code comme suit. Il n'est pas nécessaire de définir d'abord la propriété Binding sur False puis sur 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

Pour 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>

Pour définir le focus, faites-le simplement dans le code:

EmailFocus = true;

N'oubliez pas que ce plugin fait partie d'une page html, donc d'autres contrôles de la page peuvent avoir le focus

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

0

Vous pouvez utiliser le modèle de conception ViewCommand . Il décrit une méthode permettant au modèle de conception MVVM de contrôler une vue à partir d'un ViewModel avec des commandes.

Je l'ai implémenté sur la base de la suggestion de King A.Majid d'utiliser la classe MVVM Light Messenger. La classe ViewCommandManager gère l'appel de commandes dans les vues connectées. C'est fondamentalement l'autre direction des commandes régulières, pour ces cas où un ViewModel doit effectuer une action dans sa vue. Il utilise la réflexion comme les commandes liées aux données et les WeakReferences pour éviter les fuites de mémoire.

http://dev.unclassified.de/source/viewcommand (également publié sur CodeProject)


0

Personne ne semble avoir inclus la dernière étape pour faciliter la mise à jour des attributs via des variables liées. Voici ce que j'ai trouvé. Faites-moi savoir s'il existe une meilleure façon de procéder.

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

VoirModèle

    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

Tout d'abord, je voudrais remercier Avanka de m'avoir aidé à résoudre mon problème de concentration. Il y a cependant un bug dans le code qu'il a posté, à savoir dans la ligne: if (e.OldValue == null)

Le problème que j'ai eu était que si vous cliquez d'abord dans votre vue et que vous concentrez le contrôle, e.oldValue n'est plus nulle. Ensuite, lorsque vous définissez la variable pour focaliser le contrôle pour la première fois, les gestionnaires lostfocus et gotfocus ne sont pas définis. Ma solution à cela était la suivante:

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

Faites juste ceci:

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

J'aime ça. Cela fonctionne bien si vous souhaitez définir le focus initial.
user2430797

0

Après avoir implémenté la réponse acceptée, j'ai rencontré un problème selon lequel lors de la navigation dans les vues avec Prism, la zone de texte ne recevait toujours pas le focus. Une modification mineure du gestionnaire PropertyChanged l'a résolu

    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

Une approche alternative basée sur la réponse @Sheridan ici

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

Dans votre modèle de vue, configurez votre liaison de la manière habituelle, puis définissez SomeTextIsFocused sur true pour définir le focus sur votre zone de texte


-1

J'ai trouvé Crucial la solution « au problème IsVisible très utile. Cela n'a pas complètement résolu mon problème, mais un code supplémentaire suivant le même modèle pour le modèle IsEnabled l'a fait.

À la méthode IsFocusedChanged, j'ai ajouté:

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

Et voici le gestionnaire:

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 utilise WPF. Le code de focus pour WinForms ne va pas aider.
Josh G
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.