Установите фокус на текстовое поле в WPF из модели просмотра(C#)




mvvm textbox (12)

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

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

У меня есть 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}"/>

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

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

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


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

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

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


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

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

    }

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

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

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

  3. Наконец, общий подход, который я использую для установки фокуса из ViewModel, - это Attached Properties. Я написал очень простое прикрепленное свойство, которое можно установить на любом 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.

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


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

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


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

в вашей viewmodel (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



Я нашел решение 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();
    }
}

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

System.Windows.Forms.Application.DoEvents();
Keyboard.Focus(tbxLastName);




focus