Imposta lo stato attivo su TextBox in WPF dal modello di visualizzazione


129

Ho a TextBoxe a Buttondal mio punto di vista.

Ora sto verificando una condizione al clic del pulsante e se la condizione risulta essere falsa, mostrando il messaggio all'utente, quindi devo impostare il cursore sul TextBoxcontrollo.

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

Il codice sopra è nel ViewModel.

Il CompanyAssociationè il nome della vista.

Ma il cursore non si sta impostando in TextBox.

Lo 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 stai usando caliburn.micro questa è un'ottima soluzione.
matze8426

Risposte:


264

Lasciami rispondere alla tua domanda in tre parti.

  1. Mi chiedo cosa sia "cs.txtCompanyID" nel tuo esempio? È un controllo TextBox? Se sì, allora stai sbagliando. In generale, non è una buona idea avere alcun riferimento all'interfaccia utente nel ViewModel. Puoi chiedere "Perché?" ma questa è un'altra domanda da pubblicare su StackOverflow :).

  2. Il modo migliore per rintracciare i problemi con Focus è ... debug del codice sorgente .Net. Non sto scherzando. Mi ha fatto risparmiare un sacco di tempo molte volte. Per abilitare il debug del codice sorgente .net consultare il blog di Shawn Bruke .

  3. Infine, l'approccio generale che utilizzo per impostare lo stato attivo da ViewModel è Proprietà associate. Ho scritto una proprietà annessa molto semplice, che può essere impostata su qualsiasi UIElement. E può essere associato, ad esempio, alla proprietà "IsFocused" di ViewModel. Ecco qui:

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

    Ora nella tua vista (in XAML) puoi associare questa proprietà al tuo ViewModel:

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

Spero che questo ti aiuti :). Se non si riferisce alla risposta n. 2.

Saluti.


5
Bella idea. Devo impostare IsUserNameFocused su true, quindi di nuovo false per farlo funzionare, giusto?
Sam

19
Dovresti anche chiamare Keyboard.Focus(uie);dal tuo OnIsFocusedPropertyChangedevento se vuoi che il tuo controllo riceva il Focus sulla tastiera e il Focus logico
Rachel,

6
Come dovrebbe essere usato? Se imposto la mia proprietà su true, il controllo è focalizzato. Ma tornerò sempre focalizzato quando torno a questa visione. Il ripristino da OnIsFocusedPropertyChanged non cambia questo. Il ripristino diretto dopo averlo impostato da ViewModel non focalizza più nulla. Non funziona Che cosa hanno fatto esattamente quei 70 utenti?
giovedì

4
Ho anche modificato il callback in questo modo: a ...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 ... volte devo anche ripristinare "IsFocused" su false in ViewModel, se voglio impostare lo stato attivo più volte. Ma poi funziona, dove alcuni altri metodi hanno fallito.
Simon D.

3
dopo aver impostato lo stato attivo e un altro controllo ottiene lo stato attivo, impostare nuovamente lo stato attivo non funzionerà perché IsFocused è ancora vero. È necessario forzarlo su falso e quindi vero. public bool IsFocused { get { return _isFocused; } set { if (_isFocused == value) { _isFocused = false; OnPropertyChanged(); } _isFocused = value; OnPropertyChanged(); } }
Walterhuang,

75

So che questa domanda ha già ricevuto una risposta mille volte, ma ho apportato alcune modifiche al contributo di Anvaka che penso possa aiutare gli altri che hanno avuto problemi simili che avevo.

In primo luogo, ho modificato la proprietà allegata sopra in questo modo:

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

La mia ragione per aggiungere i riferimenti di visibilità erano le schede. Apparentemente se hai usato la proprietà collegata su qualsiasi altra scheda al di fuori della scheda inizialmente visibile, la proprietà collegata non ha funzionato fino a quando non hai focalizzato manualmente il controllo.

L'altro ostacolo era creare un modo più elegante di ripristinare la proprietà sottostante su false quando ha perso la concentrazione. È qui che sono arrivati ​​gli eventi di messa a fuoco persi.

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

Se esiste un modo migliore per gestire il problema di visibilità, per favore fatemelo sapere.

Nota: grazie ad Apfelkuacha per il suggerimento di inserire il BindsTwoWayByDefault nella proprietà Dependency. L'avevo fatto tanto tempo fa nel mio codice, ma non ho mai aggiornato questo post. Mode = TwoWay non è più necessario nel codice WPF a causa di questa modifica.


9
Questo funziona bene per me, tranne per il fatto che ho bisogno di aggiungere un controllo "if (e.Source == e.OriginalSource)" in GotFocus / LostFocus oppure stackoverflow (letteralmente) quando usato sul mio UserControl, che reindirizza lo stato attivo su inner componente. Ho rimosso i controlli Visible, accettando il fatto che funzioni esattamente come il metodo .Focus (). Se .Focus () non funziona, l'associazione non dovrebbe funzionare, e va bene per il mio scenario.
HelloSam,

