Ustaw fokus na TextBox w WPF z modelu widoku


130

Myślę, że mam a TextBoxi Button.

Teraz sprawdzam warunek po kliknięciu przycisku i jeśli warunek okaże się fałszywy, wyświetlam wiadomość użytkownikowi, a następnie muszę ustawić kursor na TextBoxkontrolce.

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

Powyższy kod znajduje się w ViewModel.

To CompanyAssociationjest nazwa widoku.

Ale kursor nie jest ustawiany w TextBox.

XAML to:

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

Kiedy używasz caliburn.micro ten jest doskonałym rozwiązaniem.
matze8426

Odpowiedzi:


268

Odpowiem na twoje pytanie w trzech częściach.

  1. Zastanawiam się, co to jest „cs.txtCompanyID” w Twoim przykładzie? Czy jest to formant TextBox? Jeśli tak, to jesteś na złej drodze. Ogólnie rzecz biorąc, nie jest dobrym pomysłem umieszczanie jakichkolwiek odniesień do interfejsu użytkownika w swoim ViewModel. Możesz zapytać „Dlaczego?” ale to jest kolejne pytanie do opublikowania w Stackoverflow :).

  2. Najlepszym sposobem śledzenia problemów z Focus jest ... debugowanie kodu źródłowego .Net. Bez żartów. Zaoszczędziło mi to wiele czasu. Aby włączyć debugowanie kodu źródłowego .net, zapoznaj się z blogiem Shawn Bruke .

  3. Wreszcie, ogólne podejście, którego używam do ustawiania fokusu z ViewModel, to Attached Properties. Napisałem bardzo prostą załączoną właściwość, którą można ustawić na dowolnym elemencie UIElement. I może być na przykład powiązany z właściwością ViewModel „IsFocused”. Oto ona:

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

    Teraz w Twoim widoku (w języku XAML) możesz powiązać tę właściwość z Twoim ViewModel:

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

Mam nadzieję że to pomoże :). Jeśli nie odnosi się do odpowiedzi nr 2.

Twoje zdrowie.


5
Fajny pomysł. Muszę ustawić IsUserNameFocused na true, a następnie ponownie false, aby to działało, czy to prawda?
Sam

19
Powinieneś także zadzwonić Keyboard.Focus(uie);z OnIsFocusedPropertyChangedwydarzenia, jeśli chcesz, aby twoja kontrola otrzymała Focus Focus, a także Logical Focus
Rachel

6
Jak to ma być używane? Jeśli ustawię moją właściwość na true, kontrola jest skupiona. Ale zawsze będzie ponownie skoncentrowany, kiedy wrócę do tego widoku. Zresetowanie go z OnIsFocusedPropertyChanged nie zmienia tego. Zresetowanie go bezpośrednio po ustawieniu go z ViewModel nie skupia się już na niczym. To nie działa. Co dokładnie zrobiło tych 70 popierających?
ygoe

4
Zmieniłem również wywołanie zwrotne na to: ...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 ... Czasami muszę nawet zresetować „IsFocused” na false w ViewModel, jeśli chcę wielokrotnie ustawiać fokus. Ale wtedy to działa, tam gdzie inne metody zawiodły.
Simon D.

3
po ustawieniu fokusu i uzyskaniu fokusu przez inną kontrolkę, ponowne ustawienie fokusu nie zadziała, ponieważ IsFocused jest nadal true. Trzeba wymusić na nim fałsz, a potem prawdę. public bool IsFocused { get { return _isFocused; } set { if (_isFocused == value) { _isFocused = false; OnPropertyChanged(); } _isFocused = value; OnPropertyChanged(); } }
walterhuang

76

Wiem, że na to pytanie odpowiadano już tysiące razy, ale wprowadziłem kilka zmian we wkładzie Anvaki, które moim zdaniem pomogą innym, którzy mieli podobne problemy, co ja.

Po pierwsze, zmieniłem powyższą załączoną nieruchomość w następujący sposób:

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 (e.NewValue != null && (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);
    }
}

