将WPF文本框中光标位置设置为字符串值的末尾

86

我正在尝试在首次打开窗口时将WPF文本框中光标/光标位置设置为字符串值的结尾。我使用FocusManager在窗口打开时将焦点设置在我的文本框上。

似乎没有任何作用,有什么建议吗?

注意,我正在使用MVVM模式,并且只包括了代码中的一部分XAML。

<Window 
    FocusManager.FocusedElement="{Binding ElementName=NumberOfDigits}"
    Height="400" Width="800">

    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition/>
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition/>
            <RowDefinition/>
        </Grid.RowDefinitions>

        <TextBox Grid.Column="0" Grid.Row="0" 
                 x:Name="NumberOfDigits"
                 IsReadOnly="{Binding Path=IsRunning, Mode=TwoWay}"
                 VerticalContentAlignment="Center"
                 Text="{Binding Path=Digits, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
        <Button Grid.Column="0" Grid.Row="1" 
                 Margin="10,0,10,0"
                 IsDefault="True"
                 Content="Start" 
                 Command="{Binding StartCommand}"/>
    </Grid>
 </Window>
11个回答

124

您可以使用TextBoxCaretIndex属性设置插入符号的位置。请注意,这不是一个DependencyProperty。尽管如此,您仍然可以像这样在XAML中进行设置:

<TextBox Text="123" CaretIndex="{x:Static System:Int32.MaxValue}" />
请记得在设置CaretIndex属性之后再设置Text属性,否则它将不起作用。因此,如果您像示例中那样绑定到Text,那么它可能不起作用。在这种情况下,可以使用如下的代码方式进行操作。
NumberOfDigits.CaretIndex = NumberOfDigits.Text.Length;

26

您还可以创建一个行为(Behavior),它虽然仍然是代码后台,但具有可重用性的优点。

下面是一个简单的行为类示例,使用文本框的“focus”事件:

class PutCursorAtEndTextBoxBehavior: Behavior<UIElement>
{
   private TextBox _textBox;

   protected override void OnAttached()
   {
        base.OnAttached();

        _textBox = AssociatedObject as TextBox;

        if (_textBox == null)
        {
            return;
        }
        _textBox.GotFocus += TextBoxGotFocus;
   }

    protected override void OnDetaching()
    {
        if (_textBox == null)
        {
            return;
        }
        _textBox.GotFocus -= TextBoxGotFocus;

        base.OnDetaching();
    }

    private void TextBoxGotFocus(object sender, RoutedEventArgs routedEventArgs)
    {
        _textBox.CaretIndex = _textBox.Text.Length;
    }
}    

然后,在您的XAML中,您可以这样附加行为:

    <TextBox x:Name="MyTextBox" Text="{Binding Value}">
        <i:Interaction.Behaviors>
            <behaviors:PutCursorAtEndTextBoxBehavior/>
        </i:Interaction.Behaviors>
    </TextBox>

5

这对我很有用。我也在使用MVVM模式。然而,我使用MMVM的目的是使单元测试成为可能,并使更新UI变得更容易(松耦合)。我不认为我会对光标位置进行单元测试,所以我不介意为这个简单的任务退回到代码后面。

    public ExpeditingLogView()
    {
        InitializeComponent();

        this.Loaded += (sender, args) =>
        {                                
            Description.CaretIndex = Description.Text.Length;
            Description.ScrollToEnd();
            Description.Focus();
        };
    }

4

这里的所有回答都对我没用。我正在使用绑定来控制文本框,并需要在弹出窗口后将光标移动到右侧。下面的代码让我成功实现了:

public MyWindow()
{
    InitializeComponent();

    ContentRendered += (sender, args) =>
    {
        MyTextBox.CaretIndex = MyTextBox.Text.Length;
        MyTextBox.ScrollToEnd(); // not necessary for single line texts
        MyTextBox.Focus();
    };
}

与 Ceranski 的答案类似。我们不是将代码添加到 "Loaded" 事件中,而是添加到 "ContentRendered" 事件中。

它对我有用,但MyTextBox.ScrollToEnd()不是必需的。 - Simple

4

@Louis的解决方案不能用于绑定模板中使用textbox的情况,或任何类型的延迟绑定或延迟值分配。

因此,如果textbox例如在Datagrid单元格中用作模板,则需要对该解决方案进行微小修改才能正常工作。

那就是订阅文本更改事件。

 class PutCursorAtEndTextBoxBehavior : Behavior<UIElement>
    {
        private TextBox _textBox;

        protected override void OnAttached()
        {
            base.OnAttached();

            _textBox = AssociatedObject as TextBox;

            if (_textBox == null)
            {
                return;
            }
            _textBox.GotFocus += TextBoxGotFocus;
            // to make it work with binding
            _textBox.TextChanged += TextBoxGotFocus;
        }

        protected override void OnDetaching()
        {
            if (_textBox == null)
            {
                return;
            }
            _textBox.GotFocus -= TextBoxGotFocus;
            _textBox.TextChanged -= TextBoxGotFocus;

            base.OnDetaching();
        }

        private void TextBoxGotFocus(object sender, RoutedEventArgs routedEventArgs)
        {
            _textBox.CaretIndex = _textBox.Text.Length;
        }
    }

3

如果是多行文本框,仅设置光标是不够的。尝试以下操作:

NumberOfDigits.ScrollToEnd();

仅提供代码的答案并不是好的答案,需要添加几行说明问题所在以及你的代码如何解决它。 - MikeT

3

在WPF中,如果一行太长了,滚动到行末也很重要。因此我使用以下代码:

text_Box.Text = text;
text_Box.CaretIndex = text.Length;
text_Box.ScrollToHorizontalOffset(double.MaxValue);
// or you can use this - for me works also
// text_Box.ScrollToHorizontalOffset(text_Box.GetRectFromCharacterIndex(openFileDialog.FileName.Length).Right);

但是请注意以下警告(对我来说没问题 - 可能已经修复): TextBox ScrollToHorizontalOffset在文本足够长后将无法滚动



0
我想创建一个已绑定到ViewModel的预填充文本框的UserControl / View,并且当控件打开时,焦点自动设置在文本框上,并且插入符号位置在末尾。这是我找到的唯一可行的方式:
public TextBoxDialogView()
{
    InitializeComponent();

    TextBox.GotKeyboardFocus += (sender, args) =>
    {
        TextBox.CaretIndex = TextBox.Text.Length;
    };
    _ = TextBox.Focus();
}

目前看起来运行得很好...


0
由于某些原因,我不得不使用“:”。
Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Background, new Action(() => 
{
    textBox.CaretIndex = textBox.Text.Length;
    textBox.ScrollToEnd();
}));

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接