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

На мой взгляд, у меня есть 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}"/>

person priyanka.sarkar    schedule 31.08.2009    source источник
comment
Когда вы используете caliburn.micro, это отличное решение.   -  person matze8426    schedule 31.07.2018


Ответы (20)


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

  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.

Ваше здоровье.

person Anvaka    schedule 31.08.2009
comment
Классная идея. Мне нужно установить IsUserNameFocused в значение true, а затем снова в false, чтобы это работало, это правильно? - person Sam; 19.04.2010
comment
Спасибо, Сэм. Это зависит. Иногда достаточно установить значение true только один раз. - person Anvaka; 21.04.2010
comment
@Anvaka, как вы делаете привязку в своей модели представления? это свойство, или ICommand, или что-то еще, я, кажется, не могу понять этот бит. - person Rob; 12.08.2011
comment
@Rob - это просто свойство с именем IsUserNameFocused. - person Anvaka; 12.08.2011
comment
Вы также должны вызвать Keyboard.Focus(uie); из своего OnIsFocusedPropertyChanged события, если хотите, чтобы ваш элемент управления получал фокус клавиатуры, а также логический фокус. - person Rachel; 10.10.2011
comment
Как это использовать? Если я установил для своего свойства значение true, элемент управления будет сфокусирован. Но когда я вернусь к этой точке зрения, он всегда будет снова сфокусирован. Сброс из OnIsFocusedPropertyChanged этого не меняет. Сброс сразу после установки из ViewModel больше ни на что не влияет. Не работает. Что именно сделали эти 70 сторонников? - person ygoe; 27.02.2013
comment
@Anvaka я пробовал что-то на основе этого решения, но я застрял, пожалуйста, взгляните на это Нажмите ЗДЕСЬ - person WiiMaxx; 06.06.2013
comment
Я боролся с установкой фокуса текстового поля в диалоговом окне, которое я отображаю в приложении WPF, отображающем диалог как UserControl. Мне, наконец, удалось заставить фокусировку работать, испробовав много разных подходов. Ваш код, приведенный выше, работал после того, как я настроил его для использования Dispatcher.CurrentDispatcher.BeginInvoke для выполнения вызова Focus: Dispatcher.CurrentDispatcher.BeginInvoke ((Action) (() = ›{uie.Focus (); // Плевать на false ценности })); - person Tore Aurstad; 19.06.2013
comment
вы можете изменить obj.SetValue(IsFocusedProperty, value); на obj.SetValue(IsFocusedProperty, false);, и вам не придется снова устанавливать false и true. - person Owen Johnson; 22.08.2013
comment
Я также изменил обратный вызов на это: ...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, если я хочу установить фокус несколько раз. Но тогда он работает там, где другие методы не помогли. - person Simon D.; 30.09.2013
comment
У меня это прекрасно сработало. Я использую Visual Studio 2013 / MVVM Light. Чтобы эффективно удалить видимый фокус со всех текстовых полей, вы можете установить фокус на что-то вроде TextBlock, который обычно не редактируется. - person Contango; 09.10.2014
comment
Все еще удивительное решение 8 лет спустя. Спасибо! - person M3SSYM4RV1N; 25.09.2017
comment
после того, как вы установите фокус, и другой элемент управления получит фокус, снова установить фокус не получится, потому что IsFocused по-прежнему истинно. Необходимо установить значение false, а затем true. public bool IsFocused { get { return _isFocused; } set { if (_isFocused == value) { _isFocused = false; OnPropertyChanged(); } _isFocused = value; OnPropertyChanged(); } } - person walterhuang; 29.06.2018

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

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

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)fe.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.

person Zamotic    schedule 01.11.2011
comment
Это хорошо работает для меня, за исключением того, что мне нужно добавить проверку if (e.Source == e.OriginalSource) в GotFocus / LostFocus, иначе он переполняется (буквально) при использовании в моем UserControl, который перенаправляет фокус на внутренний компонент. Я удалил проверки Visible, приняв тот факт, что он работает так же, как метод .Focus (). Если .Focus () не работает, привязка не должна работать - и это нормально для моего сценария. - person HelloSam; 23.03.2013
comment
ВЫ АБСОЛЮТНЫЙ ГЕНИЙ. Потратил на это целый день. Спасибо!! Я пытался найти способ сфокусировать элемент и скрыть его, когда у него нет фокуса. Но это действительно портит свойства зависимостей. Это решение сработало для меня. - person Asheh; 27.11.2014
comment
Я использую это в WF 4.5. В IsFocusedChanged у меня есть сценарий (действие перезагружается), где e.NewValue имеет значение null и выдает исключение, поэтому сначала проверьте это. С этим незначительным изменением все работает нормально. - person Olaru Mircea; 24.04.2015
comment
Спасибо, wprks Отлично :) Я только что добавил '{BindsTwoWayByDefault = true}' в 'FrameworkPropertyMetadata', чтобы установить режим по умолчанию на TwoWayBinding, поэтому он не нужен для каждой привязки - person R00st3r; 11.10.2016
comment
Я понимаю, что это старый ответ, но я сталкиваюсь с ситуацией, когда свойство IsEnabled элемента управления, на которое я хочу сместить фокус, привязано к многозначному преобразователю. По-видимому, обработчик события GotFocus вызывается до того, как это сделает многозначный преобразователь ... что означает, что в этот момент элемент управления отключен, поэтому, как только GotFocus завершается, вызывается LostFocus (я думаю, потому что элемент управления все еще отключен) . Есть мысли о том, как с этим справиться? - person Mark Olbert; 20.03.2018
comment
@MarkOlbert использует fe.Dispatcher.BeginInvoke(new Action(() => { fe.Focus(); }), DispatcherPriority.Loaded);, чтобы он обновлялся после загрузки. Дополнительная информация здесь: telerik.com/forums/isfocused-property#OXgFYZFOg0NZ2rQx - person Apfelkuacha; 23.10.2018
comment
Очень хорошее решение, очень помогло. Единственная проблема, с которой я столкнулся, заключалась в том, что когда я возвращался из одного окна в другое, он генерировал нулевое исключение, поэтому я добавил проверку на нулевое значение в if (e.NewValue != null && (bool)e.NewValue) и теперь хорошо работает - person Daniel Žeimo; 10.12.2018
comment
У меня это не работает. Когда я пытаюсь это сделать, элемент управления получает фокус, но не показывает курсор. Или, может быть, он теряет фокус и не показывает его по этой причине, но в любом случае фокус не попадает в нужное место. - person Joshua Frank; 26.08.2019
comment
это решение проще использовать в viewModel. Просто установите для привязанного свойства значение true один раз. после этого повторное использование изменения свойства Notify для свойства снова вернет его в фокус. Вместо этого для принятого ответа необходимо установить true - ›notify -› set false - ›notify each time, что довольно раздражает. чем больше мне приходится работать с wpf mvvm, тем больше я люблю проводить время с выигрышными формами и кодом ... однострочные решения, где так здорово .. - person Blechdose; 27.10.2019
comment
хотя это лучше работает в viewModel, он дает сбой при использовании comboBox с isEditable = true. принятый ответ в этом случае не сработает - person Blechdose; 28.10.2019

Я думаю, что лучший способ - сохранить принцип 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

person Adam    schedule 30.11.2011
comment
Что ж, если вы хотите сохранить чистоту принципа MVVM, вам не придется писать код в своем коде в первую очередь. Я считаю, что подход с прикрепленным свойством намного чище. Он также не привносит много волшебных строк в вашу модель представления. - person Ε Г И І И О; 16.03.2012
comment
Эль-Ниньо: Откуда вы взяли, что в вашем коде представления не должно быть ничего? Все, что связано с пользовательским интерфейсом, должно быть в коде программной части представления. Установка фокуса элементов пользовательского интерфейса должна определенно находиться в коде программной части представления. Позвольте модели просмотра определить, когда отправлять сообщение; пусть представление выяснит, что делать с сообщением. Это то, что делает M-V-VM: разделяет проблемы модели данных, бизнес-логики и пользовательского интерфейса. - person Kyle Hale; 19.02.2013
comment
На основе этого предложения я реализовал свой собственный ViewCommandManager, который обрабатывает вызов команд в подключенных представлениях. Это в основном другое направление обычных команд, для тех случаев, когда ViewModel необходимо выполнить какое-то действие в своем представлении (ах). Он использует отражение, такое как команды с привязкой к данным и WeakReferences, чтобы избежать утечек памяти. dev.unclassified.de/source/viewcommand (также в CodeProject) - person ygoe; 01.02.2014
comment
Я использовал этот метод для распечатки документов WPF FlowDocuments. Работает красиво. Спасибо - person Gordon Slysz; 03.05.2014
comment
Я хочу один в Silverlight? Можем ли мы его использовать? - person Bigeyes; 06.01.2017
comment
Хотя хорошо знать, как использовать внедрение зависимостей из модели представления, я думаю, что отправка сообщения (это решение) более прямолинейно и легко. - person scsfdev; 01.11.2017
comment
Я использовал это в чистом решении MVVM. Отлично работает, спасибо - person Michael K; 17.10.2018

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

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

<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
}
person Leo Vildosola    schedule 26.06.2011

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