1
Sto usando questo in WF 4.5. Su IsFocusedChanged ho uno scenario (un'attività viene ricaricata) in cui e.NewValue è nullo e genera un'eccezione, quindi verificalo prima. Tutto funziona bene con questo piccolo cambiamento.
Olaru Mircea,

1
Grazie wprks Ottimo :) Ho appena aggiunto '{BindsTwoWayByDefault = true}' in 'FrameworkPropertyMetadata' per impostare la modalità predefinita su TwoWayBinding, quindi non è necessario su ogni Binding
R00st3r

1
Mi rendo conto che questa è una vecchia risposta, ma mi imbatto in una situazione in cui la proprietà IsEnabled del controllo su cui voglio spostare lo stato attivo è legata a un convertitore multi-valore. Apparentemente, il gestore di eventi GotFocus viene chiamato prima del convertitore multi-valore ... il che significa che il controllo, a quel punto, viene disabilitato, quindi non appena GotFocus viene completato, viene chiamato LostFocus (suppongo perché il controllo è ancora disabilitato) . Qualche idea su come gestirlo?
Mark Olbert,

1
@MarkOlbert usa fe.Dispatcher.BeginInvoke(new Action(() => { fe.Focus(); }), DispatcherPriority.Loaded);che viene aggiornato dopo che è stato caricato. Maggiori informazioni qui: telerik.com/forums/isfocused-property#OXgFYZFOg0WZ2rxidln61Q
Apfelkuacha,

32

Penso che il modo migliore sia mantenere pulito il principio MVVM, quindi in pratica devi usare la classe Messenger fornita con MVVM Light ed ecco come usarlo:

nel tuo viewmodel (esempioViewModel.cs): scrivi quanto segue

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

