代码之家  ›  专栏  ›  技术社区  ›  cmeeren

以编程方式更改文本后保留文本框选择

wpf
  •  2
  • cmeeren  · 技术社区  · 6 年前

    我有一个TextBox,它的文本可以通过绑定从它的 Text 属性转换为viewmodel属性。例如,这可能是按键的结果(例如。


    具体来说:我设置了一个全局事件处理程序,在文本框聚焦时选择所有文本,以便用户在需要时更轻松地编辑文本框中的现有文本:

    EventManager.RegisterClassHandler(
        typeof(TextBox),
        UIElement.GotFocusEvent,
        new RoutedEventHandler((s, _) => (s as TextBox)?.SelectAll()));
    

    2 回复  |  直到 4 年前
        1
  •  2
  •   Bradley Uffner    6 年前

    我更喜欢在 Behavior 可以添加到XAML中;这需要 System.Windows.Interactivity.WPF NuGet Package .

    如果你真的不想 另一方面,从中提取事件处理逻辑以用于您喜欢的任何方法应该是相当简单的。

    Code in Action Animation

    public class KeepSelectionBehavior : Behavior<TextBox>
    {
        private bool _wasAllTextSelected = false;
        private int inputKeysDown = 0;
    
        protected override void OnAttached()
        {
            base.OnAttached();
    
            CheckSelection();
            AssociatedObject.TextChanged += TextBox_TextChanged;
            AssociatedObject.SelectionChanged += TextBox_SelectionChanged;
            AssociatedObject.PreviewKeyDown += TextBox_PreviewKeyDown;
            AssociatedObject.KeyUp += TextBox_KeyUp;
        }
    
        protected override void OnDetaching()
        {
            base.OnDetaching();
    
            AssociatedObject.TextChanged -= TextBox_TextChanged;
            AssociatedObject.SelectionChanged -= TextBox_SelectionChanged;
            AssociatedObject.PreviewKeyDown -= TextBox_PreviewKeyDown;
            AssociatedObject.KeyUp -= TextBox_KeyUp;
        }
    
        private void TextBox_TextChanged(object sender, TextChangedEventArgs e)
        {
            if (_wasAllTextSelected && inputKeysDown == 0)
            {
                AssociatedObject.SelectAll();
            }
            CheckSelection();
        }
    
        private void TextBox_SelectionChanged(object sender, RoutedEventArgs e)
        {
            CheckSelection();
        }
    
        private void TextBox_PreviewKeyDown(object sender, KeyEventArgs e)
        {
            if (IsInputKey(e.Key))
            {
                inputKeysDown++;
            }
        }
    
        private void TextBox_KeyUp(object sender, KeyEventArgs e)
        {
            if (IsInputKey(e.Key))
            {
                inputKeysDown--;
            }
        }
    
        private bool IsInputKey(Key key)
        {
            return
                key == Key.Space ||
                key == Key.Delete ||
                key == Key.Back ||
                (key >= Key.D0 && key <= Key.Z) ||
                (key >= Key.Multiply && key <= Key.Divide) ||
                (key >= Key.Oem1 && key <= Key.OemBackslash);
        }
    
        private void CheckSelection()
        {
            _wasAllTextSelected = AssociatedObject.SelectionLength == AssociatedObject.Text.Length;
        }
    }
    

    你可以这样使用它:

    <Window
        x:Class="ScriptyBot.Client.TestWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        Title="TestWindow"
        Width="800"
        Height="450"
        mc:Ignorable="d">
        <StackPanel>
            <TextBox Name="TextBox1" Margin="20">
                <i:Interaction.Behaviors>
                    <behaviors:KeepSelectionBehavior />
                </i:Interaction.Behaviors>
            </TextBox>
        </StackPanel>
    </Window>
    

    我正在用一个简单的 DispatchTimer 每秒钟更新一次文本:

    public partial class TestWindow : Window
    {
        private DispatcherTimer timer;
    
        public TestWindow()
        {
            InitializeComponent();
    
            timer = new DispatcherTimer(DispatcherPriority.Normal);
            timer.Interval = TimeSpan.FromSeconds(1);
            timer.Tick += (sender, e) => { TextBox1.Text = DateTime.Now.ToString(); };
            timer.Start();
        }
    }
    

    默认情况下 行为 ,您可以使用 Style app.xaml

    public class AttachableForStyleBehavior<TComponent, TBehavior> : Behavior<TComponent>
        where TComponent : System.Windows.DependencyObject
        where TBehavior : AttachableForStyleBehavior<TComponent, TBehavior>, new()
    {
        public static readonly DependencyProperty IsEnabledForStyleProperty =
            DependencyProperty.RegisterAttached(name: "IsEnabledForStyle",
                                                propertyType: typeof(bool),
                                                ownerType: typeof(AttachableForStyleBehavior<TComponent, TBehavior>),
                                                defaultMetadata: new FrameworkPropertyMetadata(false, OnIsEnabledForStyleChanged));
    
        public bool IsEnabledForStyle
        {
            get => (bool)GetValue(IsEnabledForStyleProperty);
            set => SetValue(IsEnabledForStyleProperty, value);
        }
    
        private static void OnIsEnabledForStyleChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            if (d is UIElement uiElement)
            {
                var behaviors = Interaction.GetBehaviors(uiElement);
                var existingBehavior = behaviors.FirstOrDefault(b => b.GetType() == typeof(TBehavior)) as TBehavior;
    
                if ((bool)e.NewValue == false && existingBehavior != null)
                {
                    behaviors.Remove(existingBehavior);
                }
                else if ((bool)e.NewValue == true && existingBehavior == null)
                {
                    behaviors.Add(new TBehavior());
                }
            }
        }
    }
    

    行为 类更改为如下所示:

    public class KeepSelectionBehavior : AttachableForStyleBehavior<TextBox, KeepSelectionBehavior>
    

    并且是这样应用的(它甚至可以绑定到 bool 动态开启和关闭!):

    <Style TargetType="TextBox">
        <Setter Property="KeepSelectionBehavior.IsEnabledForStyle" Value="True" />
    </Style>
    

    就我个人而言,我更喜欢使用 风格 无论如何,即使将其添加到单个一次性控件中,也要使用基于方法的方法。它大大减少了输入,而且我不必记住如何定义 xmlns Interactions Behaviors 名称空间。

        2
  •  0
  •   mm8    6 年前

    我想要收到的文本 TextBox B,以便用户在需要时更容易更改。

    TextChanged 事件。每当 Text 属性已更改。Yoy可能需要添加一个延迟,以便用户可以在每个按键笔划上不选择文本的情况下进行键入:

    private DateTime _last;
    private void txt2_TextChanged(object sender, TextChangedEventArgs e)
    {
        if (DateTime.Now.Subtract(_last) > TimeSpan.FromSeconds(3))
        {
            TextBox tb = (TextBox)sender;
            if (Keyboard.FocusedElement == tb)
                tb.SelectAll();
        }
        _last = DateTime.Now;
    }