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

Wpf 以编程方式更改文本后保留文本框选择,wpf,Wpf,我有一个文本框,它的文本可以通过从text属性绑定到viewmodel属性的方式进行编程更改。例如,这可能是按键的结果(例如。↑ 或↓), 但也可能在没有任何用户输入的情况下发生。发生这种情况时,文本框中的任何现有选择都会被删除。我希望的行为是:如果文本框具有焦点,并且在编程更改之前选择了所有文本(或者如果文本为空),我希望在更改后选择所有文本。但是,在用户键入导致更改后不应选择文本,因为这将意味着用户将反复替换一个字符 我还没有找到一个方法来实现这一点。有可能吗 具体来说:我设置了一个全局事

我有一个文本框,它的文本可以通过从
text
属性绑定到viewmodel属性的方式进行编程更改。例如,这可能是按键的结果(例如。↑ 或↓), 但也可能在没有任何用户输入的情况下发生。发生这种情况时,文本框中的任何现有选择都会被删除。我希望的行为是:如果文本框具有焦点,并且在编程更改之前选择了所有文本(或者如果文本为空),我希望在更改后选择所有文本。但是,在用户键入导致更改后不应选择文本,因为这将意味着用户将反复替换一个字符

我还没有找到一个方法来实现这一点。有可能吗


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

EventManager.RegisterClassHandler(
    typeof(TextBox),
    UIElement.GotFocusEvent,
    new RoutedEventHandler((s, _) => (s as TextBox)?.SelectAll()));
但是,在我的一个视图中,从文本框A中跳出选项卡会触发一个异步操作,该操作会更改文本框B中的文本(这是选项卡顺序中的下一个)。这种情况发生得非常快,但文本框B在文本更改之前获得焦点,因此文本未被选中。我希望选择到达文本框B的文本,以便用户可以更轻松地更改它(如果需要)

我希望选择
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;
}

我更喜欢在可以添加到XAML中的
行为中实现这种功能;这需要

我还没有完全测试它,因为我不确定如何复制您的“异步操作”,但它似乎适用于我尝试过的“正常”编程值更改

如果您确实不想要它的
行为
方面,那么从中提取事件处理逻辑以用于您喜欢的任何方法应该是相当简单的

下面是一个简短的gif:


默认情况下,
行为
必须手动应用于XAML中的每个控件,这可能非常烦人。如果您将此基类用于
行为
,则可以使用
样式
添加它。这也适用于隐式样式,因此您可以在
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());
            }
        }
    }
}
并且是这样应用的(它甚至可以绑定到
bool
并动态打开和关闭!):



就我个人而言,我更喜欢使用基于
风格的方法,即使是在将其添加到单个一次性控件时也是如此。它的键入量明显减少,而且我不必记住如何为
交互
行为
命名空间定义
xmlns

谢谢,但这在两点上失败:首先,我是我不希望从用户键入的文本中选择文本,其次,这将在通过编程更改文本和选择新文本之间造成不可接受的延迟。1.不提供每个按键之间的间隔时间少于3秒。2.如果延迟“不可接受”,您需要减少时间跨度或处理另一个事件。如何执行此操作取决于文本属性的设置方式,而您的问题没有说明。我现在更新了答案,以澄清更新是通过绑定进行的。请处理视图模型的PropertyChanged事件,然后执行相同的操作。
<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>
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();
    }
}
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>
<Style TargetType="TextBox">
    <Setter Property="KeepSelectionBehavior.IsEnabledForStyle" Value="True" />
</Style>