Powodem dodania odniesień do widoczności były zakładki. Najwyraźniej, jeśli użyłeś dołączonej właściwości na dowolnej innej karcie poza początkowo widoczną kartą, dołączona właściwość nie działała, dopóki nie ustawisz ręcznie formantu.

Inną przeszkodą było stworzenie bardziej eleganckiego sposobu resetowania podstawowej właściwości do wartości false, gdy traci ona ostrość. Tam właśnie pojawiły się wydarzenia związane z utratą ostrości.

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

Jeśli istnieje lepszy sposób rozwiązania problemu z widocznością, daj mi znać.

Uwaga: Dziękuję Apfelkuacha za sugestię umieszczenia BindsTwoWayByDefault we właściwości DependencyProperty. Zrobiłem to dawno temu we własnym kodzie, ale nigdy nie aktualizowałem tego postu. Tryb = TwoWay nie jest już potrzebny w kodzie WPF z powodu tej zmiany.


9
Działa to dobrze dla mnie, z wyjątkiem tego, że muszę dodać czek "if (e.Source == e.OriginalSource)" w GotFocus / LostFocus, albo przepływa stos (dosłownie), gdy jest używany na moim UserControl, który przekierowuje fokus na wewnętrzną składnik. Usunąłem kontrolki Visible, akceptując fakt, że działa podobnie jak metoda .Focus (). Jeśli .Focus () nie działa, powiązanie nie powinno działać - i to jest w porządku w moim scenariuszu.
HelloSam

1
Używam tego w WF 4.5. Na IsFocusedChanged mam scenariusz (działanie jest ponownie ładowane), w którym e.NewValue jest null i zgłasza wyjątek, więc najpierw sprawdź to. Wszystko działa dobrze z tą drobną zmianą.
Olaru Mircea

1
Dzięki temu wprks Świetnie :) Właśnie dodałem „{BindsTwoWayByDefault = true}” w „FrameworkPropertyMetadata”, aby ustawić tryb domyślny na TwoWayBinding, więc nie jest potrzebny przy każdym
powiązaniu

1
Zdaję sobie sprawę, że jest to stara odpowiedź, ale napotykam sytuację, w której właściwość IsEnabled kontrolki, na którą chcę przenieść fokus, jest powiązana z konwerterem wielu wartości. Najwyraźniej procedura obsługi zdarzenia GotFocus jest wywoływana, zanim konwerter wielu wartości wykona ... co oznacza, że ​​kontrolka w tym momencie jest wyłączona, więc gdy tylko GotFocus zakończy działanie, zostanie wywołany LostFocus (myślę, że kontrolka jest nadal wyłączona) . Jakieś przemyślenia, jak sobie z tym poradzić?
Mark Olbert

1
@MarkOlbert używa fe.Dispatcher.BeginInvoke(new Action(() => { fe.Focus(); }), DispatcherPriority.Loaded);, że jest aktualizowany po załadowaniu. Więcej informacji tutaj: telerik.com/forums/isfocused-property#OXgFYZFOg0WZ2rxidln61Q
Apfelkuacha

32

Myślę, że najlepszym sposobem jest utrzymanie zasady MVVM w czystości, więc w zasadzie musisz użyć klasy Messenger dostarczonej z MVVM Light i oto jak z niej korzystać:

in your viewmodel (exampleViewModel.cs): napisz co następuje

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