ora in View.cs (non in XAML view.xaml.cs) scrivi quanto segue nel costruttore

 public MyView()
        {
            InitializeComponent();

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

quel metodo funziona bene e con meno codice e mantenendo gli standard MVVM


9
Bene, se vuoi mantenere pulito il principio MVVM, non dovresti scrivere il codice nel tuo codice in primo luogo. Credo che l'approccio alla proprietà collegata sia molto più pulito. Non introduce molte stringhe magiche anche nel tuo modello di visualizzazione.
Ε Г И І И О

32
El Nino: Da dove hai preso esattamente l'idea che non ci dovrebbe essere nulla nella tua vista in codice? Tutto ciò che è correlato all'interfaccia utente dovrebbe trovarsi nel code-behind della vista. La messa a fuoco degli elementi dell'interfaccia utente dovrebbe essere sicuramente nel code-behind della vista. Consenti al viewmodel di capire quando inviare il messaggio; lascia che la vista capisca cosa fare del messaggio. Questo è ciò che MV-VM fa: separa le preoccupazioni del modello di dati, della logica aziendale e dell'interfaccia utente.
Kyle Hale,

Sulla base di questo suggerimento, ho implementato il mio ViewCommandManager che gestisce i comandi di invocazione nelle viste connesse. Fondamentalmente è l'altra direzione dei normali comandi, per questi casi in cui un ViewModel deve eseguire alcune azioni nelle sue viste. Utilizza la riflessione come comandi associati a dati e WeakReferences per evitare perdite di memoria. dev.unclassified.de/source/viewcommand (anche su CodeProject)
ygoe

Ho usato questo metodo per stampare WPF FlowDocuments. Funzionava bene. Grazie
Gordon Slysz

Ne voglio uno a Silverlight? Possiamo usarlo?
Bigeyes,

18

Nessuno di questi ha funzionato esattamente per me, ma a beneficio di altri, questo è quello che ho finito per scrivere sulla base di alcuni dei codici già forniti qui.

L'utilizzo sarebbe il seguente:

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

E l'implementazione sarebbe la seguente:

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

Questo è un vecchio thread, ma non sembra esserci una risposta con il codice che risolva i problemi con la risposta accettata da Anavanka: non funziona se si imposta la proprietà nel modello view su false o se si imposta la proprietà su vero, l'utente fa clic manualmente su qualcos'altro, quindi lo imposti nuovamente su vero. In questi casi non sono riuscito a far sì che la soluzione di Zamotic funzionasse in modo affidabile.

Mettere insieme alcune delle discussioni sopra mi dà il codice qui sotto che affronta questi problemi penso:

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

Detto questo, questo è ancora complesso per qualcosa che può essere fatto in una riga in codebehind, e CoerceValue non è davvero pensato per essere usato in questo modo, quindi forse codebehind è la strada da percorrere.


1
Funziona in modo coerente, mentre la risposta accettata no. Grazie!
NathanAldenSr

4

Nel mio caso, FocusExtension non ha funzionato fino a quando non ho cambiato il metodo OnIsFocusedPropertyChanged. Quello originale funzionava solo nel debug quando un punto di interruzione interrompeva il processo. In fase di esecuzione, il processo è troppo rapido e non succede nulla. Con questa piccola modifica e l'aiuto del nostro amico Task, questo funziona bene in entrambi gli scenari.

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

Il problema è che una volta che IsUserNameFocused è impostato su true, non sarà mai falso. Questo risolve gestendo GotFocus e LostFocus per FrameworkElement.

Ho avuto problemi con la formattazione del codice sorgente, quindi ecco un link


1
Ho cambiato "oggetto fe = (FrameworkElement) d;" a "FrameworkElement fe = (FrameworkElement) d;" quindi l'intellisense funziona

Non risolve ancora il problema. L'elemento rimane concentrato ogni volta che ci torno.
giovedì

3

Il brillante codice di Anvakas è per le applicazioni desktop di Windows. Se sei come me e hai bisogno della stessa soluzione per le app di Windows Store, questo codice potrebbe essere 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

Per coloro che cercavano di utilizzare la soluzione di Anvaka sopra, avevo problemi con l'associazione funzionante solo la prima volta, poiché lostfocus non avrebbe aggiornato la proprietà su false. Puoi impostare manualmente la proprietà su false e poi true ogni volta, ma una soluzione migliore potrebbe essere quella di fare qualcosa del genere nella tua proprietà:

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

In questo modo devi solo impostarlo su true e otterrà la messa a fuoco.


Perché hai un'istruzione if? _isFocused, una volta impostato su false, verrà semplicemente modificato in valore nella riga successiva.
Damien McGivern il

1
@Tyrsius Puoi aggirare questo problema portando la proprietà di dipendenza a Coerce, vedi qui- social.msdn.microsoft.com/Forums/en-US/wpf/thread/…
RichardOD,


1

Ho trovato la soluzione modificando il codice come segue. Non è necessario impostare la proprietà Binding prima su False poi su 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

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

Per impostare lo stato attivo, basta farlo nel codice:

EmailFocus = true;

Ricorda che questo plugin fa parte di una pagina html, quindi altri controlli nella pagina potrebbero essere focalizzati

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

0

È possibile utilizzare il modello di progettazione ViewCommand . Descrive un metodo per il modello di progettazione MVVM per controllare una vista da un ViewModel con comandi.

L'ho implementato in base al suggerimento di King A.Majid di utilizzare la classe MVVM Light Messenger. La classe ViewCommandManager gestisce i comandi di richiamo nelle viste connesse. È sostanzialmente l'altra direzione dei normali comandi, per questi casi in cui un ViewModel deve eseguire alcune azioni nella sua vista. Utilizza la riflessione come comandi associati a dati e WeakReferences per evitare perdite di memoria.

http://dev.unclassified.de/source/viewcommand (pubblicato anche su CodeProject)


0

Nessuno sembra aver incluso il passaggio finale per facilitare l'aggiornamento degli attributi tramite variabili associate. Ecco cosa mi è venuto in mente. Fammi sapere se esiste un modo migliore per farlo.

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

Prima di tutto vorrei ringraziare Avanka per avermi aiutato a risolvere il mio problema di concentrazione. C'è tuttavia un bug nel codice che ha pubblicato, vale a dire nella riga: if (e.OldValue == null)

Il problema che ho avuto è stato che se si fa clic per la prima volta nella vista e si focalizza il controllo, e.oldValue non è più nullo. Quindi, quando si imposta la variabile per focalizzare il controllo per la prima volta, ciò comporta che i gestori lostfocus e gotfocus non vengano impostati. La mia soluzione a questo è stata la seguente:

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

Basta fare questo:

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

Mi piace questo. Funziona bene se si desidera impostare il focus iniziale.
user2430797

0

Dopo aver implementato la risposta accettata, mi sono imbattuto in un problema che durante la navigazione delle visualizzazioni con Prism TextBox non riusciva ancora a mettere a fuoco. Una piccola modifica al gestore PropertyChanged l'ha risolta

    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

Un approccio alternativo basato sulla risposta @Sheridan qui

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

Nel tuo modello di vista imposta la rilegatura nel solito modo, quindi imposta SomeTextIsFocused su true per impostare lo stato attivo sulla casella di testo


-1

Ho trovato Crucial soluzione 's al problema IsVisible molto utile. Non ha risolto completamente il mio problema, ma lo ha fatto qualche codice aggiuntivo che segue lo stesso modello per il modello IsEnabled.

Al metodo IsFocusedChanged ho aggiunto:

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

Ed ecco il gestore:

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 utilizza WPF. Il focus code per WinForms non è di aiuto.
Josh G,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.