Установить фокус на TextBox в WPF из модели представления


129

У меня есть TextBoxи есть Buttonна мой взгляд.

Теперь я проверяю условие при нажатии кнопки, и если условие оказывается ложным, отображаю сообщение пользователю, а затем мне нужно установить курсор на TextBoxэлемент управления.

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

Приведенный выше код находится в ViewModel.

Это CompanyAssociationимя представления.

Но курсор не устанавливается в TextBox.

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

Когда вы используете caliburn.micro, это отличное решение.
matze8426

Ответы:


265

Позвольте мне ответить на ваш вопрос в трех частях.

  1. Мне интересно, что такое "cs.txtCompanyID" в вашем примере? Это элемент управления TextBox? Если да, то вы ошиблись. Вообще говоря, не рекомендуется иметь какую-либо ссылку на пользовательский интерфейс в вашей ViewModel. Вы можете спросить "Почему?" но это еще один вопрос для публикации в Stackoverflow :).

  2. Лучший способ отследить проблемы с Focus - это ... отладка исходного кода .Net. Без шуток. Это много раз экономило мне много времени. Чтобы включить отладку исходного кода .net, обратитесь к блогу Шона Брука .

  3. Наконец, общий подход, который я использую для установки фокуса из ViewModel, - это прикрепленные свойства. Я написал очень простое вложенное свойство, которое можно установить на любом UIElement. И его можно связать, например, со свойством ViewModel IsFocused. Вот:

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

    Теперь в вашем представлении (в XAML) вы можете привязать это свойство к вашей ViewModel:

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

Надеюсь это поможет :). Если это не относится к ответу №2.

Приветствия.


5
Классная идея. Мне нужно установить IsUserNameFocused в true, а затем снова в false, чтобы это работало, это правильно?
Сэм

19
Вам также следует позвонить Keyboard.Focus(uie);с вашего OnIsFocusedPropertyChangedмероприятия, если вы хотите, чтобы ваше управление получило Keyboard Focus, а также Logical Focus
Рэйчел

6
Как это использовать? Если я установил для своего свойства значение true, элемент управления будет сфокусирован. Но когда я вернусь к этой точке зрения, она всегда будет снова сфокусирована. Сброс из OnIsFocusedPropertyChanged этого не меняет. Сброс сразу после установки из ViewModel больше ни на что не влияет. Не работает. Что конкретно сделали эти 70 сторонников?
ygoe

4
Я также изменил обратный вызов на это: ...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 ... Иногда мне даже нужно сбросить IsFocused на false в ViewModel, если я хочу установить фокус несколько раз. Но тогда он работает там, где другие методы не помогли.
Саймон Д.

3
после того, как вы установите фокус, и другой элемент управления получит фокус, снова установить фокус не получится, потому что IsFocused по-прежнему истинно. Необходимо установить значение false, а затем true. public bool IsFocused { get { return _isFocused; } set { if (_isFocused == value) { _isFocused = false; OnPropertyChanged(); } _isFocused = value; OnPropertyChanged(); } }
walterhuang

75

Я знаю, что на этот вопрос уже тысячи раз ответили, но я внес некоторые правки в статью Анваки, которая, я думаю, поможет другим, у которых были похожие проблемы, которые были у меня.

Во-первых, я изменил указанное выше вложенное свойство следующим образом:

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

Причиной добавления ссылок на видимость были вкладки. По-видимому, если вы использовали прикрепленное свойство на любой другой вкладке за пределами изначально видимой вкладки, присоединенное свойство не работало, пока вы вручную не сфокусировали элемент управления.

Другим препятствием было создание более элегантного способа сброса базового свойства на false при потере фокуса. Вот тут и появились события потери фокуса.

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

Если есть лучший способ решить проблему с видимостью, дайте мне знать.

Примечание. Спасибо Apfelkuacha за предложение поместить BindsTwoWayByDefault в DependencyProperty. Я сделал это давно в своем собственном коде, но никогда не обновлял этот пост. Из-за этого изменения Mode = TwoWay больше не требуется в коде WPF.


9
Это хорошо работает для меня, за исключением того, что мне нужно добавить проверку «if (e.Source == e.OriginalSource)» в GotFocus / LostFocus, иначе он переполняется (буквально) при использовании в моем UserControl, который перенаправляет фокус на внутренний составная часть. Я удалил проверки Visible, приняв тот факт, что он работает так же, как метод .Focus (). Если .Focus () не работает, привязка не должна работать - и это нормально для моего сценария.
HelloSam

