从视图模型中将焦点设置在 WPF 中的 TextBox 上

Posted

技术标签:

【中文标题】从视图模型中将焦点设置在 WPF 中的 TextBox 上【英文标题】:Set focus on TextBox in WPF from view model 【发布时间】:2010-11-24 06:30:37 【问题描述】:

我认为我有一个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 时,this 是一个很好的解决方案。 【参考方案1】:

让我分三个部分回答你的问题。

    我想知道您的示例中的“cs.txtCompanyID”是什么?它是一个文本框控件吗?如果是,那么你走错路了。一般来说,在 ViewModel 中引用 UI 并不是一个好主意。你可以问“为什么?”但这是在 *** 上发布的另一个问题 :)。

    跟踪 Focus 问题的最佳方法是... 调试 .Net 源代码。不开玩笑。它为我节省了很多时间。要启用 .net 源代码调试,请参阅 Shawn Bruke's 博客。

    最后,我用来从 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。

干杯。

【讨论】:

好主意。我需要将 IsUserNameFocused 设置为 true,然后再设置为 false 才能正常工作,对吗? 如果你希望你的控件接收键盘焦点和逻辑焦点,你也应该从你的OnIsFocusedPropertyChanged事件中调用Keyboard.Focus(uie); 这应该如何使用?如果我将我的属性设置为 true,则控件将获得焦点。但是当我回到这个观点时,它总是会再次聚焦。从 OnIsFocusedPropertyChanged 重置它不会改变这一点。从 ViewModel 设置后直接重置它不再关注任何东西。它不起作用。这 70 位支持者到底做了什么? 您可以将obj.SetValue(IsFocusedProperty, value);更改为obj.SetValue(IsFocusedProperty, false);,而不必再次设置false和true。 我还将回调更改为:...if ((bool)e.NewValue &amp;&amp; uie.Dispatcher != null) uie.Dispatcher.BeginInvoke(DispatcherPriority.Normal, (Action)(() =&gt; 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 ... 如果我想多次设置焦点,有时我什至必须在 ViewModel 中将“IsFocused”重置为 false。但后来它起作用了,其他一些方法失败了。【参考方案2】:

我知道这个问题现在已经被回答了一千遍了,但是我对 Anvaka 的贡献做了一些修改,我认为这将帮助其他有类似问题的人。

首先,我像这样更改了上面的附加属性:

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 的建议。我很久以前就在我自己的代码中做到了这一点,但从未更新过这篇文章。由于此更改,WPF 代码中不再需要 Mode=TwoWay。

【讨论】:

这对我很有效,除了我需要在 GotFocus/LostFocus 中添加“if (e.Source == e.OriginalSource)”检查,否则在我的 UserControl 上使用时它会溢出(字面意思) ,它确实将焦点重定向到内部组件。我删除了 Visible 检查,接受它就像 .Focus() 方法一样工作的事实。如果 .Focus() 不起作用,则绑定不应该起作用 - 这对我的场景来说没问题。 我在 WF 4.5 中使用它。在 IsFocusedChanged 上,我有一个场景(一个 Activity 被重新加载),其中 e.NewValue 为 null 并引发异常,因此请先检查。这个小改动一切正常。 感谢这个 wprks 太好了 :) 我刚刚在“FrameworkPropertyMetadata”中添加了“BindsTwoWayByDefault = true”以将默认模式设置为 TwoWayBinding,因此每个 Binding 都不需要它 我意识到这是一个旧答案,但我遇到了一种情况,即我想将焦点转移到的控件的 IsEnabled 属性与多值转换器相关联。显然,在多值转换器执行之前调用了 GotFocus 事件处理程序......这意味着此时控件被禁用,因此一旦 GotFocus 完成,就会调用 LostFocus(我猜是因为控件仍然被禁用) .关于如何处理的任何想法? @MarkOlbert 使用fe.Dispatcher.BeginInvoke(new Action(() =&gt; fe.Focus(); ), DispatcherPriority.Loaded); 它在加载后更新。更多信息:telerik.com/forums/isfocused-property#OXgFYZFOg0WZ2rxidln61Q【参考方案3】:

我认为最好的方法是保持 MVVM 原则的干净, 所以基本上你必须使用 MVVM Light 提供的 Messenger 类,这里是如何使用它:

在你的视图模型(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 标准

【讨论】:

好吧,如果你想保持 MVVM 原则的简洁,你一开始就不会在你的代码中编写代码。我相信附加属性方法要干净得多。它也不会在您的视图模型中引入很多魔术字符串。 厄尔尼诺:你从哪里得到的想法在你的视图代码隐藏中不应该有任何东西?任何与 UI 相关的内容都应该在视图的代码隐藏中。设置 UI 元素的焦点一定应该在视图的代码隐藏中。让 viewmodel 弄清楚何时发送消息;让视图弄清楚如何处理消息。 就是 M-V-VM 所做的:分离数据模型、业务逻辑和 UI 的关注点。 根据这个建议,我实现了自己的 ViewCommandManager 来处理连接视图中的调用命令。它基本上是常规命令的另一个方向,对于 ViewModel 需要在其视图中执行某些操作的这些情况。它使用像数据绑定命令和 WeakReferences 这样的反射来避免内存泄漏。 dev.unclassified.de/source/viewcommand(也在 CodeProject 上) 我使用这种方法打印出 WPF FlowDocuments。工作得很好。谢谢 我想要一个 Silverlight 的?我们可以使用它吗?【参考方案4】:

这些都不完全适合我,但为了其他人的利益,这是我最终根据这里已经提供的一些代码编写的。

用法如下:

<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

【讨论】:

【参考方案5】:

这是一个旧线程,但似乎没有解决 Anavanka 接受的答案问题的代码的答案:如果您将 viewmodel 中的属性设置为 false,或者如果您设置你的属性为真,用户手动点击其他东西,然后你再次将其设置为真。在这些情况下,我也无法让 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 并不是真的要以这种方式使用,所以也许代码隐藏是要走的路。

【讨论】:

这始终有效,而接受的答案则不然。谢谢!【参考方案6】:

就我而言,直到我更改 OnIsFocusedPropertyChanged 方法后,FocusExtension 才起作用。当断点停止进程时,原始版本仅在调试中工作。在运行时,这个过程太快了,什么也没发生。通过这个小修改和我们朋友 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);
  

【讨论】:

【参考方案7】:

问题是一旦 IsUserNameFocused 设置为 true,它就永远不会为 false。这通过处理 FrameworkElement 的 GotFocus 和 LostFocus 来解决它。

我的源代码格式有问题,所以这里是link

【讨论】:

我更改了“object fe = (FrameworkElement)d;”到“FrameworkElement fe = (FrameworkElement)d;”所以智能感知工作 仍然没有解决问题。每次我回到该元素时,它都会保持焦点。【参考方案8】:

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

【讨论】:

【参考方案9】:

对于那些尝试使用上述 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 将在下一行更改为值。 @Tyrsius 您可以通过将依赖项属性设置为 Coerce 来解决此问题,请参见此处-social.msdn.microsoft.com/Forums/en-US/wpf/thread/…【参考方案10】:

我使用 WPF / Caliburn Micro 发现“dfaivre”已经制定了一个通用且可行的解决方案 这里: http://caliburnmicro.codeplex.com/discussions/222892

【讨论】:

【参考方案11】:

对于 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();

【讨论】:

【参考方案12】:

我通过如下编辑代码找到了解决方案。不需要先设置 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);
        
    

