Setzen Sie den Fokus auf TextBox in WPF aus dem Ansichtsmodell

129

Ich habe ein TextBoxund ein Buttonaus meiner Sicht.

Jetzt überprüfe ich eine Bedingung beim Klicken auf die Schaltfläche. Wenn sich herausstellt, dass die Bedingung falsch ist, zeige ich dem Benutzer die Meldung an und setze den Cursor auf das TextBoxSteuerelement.

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

Der obige Code befindet sich im ViewModel.

Das CompanyAssociationist der Ansichtsname.

Der Cursor wird jedoch nicht in der gesetzt TextBox.

Das xaml ist:

<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}"/>
priyanka.sarkar
quelle
Wenn Sie caliburn.micro verwenden, ist dies eine hervorragende Lösung.
Matze8426

Antworten:

264

Lassen Sie mich Ihre Frage in drei Teilen beantworten.

  1. Ich frage mich, was in Ihrem Beispiel "cs.txtCompanyID" ist. Ist es ein TextBox-Steuerelement? Wenn ja, dann sind Sie auf dem falschen Weg. Im Allgemeinen ist es keine gute Idee, in Ihrem ViewModel einen Verweis auf die Benutzeroberfläche zu haben. Sie können fragen "Warum?" Aber dies ist eine andere Frage, die Sie auf Stackoverflow stellen sollten :).

  2. Der beste Weg, um Probleme mit Focus aufzuspüren, ist ... das Debuggen von .Net-Quellcode. Im Ernst. Es hat mir viele Male viel Zeit gespart. Informationen zum Aktivieren des Debuggens von .net-Quellcode finden Sie in Shawn Brukes Blog.

  3. Der allgemeine Ansatz, mit dem ich den Fokus in ViewModel festlege, sind die angehängten Eigenschaften. Ich habe eine sehr einfache angehängte Eigenschaft geschrieben, die für jedes UIElement festgelegt werden kann. Und es kann zum Beispiel an die ViewModel-Eigenschaft "IsFocused" gebunden werden. Hier ist es:

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

    Jetzt können Sie in Ihrer Ansicht (in XAML) diese Eigenschaft an Ihr ViewModel binden:

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

Hoffe das hilft :). Wenn es sich nicht auf die Antwort Nr. 2 bezieht.

Prost.

