【问题标题】:How to set focus on AND SELECT ALL of an initial text box (MVVM-Style)?如何设置焦点并选择所有初始文本框(MVVM 样式)?
【发布时间】:2014-01-09 16:46:11
【问题描述】:

我有一个简单的 WPF 页面,其中有一个文本框字段,我的客户希望在页面显示时突出显示该文本框字段。在后面的代码中,它将是三行,但我正在浏览 MVVM(我开始认为它有点被高估了)。我已经尝试了很多不同的行为变体和全局事件以及FocusManager.FocusedElement,但我什么都做不了。

我使用的大部分代码最终都调用了这两行代码:

Keyboard.Focus(textBox);
textBox.SelectAll();

但无论我将这些行放在哪里,文本框都只会聚焦;没有选择文本。我从来没有在这么简单的事情上遇到过这么大的麻烦。我已经在互联网上撞了两个小时。有谁知道怎么做?

同样,我想要做的就是让文本框成为焦点,并且在导航到页面时它的文本全部被选中。请帮忙!

【问题讨论】:

    标签: wpf focus


    【解决方案1】:

    “焦点”和“从文本框中选择所有文本”是视图特定的问题

    把它放在代码后面。它根本不会破坏 MVVM 分离。

    public void WhateverControl_Loaded(stuff)
    {
        Keyboard.Focus(textBox);
        textBox.SelectAll();
    }
    

    如果您需要响应特定的应用程序/业务逻辑来执行此操作。创建附加属性。

    或者:

    让您的 View 通过以下方式解析 ViewModel:

    this.DataContext as MyViewModel;
    

    然后在 ViewModel 中创建一些您可以挂钩的事件:

    public class MyViewModel
    {
        public Action INeedToFocusStuff {get;set;}
    
        public void SomeLogic()
        {
            if (SomeCondition)
                INeedToFocusStuff();
        }
    }
    

    然后将其连接到视图中:

    public void Window_Loaded(Or whatever)
    {
        var vm = this.DataContext as MyViewModel;
        vm.INeedToFocusStuff += FocusMyStuff;
    }
    
    public void FocusMyStuff()
    {
        WhateverTextBox.Focus();
    }
    

    看看这个简单的抽象如何在 View 中保留与 View 相关的内容,在 ViewModel 中保留与 ViewModel 相关的内容,同时允许它们交互。把事情简单化。 WPF 应用程序不需要 NASA 的服务器。


    没有 MVVM 没有被高估,MVVM 非常有帮助,我想说甚至是必要的。一旦您开始使用诸如 ListBoxes 或 DataGrids 之类的 ItemsControl,您就会很快意识到这一点。

    【讨论】:

    • 多年来我一直在使用 WPF 和 MVVM。与我遇到的一些困难相比,我得到的好处要多得多。我在学校学到了说不要把任何东西放在后面的代码中,并使 UI 中的所有内容都可声明。也许这就是我需要拒绝的,但有时我很难画出这条线。
    • @Jordan 我认为这是不言而喻的:Data 进入 ModelPresentation Logic 进入 ViewModel。 UI 进入视图。
    • 问题似乎出在对 Keyboard.Focus 和 textbox.SelectAll 本身的调用上。无论我在哪里打电话SelectAll 都不会。
    • 当我编写一个 CRUD 应用程序时,它是直截了当的,但 90% 的时间我的应用程序不是以数据为中心的,因此将它们映射到该模式有点模糊。跨度>
    • @Jordan 你在Loaded 事件中调用这些方法吗?发布您当前的代码。
    【解决方案2】:

    以下是一些解决方法:

    使用 Interaction.Behaviors

    您可以安装名为Microsoft.Xaml.Behaviors.Wpf 的 NuGet 包,并编写自己的 Behavior:

    using Microsoft.Xaml.Behaviors;
    using System.Windows;
    using System.Windows.Controls;
    
    public class AutoSelectAllBehavior : Behavior<TextBox>
    {
        protected override void OnAttached()
        {
            base.OnAttached();
    
            AssociatedObject.GotFocus += AssociatedObject_GotFocus;
        }
    
        private void AssociatedObject_GotFocus(object sender, RoutedEventArgs e)
        {
            if (AssociatedObject is TextBox box)
                box.SelectAll();
        }
    
        protected override void OnDetaching()
        {
            base.OnDetaching();
    
            AssociatedObject.GotFocus -= AssociatedObject_GotFocus;
        }
    }
    

    并将此行为附加到 xaml 中的 TextBox:

    <!-- xmlns:i="http://schemas.microsoft.com/xaml/behaviors" -->
    
    <TextBox>
        <i:Interaction.Behaviors>
            <br:AutoSelectAllBehavior />
        </i:Interaction.Behaviors>
    </TextBox>
    

    使用 Interaction.Triggers

    这与上一节中提到的相同。这个特殊的可以考虑让你能够将 UIElement 事件绑定到你的 ViewModel。

    在您的 ViewModel 中,假设您有一个 ICommand 中继命令(您可能还需要 Microsoft.Toolkit.MVVM 以便您可以使用一些方便的中继命令):

    public ICommand SelectAllCommand { get; }
    
    public ViewModel()
    {
        SelectAllCommand = new RelayCommand<TextBox>(box => box.SelectAll());
    }
    

    然后通过设置触发器将此命令附加到文本框:

    <TextBox>
        <i:Interaction.Triggers>
            <i:EventTrigger EventName="GotFocus">
                <i:InvokeCommandAction Command="{Binding SelectAllCommand}" CommandParameter="{Binding RelativeSource={RelativeSource AncestorType=TextBox}}"/>
            </i:EventTrigger>
        </i:Interaction.Triggers>
    </TextBox>
    

    使用附加属性

    你也可以使用附加属性(写你自己的类派生自TextBox和使用依赖属性很相似):

    using System.Windows;
    using System.Windows.Controls;
    
    public class TextBoxProperties
    {
        public static bool GetAutoSelectAll(DependencyObject obj)
        {
            return (bool)obj.GetValue(AutoSelectAllProperty);
        }
    
        public static void SetAutoSelectAll(DependencyObject obj, bool value)
        {
            obj.SetValue(AutoSelectAllProperty, value);
        }
    
        public static readonly DependencyProperty AutoSelectAllProperty =
            DependencyProperty.RegisterAttached("AutoSelectAll", typeof(bool), typeof(TextBoxProperties), new PropertyMetadata(false, TextBoxProperties_PropertyChanged));
    
        private static void TextBoxProperties_PropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            if ((bool)e.NewValue)
            {
                if (d is TextBox box)
                {
                    box.GotFocus += TextBox_GotFocus;
                }
            }
        }
        private static void TextBox_GotFocus(object sender, RoutedEventArgs e)
        {
            var box = sender as TextBox;
            box.SelectAll();
        }
    }
    

    然后你可以像这样使用它:

    <!-- xmlns:ap="..." -->
    
    <TextBox ap:TextBoxProperties.AutoSelectAll="True" />
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-06-21
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多