我正在开发一个使用MVVM的WPF应用程序,用户需要将文本从TextBlock元素输入到文本框中.文本框的占位符被"填充"并定位在文本块的正上方.它有一个透明的背景.当用户输入文本时,他们到达TextBlock的右侧,接下来的单词将被隐藏.

我希望文本框自动滚动,以便最后输入的字符在文本框的左侧可见,允许用户看到他们下一步需要输入的内容.

以下是我使用的XAML代码:

<ScrollViewer
                x:Name="scroll"
                Grid.Row="1"
                HorizontalAlignment="Stretch"
                VerticalAlignment="Stretch"
                CanContentScroll="True"
                HorizontalScrollBarVisibility="Visible"
                VerticalScrollBarVisibility="Disabled">
                <Grid>
                    <Grid.RowDefinitions>
                        <RowDefinition />
                    </Grid.RowDefinitions>

                    <Border
                        Grid.Row="0"
                        HorizontalAlignment="Stretch"
                        VerticalAlignment="Stretch"
                        Background="#8EABAF">
                        <TextBlock
                            x:Name="tblTextToType"
                            Margin="2,0,0,0"
                            VerticalAlignment="Center"
                            AllowDrop="False"
                            FontFamily="/Fonts/Nunito_Sans/#NunitoSans"
                            FontSize="28"
                            Foreground="Gray"
                            TextAlignment="Center" />
                    </Border>

                    <TextBox
                        x:Name="tbTypedText"
                        Grid.Row="0"
                        HorizontalAlignment="Stretch"
                        VerticalAlignment="Stretch"
                        AllowDrop="False"
                        Background="Transparent"
                        FontFamily="/Fonts/Nunito_Sans/#NunitoSans"
                        FontSize="28"
                        Foreground="Black"
                        IsInactiveSelectionHighlightEnabled="True"
                        IsReadOnly="True"
                        IsUndoEnabled="False"
                        MaxLines="1"
                        SelectionBrush="Green">
                    </TextBox>
                </Grid>
            </ScrollViewer>

此外,我还将两个命令绑定到应用程序窗口以处理击键.如果输入的文本与TextBlock的内容匹配,则会以编程方式将其添加到TextBox中.

The Type method performs the required effect with scrolling text, but only after the first time the text exceeds the width of the TextBox, and I need it to happen after each such exceeding

C#代码处理KeyDownCommand和KeyUpCommand命令:

public ICommand KeyDownCommand { get; private set; }
        private void ExecuteKeyDown(object? obj)
        {
            if (obj is KeyEventArgs e && _keyboardButtons.ContainsKey(e.Key))
            {
                Key key = (e.Key == Key.System) ? e.SystemKey : e.Key;
                _keyboardButtons[key].KeyGrid.Background = SecondColor;

                switch (key)
                {
                    case Key.LeftCtrl:
                    case Key.RightCtrl:
                    case Key.LWin:
                    case Key.RWin:
                    case Key.LeftAlt:
                    case Key.RightAlt:
                    case Key.Back:
                    case Key.Tab:
                    case Key.Enter:
                        return;

                    case Key.LeftShift:
                    case Key.RightShift:
                    case Key.CapsLock:
                        UpdateKeyboard();
                        return;

                    case Key.Space:
                        Type(' ');
                        return;

                    default:
                        Type(char.Parse(_keyboardButtons[key].Content.Text));
                        break;
                }

                return;
            }
        }
        public ICommand KeyUpCommand { get; private set; }
        private void ExecuteKeyUp(object? obj)
        {
            if (obj is KeyEventArgs e && _keyboardButtons.ContainsKey(e.Key))
            {
                _keyboardButtons[e.Key].KeyGrid.Background = Brushes.Transparent;
                UpdateKeyboard();
            }
        }

private bool isTextWidthExceededViewport = false;
private void Type(char character)
        {
            if (_textBox.Text.Length < _textBlock.Text.Length)
            {
                char expectedCharacter = _textBlock.Text[_textBox.Text.Length];

                if (character == expectedCharacter)
                {
                    _textBox.Text += character;

                    correctlyTypedTextLength++;
                    _progressBar.Value++;


                    _textBox.Foreground = (correctlyTypedTextLength == _textBox.Text.Length)
                        ? new SolidColorBrush(Colors.Black)
                        : new SolidColorBrush(Colors.Red);

                    _textBox.Select(0, correctlyTypedTextLength);

                    var typeface = new Typeface(_textBox.FontFamily, _textBox.FontStyle, _textBox.FontWeight, _textBox.FontStretch);
                    var formattedText = new FormattedText(_textBox.Text, Thread.CurrentThread.CurrentCulture, _textBox.FlowDirection, typeface, _textBox.FontSize, _textBox.Foreground);
                    var size = new Size(formattedText.Width, formattedText.Height);
                    if (size.Width + 20 > _scrollViewer.ViewportWidth && !isTextWidthExceededViewport)
                    {
                        _scrollViewer.ScrollToRightEnd();
                        isTextWidthExceededViewport = true;
                    }
                }
                else
                {
                    Fails++;
                    _textBox.Foreground = Brushes.Red;

                    if (Properties.Settings.Default.ErrorSound)
                        System.Media.SystemSounds.Exclamation.Play();
                }
            }
        }