Anvaka
quelle
5
Tolle Idee. Ich muss IsUserNameFocused auf true und dann wieder auf false setzen, damit dies funktioniert. Ist das richtig?
Sam
19
Sie sollten auch Keyboard.Focus(uie);von Ihrer OnIsFocusedPropertyChangedVeranstaltung aus anrufen , wenn Sie möchten, dass Ihre Steuerung sowohl den Tastaturfokus als auch den logischen Fokus erhält
Rachel
6
Wie soll das verwendet werden? Wenn ich meine Eigenschaft auf true setze, ist das Steuerelement fokussiert. Aber es wird immer wieder fokussiert sein, wenn ich auf diese Ansicht zurückkomme. Das Zurücksetzen von OnIsFocusedPropertyChanged ändert daran nichts. Das Zurücksetzen direkt nach dem Einstellen im ViewModel fokussiert nichts mehr. Es funktioniert nicht. Was genau haben diese 70 Upvoter getan?
Ygoe
2
Ich hatte Probleme damit, den Fokus eines Textfelds in einem Dialogfeld festzulegen, das ich in einer WPF-Anwendung anzeige, in der das Dialogfeld als UserControl angezeigt wird. Nachdem ich viele verschiedene Ansätze ausprobiert hatte, gelang es mir endlich, die Fokussierung zum Laufen zu bringen. Ihr Code oben hat funktioniert, nachdem ich ihn so angepasst habe, dass er Dispatcher.CurrentDispatcher.BeginInvoke verwendet, um den Focus-Aufruf auszuführen: Dispatcher.CurrentDispatcher.BeginInvoke ((Action) (() => {uie.Focus (); // Falsch ist mir egal Werte}));
Tore Aurstad
4
Ich habe auch den Rückruf folgendermaßen geändert: ...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 ... Manchmal muss ich sogar 'IsFocused' im ViewModel auf false zurücksetzen, wenn ich den Fokus mehrmals setzen möchte. Aber dann funktioniert es, wo einige andere Methoden fehlgeschlagen sind.
Simon D.
75

Ich weiß, dass diese Frage inzwischen tausendmal beantwortet wurde, aber ich habe einige Änderungen an Anvakas Beitrag vorgenommen, von denen ich denke, dass sie anderen helfen werden, die ähnliche Probleme hatten wie ich.

Erstens habe ich die oben angehängte Eigenschaft wie folgt geändert:

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

Mein Grund für das Hinzufügen der Sichtbarkeitsreferenzen waren Registerkarten. Wenn Sie die angehängte Eigenschaft auf einer anderen Registerkarte außerhalb der ursprünglich sichtbaren Registerkarte verwendet haben, hat die angehängte Eigenschaft anscheinend erst funktioniert, nachdem Sie das Steuerelement manuell fokussiert haben.

Das andere Hindernis bestand darin, eine elegantere Methode zum Zurücksetzen der zugrunde liegenden Eigenschaft auf false zu schaffen, wenn sie den Fokus verlor. Hier kamen die Ereignisse mit verlorenem Fokus ins Spiel.

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

Wenn es eine bessere Möglichkeit gibt, das Sichtbarkeitsproblem zu lösen, lassen Sie es mich bitte wissen.

Hinweis: Vielen Dank an Apfelkuacha für den Vorschlag, BindsTwoWayByDefault in die DependencyProperty aufzunehmen. Ich hatte das vor langer Zeit in meinem eigenen Code getan, aber diesen Beitrag nie aktualisiert. Der Modus = Zwei Wege ist aufgrund dieser Änderung im WPF-Code nicht mehr erforderlich.

Zamotisch
quelle
9
Dies funktioniert gut für mich, außer dass ich einen "if (e.Source == e.OriginalSource)" - Check im GotFocus / LostFocus hinzufügen muss, oder es läuft (buchstäblich) über, wenn es auf meinem UserControl verwendet wird, wodurch der Fokus auf inner umgeleitet wird Komponente. Ich habe die sichtbaren Überprüfungen entfernt und akzeptiert, dass sie genau wie die .Focus () -Methode funktionieren. Wenn .Focus () nicht funktioniert, sollte die Bindung nicht funktionieren - und das ist für mein Szenario in Ordnung.
HelloSam
1
Ich benutze dies in WF 4.5. Auf IsFocusedChanged habe ich ein Szenario (eine Aktivität wird neu geladen), in dem e.NewValue null ist und eine Ausnahme auslöst. Überprüfen Sie dies also zuerst. Mit dieser kleinen Änderung funktioniert alles einwandfrei.
Olaru Mircea
1
Vielen Dank an diese wprks Großartig :) Ich habe gerade '{BindsTwoWayByDefault = true}' bei 'FrameworkPropertyMetadata' hinzugefügt, um den Standardmodus auf TwoWayBinding zu setzen, sodass er nicht bei jeder Bindung benötigt wird
R00st3r
1
Mir ist klar, dass dies eine alte Antwort ist, aber ich stoße auf eine Situation, in der die IsEnabled-Eigenschaft des Steuerelements, auf das ich den Fokus verschieben möchte, an einen Mehrwertkonverter gebunden ist. Anscheinend wird der GotFocus-Ereignishandler aufgerufen, bevor der Mehrwertkonverter dies tut ... was bedeutet, dass das Steuerelement zu diesem Zeitpunkt deaktiviert ist. Sobald GotFocus abgeschlossen ist, wird LostFocus aufgerufen (ich denke, weil das Steuerelement immer noch deaktiviert ist). . Irgendwelche Gedanken darüber, wie man damit umgeht?
Mark Olbert
1
@MarkOlbert verwendet, fe.Dispatcher.BeginInvoke(new Action(() => { fe.Focus(); }), DispatcherPriority.Loaded);dass es aktualisiert wird, nachdem es geladen wurde. Weitere Informationen hier: telerik.com/forums/isfocused-property#OXgFYZFOg0WZ2rxidln61Q
Apfelkuacha
32

Ich denke, der beste Weg ist, das MVVM-Prinzip sauber zu halten. Sie müssen also im Grunde die Messenger-Klasse verwenden, die mit dem MVVM Light geliefert wird, und hier ist, wie Sie es verwenden:

Schreiben Sie in Ihrem Ansichtsmodell (exampleViewModel.cs) Folgendes

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

Schreiben Sie jetzt in Ihre View.cs (nicht in die XAML die view.xaml.cs) Folgendes in den Konstruktor

 public MyView()
        {
            InitializeComponent();

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

Diese Methode funktioniert einwandfrei und mit weniger Code und unter Beibehaltung der MVVM-Standards

Adam
quelle
9
Wenn Sie das MVVM-Prinzip sauber halten möchten, würden Sie in erster Linie keinen Code in Ihren Code schreiben. Ich glaube, dass der Ansatz der angehängten Immobilien viel sauberer ist. Es werden auch nicht viele magische Zeichenfolgen in Ihr Ansichtsmodell eingefügt.
Г И І І О
32
El Nino: Wo genau sind Sie auf die Idee gekommen, dass in Ihrer Ansicht kein Code-Behind enthalten sein sollte? Alles, was mit der Benutzeroberfläche zu tun hat, sollte sich im Code-Behind der Ansicht befinden. Das Festlegen des Fokus von UI-Elementen sollte auf jeden Fall im Code- Behind der Ansicht liegen. Lassen Sie das Ansichtsmodell herausfinden, wann die Nachricht gesendet werden soll. Lassen Sie die Ansicht herausfinden, was mit der Nachricht zu tun ist. Dies ist, was MV-VM tut: Trennt die Belange von Datenmodell, Geschäftslogik und Benutzeroberfläche.
Kyle Hale
Basierend auf diesem Vorschlag habe ich meinen eigenen ViewCommandManager implementiert, der das Aufrufen von Befehlen in verbundenen Ansichten behandelt. Dies ist im Grunde die andere Richtung der regulären Befehle, in diesen Fällen, wenn ein ViewModel in seinen Ansichten eine Aktion ausführen muss. Es verwendet Reflexionen wie datengebundene Befehle und WeakReferences, um Speicherverluste zu vermeiden. dev.unclassified.de/source/viewcommand (auch auf CodeProject)
ygoe
Ich habe diese Methode verwendet, um WPF FlowDocuments auszudrucken. Hat gut funktioniert. Vielen Dank
Gordon Slysz
Ich möchte eine in Silverlight? Können wir es benutzen?
Großaugen
18

Keines davon hat genau für mich funktioniert, aber zum Nutzen anderer habe ich es letztendlich auf der Grundlage einiger der hier bereits bereitgestellten Codes geschrieben.

Die Verwendung wäre wie folgt:

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

Und die Implementierung wäre wie folgt:

/// <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
}
Leo Vildosola
quelle
11

Dies ist ein alter Thread, aber es scheint keine Antwort mit Code zu geben, die die Probleme mit Anavankas akzeptierter Antwort behebt: Es funktioniert nicht, wenn Sie die Eigenschaft im Ansichtsmodell auf false setzen oder wenn Sie Ihre Eigenschaft auf setzen true, der Benutzer klickt manuell auf etwas anderes und setzt es dann erneut auf true. Auch in diesen Fällen konnte ich Zamotics Lösung nicht zuverlässig zum Laufen bringen.

Wenn ich einige der obigen Diskussionen zusammenfasse, erhalte ich den folgenden Code, der diese Probleme behandelt, denke ich:

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

Trotzdem ist dies für etwas, das in Codebehind in einer Zeile ausgeführt werden kann, immer noch komplex, und CoerceValue ist nicht wirklich dazu gedacht, auf diese Weise verwendet zu werden. Vielleicht ist Codebehind der richtige Weg.

Rich N.
quelle
1
Dies funktioniert konsistent, die akzeptierte Antwort jedoch nicht. Vielen Dank!
NathanAldenSr
4

In meinem Fall funktionierte die FocusExtension erst, als ich die Methode OnIsFocusedPropertyChanged änderte. Das Original arbeitete nur beim Debuggen, als ein Haltepunkt den Prozess stoppte. Zur Laufzeit ist der Prozess zu schnell und nichts ist passiert. Mit dieser kleinen Modifikation und der Hilfe unseres Freundes Task funktioniert dies in beiden Szenarien einwandfrei.

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);
  }
}
Vincent Rithner
quelle
3