person Rich N    schedule 07.07.2015
comment
Это работает последовательно, тогда как принятый ответ - нет. Спасибо! - person NathanAldenSr; 19.11.2016

В моем случае 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);
  }
}
person Community    schedule 11.07.2013

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

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

person Shawn    schedule 24.06.2010
comment
Я изменил объект fe = (FrameworkElement) d; в FrameworkElement fe = (FrameworkElement) d; так что intellisense работает - person ; 29.01.2013
comment
По-прежнему не решает проблему. Элемент остается сфокусированным каждый раз, когда я к нему возвращаюсь. - person ygoe; 27.02.2013

Великолепный код 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);
            }
        }
    }
}
person PEK    schedule 02.02.2014

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

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

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

person Kyeotic    schedule 31.08.2011
comment
Почему у вас есть оператор if? если для _isFocused установлено значение false, оно будет просто изменено на значение в следующей строке. - person Damien McGivern; 07.10.2011
comment
@Tyrsius Вы можете решить эту проблему, передав свойство зависимости в Coerce, см. Здесь- social.msdn.microsoft.com/Forums/en-US/wpf/thread/ - person RichardOD; 17.02.2012

Я использую WPF / Caliburn Micro и обнаружил, что dfaivre представил здесь общее и работоспособное решение: http://caliburnmicro.codeplex.com/discussions/222892