【讨论】:

【参考方案13】:

基于@Sheridan 答案here 的替代方法

 <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 以将焦点设置在您的文本框上

【讨论】:

【参考方案14】:

您可以使用 ViewCommand 设计模式。它描述了一种 MVVM 设计模式通过命令从 ViewModel 控制 View 的方法。

我根据 King A.Majid 的建议实现了它,使用 MVVM Light Messenger 类。 ViewCommandManager 类处理在连接视图中调用命令。对于 ViewModel 需要在其视图中执行某些操作的这些情况,它基本上是常规命令的另一个方向。它使用反射(如数据绑定命令和 WeakReferences)来避免内存泄漏。

http://dev.unclassified.de/source/viewcommand(也发布在 CodeProject 上)

【讨论】:

【参考方案15】:

似乎没有人包含最后一步,以便通过绑定变量轻松更新属性。这就是我想出的。如果有更好的方法,请告诉我。

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

视图模型

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

    

【讨论】:

【参考方案16】:

首先,我要感谢 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);
            
        
    

【讨论】:

【参考方案17】:

在实施接受的答案后,我确实遇到了一个问题,即在使用 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();
            ));
        
    

【讨论】:

【参考方案18】:

我发现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();
    

【讨论】:

【参考方案19】:
public class DummyViewModel : ViewModelBase
    
        private bool isfocused= false;
        public bool IsFocused
        
            get
            
                return isfocused;
            
            set
            
                isfocused= value;
                OnPropertyChanged("IsFocused");
            
        
    

【讨论】:

【参考方案20】:

这样做:

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

【讨论】:

我喜欢这个。如果您想设置初始焦点,这很有效。【参考方案21】:
System.Windows.Forms.Application.DoEvents();
Keyboard.Focus(tbxLastName);

【讨论】:

OP 正在使用 WPF。 WinForms 的焦点代码无济于事。

以上是关于从视图模型中将焦点设置在 WPF 中的 TextBox 上的主要内容,如果未能解决你的问题,请参考以下文章

WPF 复合控件焦点设置

从视图模型 (WPF) 调用视图中的动画

将焦点设置在WPF中的文本框中

将焦点设置在 xaml wpf 中的文本框上

如何从作为wpf mvvm模式中的窗口打开的视图模型中关闭用户控件?

WPF:无法设置焦点