Das Problem ist, dass wenn IsUserNameFocused einmal auf true gesetzt ist, es niemals false sein wird. Dies löst es, indem GotFocus und LostFocus für das FrameworkElement behandelt werden.

Ich hatte Probleme mit der Formatierung des Quellcodes. Hier ist ein Link

Shawn
quelle
1
Ich habe "object fe = (FrameworkElement) d;" zu "FrameworkElement fe = (FrameworkElement) d;" so funktioniert die Intellisense
Löst das Problem immer noch nicht. Das Element bleibt jedes Mal fokussiert, wenn ich darauf zurückkomme.
Ygoe
3

Anvakas brillanter Code ist für Windows Desktop-Anwendungen. Wenn Sie wie ich sind und dieselbe Lösung für Windows Store-Apps benötigen, ist dieser Code möglicherweise hilfreich:

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);
            }
        }
    }
}
PEK
quelle
1

Für diejenigen, die versuchten, die oben genannte Lösung von Anvaka zu verwenden, gab es Probleme mit der Bindung, die nur beim ersten Mal funktionierte, da lostfocus die Eigenschaft nicht auf false aktualisieren würde. Sie können die Eigenschaft jedes Mal manuell auf false und dann auf true setzen. Eine bessere Lösung könnte jedoch darin bestehen, in Ihrer Eigenschaft Folgendes zu tun:

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

Auf diese Weise müssen Sie es immer nur auf true setzen, und es wird fokussiert.