1
Я использую это в WF 4.5. В IsFocusedChanged у меня есть сценарий (действие перезагружается), где e.NewValue имеет значение null и выдает исключение, поэтому сначала проверьте это. С этим незначительным изменением все работает нормально.
Олару Мирча,

1
Спасибо за этот wprks Отлично :) Я только что добавил '{BindsTwoWayByDefault = true}' в 'FrameworkPropertyMetadata', чтобы установить режим по умолчанию на TwoWayBinding, поэтому он не нужен при каждой
привязке

1
Я понимаю, что это старый ответ, но я столкнулся с ситуацией, когда свойство IsEnabled элемента управления, на которое я хочу сместить фокус, привязано к многозначному преобразователю. По-видимому, обработчик события GotFocus вызывается до того, как это сделает преобразователь с несколькими значениями ... что означает, что элемент управления в этот момент отключен, поэтому, как только GotFocus завершается, вызывается LostFocus (я думаю, потому что элемент управления все еще отключен) , Есть мысли о том, как с этим справиться?
Марк Ольберт

1
@MarkOlbert использует fe.Dispatcher.BeginInvoke(new Action(() => { fe.Focus(); }), DispatcherPriority.Loaded);то, что он обновляется после загрузки. Подробнее здесь: telerik.com/forums/isfocused-property#OXgFYZFOg0WZ2rxidln61Q
Apfelkuacha

32

Я думаю, что лучший способ - сохранить чистоту принципа MVVM, поэтому в основном вы должны использовать класс Messenger, предоставляемый с MVVM Light, и вот как его использовать:

в вашей модели просмотра (exampleViewModel.cs): напишите следующее

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

теперь в вашем View.cs (а не в XAML view.xaml.cs) напишите следующее в конструкторе

 public MyView()
        {
            InitializeComponent();

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

этот метод работает нормально, с меньшим количеством кода и поддержкой стандартов MVVM


9
Что ж, если вы хотите сохранить принцип MVVM в чистоте, вы не будете писать код в своем коде в первую очередь. Я считаю, что подход с прикрепленным свойством намного чище. Он также не привносит много волшебных строк в вашу модель представления.
Ε Г И І И О

32
Эль-Ниньо: Откуда вы взяли, что в вашем коде представления не должно быть ничего? Все, что связано с пользовательским интерфейсом, должно быть в коде программной части представления. Настройка фокуса элементов пользовательского интерфейса должен определенно быть фоновым кодом представления. Позвольте модели просмотра определить, когда отправлять сообщение; пусть представление выясняет, что делать с сообщением. Это то, что делает MV-VM: разделяет проблемы модели данных, бизнес-логики и пользовательского интерфейса.
Кайл Хейл,

Основываясь на этом предложении, я реализовал свой собственный ViewCommandManager, который обрабатывает вызов команд в подключенных представлениях. По сути, это другое направление обычных команд, для тех случаев, когда ViewModel должен выполнить какое-то действие в своем представлении (ах). Он использует отражение, такое как команды с привязкой к данным и WeakReferences, чтобы избежать утечек памяти. dev.unclassified.de/source/viewcommand (также на CodeProject)
ygoe 01

Я использовал этот метод для распечатки документов WPF FlowDocuments. Работал красиво. Спасибо
Gordon Slysz

Я хочу один в Silverlight? Можем ли мы его использовать?
Bigeyes

18

Ни один из них не сработал для меня точно, но для других я написал это на основе некоторого кода, уже предоставленного здесь.

Использование будет следующим:

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

А реализация была бы такой:

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

Это старый поток, но, похоже, нет ответа с кодом, который решает проблемы с принятым ответом Anavanka: он не работает, если вы установите для свойства в модели просмотра значение false или если вы установите для своего свойства значение true, пользователь вручную щелкает что-то еще, а затем вы снова устанавливаете для него значение true. Я также не мог заставить решение Zamotic работать надежно в этих случаях.

Собрав вместе некоторые из приведенных выше обсуждений, я получаю приведенный ниже код, который, как мне кажется, решает эти проблемы:

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

Тем не менее, это все еще сложно для чего-то, что можно сделать одной строкой в ​​выделенном коде, и CoerceValue на самом деле не предназначен для использования таким образом, поэтому, возможно, выделенный код - это выход.


2
Это работает последовательно, тогда как принятый ответ - нет. Спасибо!
NathanAldenSr

У меня работает хорошо! Спасибо Большое! :)
Давид Веквейт

4