teraz w swoim View.cs (a nie w kodzie XAML the view.xaml.cs) napisz w konstruktorze:

 public MyView()
        {
            InitializeComponent();

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

ta metoda działa dobrze, przy mniejszej ilości kodu i przy zachowaniu standardów MVVM


9
Cóż, jeśli chcesz zachować czystość zasady MVVM, w pierwszej kolejności nie będziesz pisać kodu w swoim kodzie. Uważam, że dołączone podejście do nieruchomości jest znacznie czystsze. Nie wprowadza również wielu magicznych ciągów do modelu widoku.
Ε Г И І И О

32
El Nino: Skąd dokładnie przyszedł Ci do głowy pomysł, że w Twoim widoku nie powinno być niczego za kodem? Wszystko, co jest związane z interfejsem użytkownika, powinno znajdować się w kodzie widoku. Ustawienie fokusu elementów interfejsu użytkownika powinno zdecydowanie znajdować się w kodzie widoku. Pozwól modelowi widoku dowiedzieć się, kiedy wysłać wiadomość; niech widok wymyśli, co zrobić z wiadomością. To właśnie robi MV-VM: oddziela obawy dotyczące modelu danych, logiki biznesowej i interfejsu użytkownika.
Kyle Hale,

Na podstawie tej sugestii zaimplementowałem własny ViewCommandManager, który obsługuje wywoływanie poleceń w połączonych widokach. Zasadniczo jest to drugi kierunek zwykłych poleceń w tych przypadkach, gdy ViewModel musi wykonać jakąś akcję w swoich widokach. Używa odbić, takich jak polecenia powiązane z danymi i WeakReferences, aby uniknąć wycieków pamięci. dev.unclassified.de/source/viewcommand (także na CodeProject)
ygoe

Użyłem tej metody do wydrukowania WPF FlowDocuments. Ładnie działało. Dzięki
Gordon Slysz

Chcę jednego w Silverlight? Czy możemy to wykorzystać?
Bigeyes

18

Żadne z nich nie działało dokładnie dla mnie, ale dla dobra innych, to właśnie napisałem na podstawie części kodu już tu podanego.

Sposób użycia byłby następujący:

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

A realizacja wyglądałaby następująco:

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

12

To jest stary wątek, ale wydaje się, że nie ma odpowiedzi z kodem, który rozwiązuje problemy z zaakceptowaną odpowiedzią Anavanka: nie działa, jeśli ustawisz właściwość w modelu widoku na fałsz lub ustawisz właściwość na prawda, użytkownik ręcznie klika coś innego, a następnie ponownie ustawiasz wartość true. Również w tych przypadkach nie mogłem sprawić, by rozwiązanie Zamotic działało niezawodnie.

Łącząc niektóre z powyższych dyskusji, otrzymam poniższy kod, który moim zdaniem rozwiązuje te problemy:

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

To powiedziawszy, jest to nadal skomplikowane dla czegoś, co można zrobić w jednym wierszu za kodem, a CoerceValue nie jest tak naprawdę przeznaczone do użycia w ten sposób, więc może być właściwym rozwiązaniem.


2
Działa to konsekwentnie, podczas gdy zaakceptowana odpowiedź nie. Dzięki!
NathanAldenSr

4

W moim przypadku FocusExtension nie działało, dopóki nie zmienię metody OnIsFocusedPropertyChanged. Oryginalny działał tylko w debugowaniu, gdy punkt przerwania zatrzymał proces. W czasie wykonywania proces przebiega zbyt szybko i nic się nie dzieje. Dzięki tej niewielkiej modyfikacji i pomocy naszego przyjaciela Taska działa to dobrze w obu scenariuszach.

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

Problem polega na tym, że gdy IsUserNameFocused jest ustawiona na true, nigdy nie będzie fałszywa. To rozwiązuje problem, obsługując GotFocus i LostFocus dla FrameworkElement.

Miałem problem z formatowaniem kodu źródłowego, więc oto link


1
Zmieniłem "obiekt fe = (FrameworkElement) d;" to „FrameworkElement fe = (FrameworkElement) d;” więc

Nadal nie rozwiązuje problemu. Element pozostaje skupiony za każdym razem, gdy do niego wracam.
ygoe

3

Genialny kod Anvakas jest przeznaczony dla aplikacji Windows Desktop. Jeśli jesteś podobny do mnie i potrzebujesz tego samego rozwiązania dla aplikacji Windows Store, ten kod może być przydatny:

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

Dla tych, którzy próbowali użyć powyższego rozwiązania Anvaki, miałem problemy z wiązaniem działającym tylko za pierwszym razem, ponieważ lostfocus nie aktualizował właściwości na false. Możesz ręcznie ustawić właściwość na false, a następnie za każdym razem na true, ale lepszym rozwiązaniem może być zrobienie czegoś takiego w swojej właściwości:

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

W ten sposób wystarczy, że ustawisz ją na true, a zostanie ona skupiona.


Dlaczego masz oświadczenie if? _isFocused raz ustawiony na false zostanie po prostu zmieniony na wartość w następnej linii.
Damien McGivern

1
@Tyrsius Możesz obejść ten problem, pobierając właściwość zależności do Coerce, patrz tutaj- social.msdn.microsoft.com/Forums/en-US/wpf/thread/…
RichardOD


1

Znalazłem rozwiązanie, edytując kod w następujący sposób. Nie ma potrzeby ustawiania właściwości Binding najpierw False, a następnie 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

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

LUB

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

Aby ustawić fokus, po prostu zrób to w kodzie:

EmailFocus = true;

Pamiętaj, że ta wtyczka jest częścią strony html, więc inne elementy sterujące na stronie mogą mieć fokus

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

0

Możesz użyć wzorca projektowego ViewCommand . Opisuje metodę dla wzorca projektowego MVVM do kontrolowania widoku z ViewModel za pomocą poleceń.

Zaimplementowałem to w oparciu o sugestię Kinga A.Majida, aby użyć klasy MVVM Light Messenger. Klasa ViewCommandManager obsługuje wywoływanie poleceń w połączonych widokach. Zasadniczo jest to inny kierunek zwykłych poleceń w tych przypadkach, gdy ViewModel musi wykonać jakąś akcję w swoim widoku. Używa odbić, takich jak polecenia powiązane z danymi i WeakReferences, aby uniknąć wycieków pamięci.

http://dev.unclassified.de/source/viewcommand (również opublikowane na CodeProject)


0

Wydaje się, że nikt nie uwzględnił ostatniego kroku, aby ułatwić aktualizację atrybutów za pomocą powiązanych zmiennych. Oto, co wymyśliłem. Daj mi znać, czy istnieje lepszy sposób na zrobienie tego.

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

Po pierwsze, chciałbym podziękować Avance za pomoc w rozwiązaniu mojego problemu z koncentracją. Jest jednak błąd w przesłanym przez niego kodzie, a mianowicie w linii: if (e.OldValue == null)

Problem, jaki miałem, polegał na tym, że jeśli najpierw klikniesz w swoim widoku i ustawiasz ostrość kontrolki, e.oldValue nie jest już zerowa. Następnie, gdy ustawisz zmienną tak, aby skupiła się na kontrolce po raz pierwszy, spowoduje to, że programy obsługi lostfocus i gotfocus nie zostały ustawione. Moje rozwiązanie było następujące:

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

Po prostu zrób to:

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

Lubię to. Działa to dobrze, jeśli chcesz ustawić początkową fokus.
user2430797

0

Po wdrożeniu zaakceptowanej odpowiedzi natknąłem się na problem polegający na tym, że podczas nawigacji po widokach za pomocą Prism TextBox nadal nie był aktywny. Niewielka zmiana w procedurze obsługi PropertyChanged rozwiązała ten problem

    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

Alternatywne podejście oparte na odpowiedzi @Sheridan tutaj

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

W modelu widoku skonfiguruj swoje powiązanie w zwykły sposób, a następnie ustaw SomeTextIsFocused na true, aby ustawić fokus na polu tekstowym


-1

Znalazłem Crucial , że rozwiązanie dotyczące problemu IsVisible jest bardzo przydatne. Nie rozwiązało to całkowicie mojego problemu, ale zrobił to dodatkowy kod zgodny z tym samym wzorcem dla wzorca IsEnabled.

Do metody IsFocusedChanged dodałem:

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

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

-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 używa WPF. Kod skupiający się na WinForms nie pomoże.
Josh G
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.