Kyeotic
quelle
Warum hast du eine if-Anweisung? Das auf false gesetzte _isFocused wird nur in der nächsten Zeile auf den Wert geändert.
Damien McGivern
1
@ Tyrsius Sie können dieses Problem abrunden, indem Sie die Abhängigkeitseigenschaft auf Coerce übertragen, siehe hier: social.msdn.microsoft.com/Forums/en-US/wpf/thread/…
RichardOD
1

Ich habe eine Lösung gefunden, indem ich den Code wie folgt bearbeitet habe. Es ist nicht erforderlich, die Bindungseigenschaft zuerst auf False und dann auf True festzulegen.

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);
        }
    }
}
Bharat Mendapara
quelle
0

Für 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>

ODER

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

Um den Fokus zu setzen, sollte dies nur im Code erfolgen:

EmailFocus = true;

Denken Sie daran, dass dieses Plugin Teil einer HTML-Seite ist, sodass andere Steuerelemente auf der Seite möglicherweise den Fokus haben

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

Sie können das ViewCommand- Entwurfsmuster verwenden. Es beschreibt eine Methode für das MVVM-Entwurfsmuster zum Steuern einer Ansicht aus einem ViewModel mit Befehlen.

Ich habe es basierend auf dem Vorschlag von King A.Majid implementiert, die MVVM Light Messenger-Klasse zu verwenden. Die ViewCommandManager-Klasse behandelt das Aufrufen von Befehlen in verbundenen Ansichten. Dies ist im Grunde die andere Richtung der regulären Befehle, in diesen Fällen, in denen ein ViewModel in seiner Ansicht eine Aktion ausführen muss. Es verwendet Reflexionen wie datengebundene Befehle und WeakReferences, um Speicherverluste zu vermeiden.

http://dev.unclassified.de/source/viewcommand (auch auf CodeProject veröffentlicht)

ygoe
quelle
0

Niemand scheint den letzten Schritt aufgenommen zu haben, um das Aktualisieren von Attributen über gebundene Variablen zu vereinfachen. Folgendes habe ich mir ausgedacht. Lassen Sie mich wissen, ob es einen besseren Weg gibt, dies zu tun.

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

    }
Hugh
quelle
0

Zunächst möchte ich Avanka dafür danken, dass sie mir bei der Lösung meines Fokusproblems geholfen hat. Es gibt jedoch einen Fehler in dem Code, den er gepostet hat, nämlich in der Zeile: if (e.OldValue == null)

Das Problem, das ich hatte, war, dass e.oldValue nicht mehr null ist, wenn Sie zum ersten Mal in Ihre Ansicht klicken und das Steuerelement fokussieren. Wenn Sie dann die Variable so einstellen, dass das Steuerelement zum ersten Mal fokussiert wird, werden die Handler für den verlorenen Fokus und den gotfocus nicht festgelegt. Meine Lösung dafür war wie folgt:

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);
            }
        }
    }
user2127475
quelle
0

Mach das einfach:

<Window x:class...
   ...
   ...
   FocusManager.FocusedElement="{Binding ElementName=myTextBox}"
>
<Grid>
<TextBox Name="myTextBox"/>
...
Zoltan
quelle
Ich mag das. Dies funktioniert gut, wenn Sie den anfänglichen Fokus festlegen möchten.
user2430797
0

Nachdem ich die akzeptierte Antwort implementiert hatte, stieß ich auf ein Problem, dass die TextBox beim Navigieren in Ansichten mit Prism immer noch nicht fokussiert wurde. Eine geringfügige Änderung am PropertyChanged-Handler hat das Problem behoben

    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();
            }));
        }
    }
Joe H.
quelle
0

Ein alternativer Ansatz basierend auf der Antwort von @Sheridan hier

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

Richten Sie in Ihrem Ansichtsmodell Ihre Bindung wie gewohnt ein und setzen Sie dann SomeTextIsFocused auf true, um den Fokus auf Ihr Textfeld festzulegen

stuicidle
quelle
-1

Ich habe Crucial gefunden ‚s Lösung für das Problem IsVisible sehr nützlich. Es hat mein Problem nicht vollständig gelöst, aber ein zusätzlicher Code, der dem gleichen Muster für das IsEnabled-Muster folgt, hat es getan.

Zur IsFocusedChanged-Methode habe ich hinzugefügt:

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

Und hier ist der Handler:

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();
    }
}
Wayne Maurer
quelle
-1
public class DummyViewModel : ViewModelBase
    {
        private bool isfocused= false;
        public bool IsFocused
        {
            get
            {
                return isfocused;
            }
            set
            {
                isfocused= value;
                OnPropertyChanged("IsFocused");
            }
        }
    }
Jayasri
quelle
-7
System.Windows.Forms.Application.DoEvents();
Keyboard.Focus(tbxLastName);
KitWest
quelle
1
OP verwendet WPF. Der Fokuscode für WinForms wird nicht helfen.
Josh G