В моем случае FocusExtension не работал, пока я не изменил метод OnIsFocusedPropertyChanged. Исходный работал только при отладке, когда точка останова останавливала процесс. Во время выполнения процесс идет слишком быстро и ничего не происходит. Благодаря этой небольшой модификации и помощи нашего друга Task, это отлично работает в обоих сценариях.

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

Проблема в том, что если для IsUserNameFocused установлено значение true, оно никогда не будет ложным. Это решает проблему, обрабатывая GotFocus и LostFocus для FrameworkElement.

У меня возникли проблемы с форматированием исходного кода, вот ссылка


1
Я изменил "объект fe = (FrameworkElement) d;" в "FrameworkElement fe = (FrameworkElement) d;" так что intellisense работает

По-прежнему не решает проблему. Элемент остается сфокусированным каждый раз, когда я к нему возвращаюсь.
ygoe

3

Великолепный код Anvakas предназначен для настольных приложений Windows. Если вы похожи на меня и вам нужно такое же решение для приложений Магазина Windows, этот код может вам пригодиться:

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

Для тех, кто пытался использовать решение Anvaka выше, у меня были проблемы с привязкой, работающей только в первый раз, поскольку lostfocus не обновлял свойство до false. Вы можете вручную установить для свойства значение false, а затем true каждый раз, но лучшим решением могло бы быть выполнение чего-то вроде этого в своем свойстве:

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

Таким образом, вам нужно только установить для него значение true, и оно получит фокус.


Почему у вас есть оператор if? если для _isFocused установлено значение false, оно будет просто изменено на значение в следующей строке.
Дэмиен МакГиверн

1
@Tyrsius Вы можете решить эту проблему, передав свойство dependency в Coerce, см. Здесь
social.msdn.microsoft.com/Forums/en-US/wpf/thread/…


1

Я нашел решение, отредактировав код, как показано ниже. Нет необходимости устанавливать свойство Binding сначала False, затем 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

Для 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>

ИЛИ

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

Чтобы установить фокус, нужно просто сделать это в коде:

EmailFocus = true;

Помните, что этот плагин является частью html-страницы, поэтому другие элементы управления на странице могут иметь фокус.

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

0

Вы можете использовать ViewCommand проектирования . Он описывает метод шаблона проектирования MVVM для управления представлением из ViewModel с помощью команд.

Я реализовал его на основе предложения короля А. Маджида использовать класс MVVM Light Messenger. Класс ViewCommandManager обрабатывает вызов команд в подключенных представлениях. По сути, это другое направление обычных команд, для тех случаев, когда ViewModel необходимо выполнить какое-то действие в своем View. Он использует отражение, такое как команды с привязкой к данным и WeakReferences, чтобы избежать утечек памяти.

http://dev.unclassified.de/source/viewcommand (также опубликовано на CodeProject)


0

Кажется, никто не включил последний шаг, чтобы упростить обновление атрибутов с помощью связанных переменных. Вот что я придумал. Дайте мне знать, если есть лучший способ сделать это.

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

Прежде всего, я хотел бы поблагодарить Аванку за помощь в решении моей проблемы с фокусировкой. Однако в опубликованном им коде есть ошибка, а именно в строке: if (e.OldValue == null)

Проблема, с которой я столкнулся, заключалась в том, что если вы сначала щелкнете в своем представлении и сфокусируете элемент управления, e.oldValue больше не будет нулевым. Затем, когда вы устанавливаете переменную для фокусировки элемента управления в первый раз, это приводит к тому, что обработчики lostfocus и gotfocus не устанавливаются. Мое решение было следующим:

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

Просто сделай это:

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

Мне это нравится. Это хорошо работает, если вы хотите установить начальный фокус.
user2430797

0

После реализации принятого ответа я столкнулся с проблемой, что при навигации по представлениям с помощью Prism TextBox по-прежнему не фокусировался. Небольшое изменение в обработчике PropertyChanged решило эту проблему.

    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

Альтернативный подход, основанный на ответе @Sheridan здесь

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

В вашей модели представления настройте привязку обычным способом, а затем установите SomeTextIsFocused в значение true, чтобы установить фокус на текстовом поле.


-1

Я нашел Crucial решение проблемы IsVisible от очень полезным. Это не решило полностью мою проблему, но кое-какой дополнительный код, следующий по тому же шаблону, что и шаблон IsEnabled, помог.

К методу IsFocusedChanged я добавил:

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

А вот и обработчик:

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 использует WPF. Код фокуса для WinForms не поможет.
Josh G
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.