我也try 过使用其他命令和自定义文本框元素来实现自动滚动,但没有成功.

总而言之,我的WPF应用程序需要一个文本框来自动滚动,以便当用户输入的内容超过文本框的可见宽度时,文本框左侧仍显示最后输入的字符.

如果有任何关于如何实现这种自动滚动行为的见解或解决方案,我将不胜感激.谢谢!

推荐答案

您可以执行以下操作;当文本接近末尾时,它将滚动到接近开头

var typeface = new Typeface(_textBox.FontFamily, _textBox.FontStyle, _textBox.FontWeight, _textBox.FontStretch);
var formattedText = new FormattedText(_textBox.Text, Thread.CurrentThread.CurrentCulture, _textBox.FlowDirection, typeface, _textBox.FontSize, _textBox.Foreground);
var size = new Size(formattedText.Width, formattedText.Height);

if (size.Width - scroll.HorizontalOffset >= (scroll.ViewportWidth - 10))
{
    scroll.ScrollToHorizontalOffset(scroll.HorizontalOffset + scroll.ViewportWidth -10);
}

要滚动到末尾,您可以执行scroll.ScrollToRightEnd();

或者,要使键入的文本始终位于文本框的中心,您可以执行以下操作

var typeface = new Typeface(tbTypedText.FontFamily, tbTypedText.FontStyle, tbTypedText.FontWeight, tbTypedText.FontStretch);
var formattedText = new FormattedText(tbTypedText.Text, Thread.CurrentThread.CurrentCulture, tbTypedText.FlowDirection, typeface, tbTypedText.FontSize, tbTypedText.Foreground);
var size = new Size(formattedText.Width, formattedText.Height);
scroll.ScrollToHorizontalOffset(size.Width > 6 ? size.Width -100 : size.Width);

就像这样

private void Type(char character)
    {
        if (_textBox.Text.Length < _textBlock.Text.Length)
        {
            char expectedCharacter = _textBlock.Text[_textBox.Text.Length];

            if (character == expectedCharacter)
            {
                _textBox.Text += character;

                correctlyTypedTextLength++;
                _progressBar.Value++;


                _textBox.Foreground = (correctlyTypedTextLength == _textBox.Text.Length)
                    ? new SolidColorBrush(Colors.Black)
                    : new SolidColorBrush(Colors.Red);

                _textBox.Select(0, correctlyTypedTextLength);
                var typeface = new Typeface(_textBox.FontFamily, _textBox.FontStyle, _textBox.FontWeight, _textBox.FontStretch);
                var formattedText = new FormattedText(_textBox.Text, Thread.CurrentThread.CurrentCulture, _textBox.FlowDirection, typeface, _textBox.FontSize, _textBox.Foreground);
                var size = new Size(formattedText.Width, formattedText.Height);

                if (size.Width - scroll.HorizontalOffset >= (scroll.ViewportWidth - 10))
                {
                    scroll.ScrollToHorizontalOffset(scroll.HorizontalOffset + scroll.ViewportWidth -10);
                }
            }
            else
            {
                Fails++;
                _textBox.Foreground = Brushes.Red;

                if (Properties.Settings.Default.ErrorSound)
                    System.Media.SystemSounds.Exclamation.Play();
            }
        }
    }

Csharp相关问答推荐

C#中的多yield 机制

在C# 11之前, struct 中的每个字段都必须显式分配?不能繁殖

使用客户端密钥为Fabric Rest API生成令牌

System.Text.Json数据化的C#类映射

为什么SignalR在每个Blazor服务器应用程序启动时最多启动8个服务器?

Razor视图Razor页面指向同一端点时的优先级

在允许溢出的情况下将小数转换为长

如何解决提交按钮后 Select 选项错误空参照异常

StackExchange.Redis.RedisServerException:调用ITransaction.ExecuteAsync()时出现错误未知命令取消监视

如何让NLog停止写入冗余信息?

JsonSchema.Net删除假阳性判断结果

Polly重试URL复制值

我什么时候应该在Dapper中使用Connection.OpenAsync?

如何返回具有泛型的类?

.Net MAUI,在将FlyoutPage添加到容器之前,必须设置添加构造函数导致Flyout和Detail"

将C#类导入到PowerShell

将字符串类型日期输入(yyyy-mm-ddthh:mm:ss)转换为MM/dd/yyyy格式

在.NET Maui中,Flyoutindow/Hamburger菜单可以在shell 之外实现吗?

Cmd中的&ping.end()";有时会失败,而";ping";总是有效

C#中的逻辑运算符用作单词';is';and';and';