person kpp    schedule 31.10.2013

Я нашел решение, отредактировав следующий код. Нет необходимости устанавливать свойство привязки сначала 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);
        }
    }
}
person Bharat Mendapara    schedule 11.12.2014

Альтернативный подход, основанный на ответе @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, чтобы установить фокус на текстовом поле.

person stuicidle    schedule 26.11.2019

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

OR

<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();
}
person ADM-IT    schedule 29.11.2013

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

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

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

person ygoe    schedule 01.02.2014

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

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

    }
person Hugh    schedule 15.06.2016

Прежде всего, я хотел бы поблагодарить Аванку за помощь в решении моей проблемы с фокусировкой. Однако в опубликованном им коде есть ошибка, а именно в строке: 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);
            }
        }
    }
person user2127475    schedule 22.11.2017

После реализации принятого ответа я столкнулся с проблемой, что при навигации по представлениям с помощью 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();
            }));
        }
    }
person Joe H    schedule 13.12.2018

Я нашел решение проблемы IsVisible Crucial очень полезным. Это не решило полностью мою проблему, но кое-какой дополнительный код, следующий по тому же шаблону, что и шаблон 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();
    }
}
person Wayne Maurer    schedule 08.11.2011

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

<Window x:class...
   ...
   ...
   FocusManager.FocusedElement="{Binding ElementName=myTextBox}"
>
<Grid>
<TextBox Name="myTextBox"/>
...
person Zoltan    schedule 06.07.2018
comment
Мне это нравится. Это хорошо работает, если вы хотите установить начальный фокус. - person user2430797; 20.02.2020

person    schedule
comment
OP использует WPF. Код фокуса для WinForms не поможет. - person Josh G; 08.06.2011