RError.com

RError.com Logo RError.com Logo

RError.com Navigation

  • 主页

Mobile menu

Close
  • 主页
  • 系统&网络
    • 热门问题
    • 最新问题
    • 标签
  • Ubuntu
    • 热门问题
    • 最新问题
    • 标签
  • 帮助
主页 / 问题 / 1586291
Accepted
ikikaidesu
ikikaidesu
Asked:2024-07-06 03:38:52 +0000 UTC2024-07-06 03:38:52 +0000 UTC 2024-07-06 03:38:52 +0000 UTC

c# wpf 检查当前行的最后一个单词

  • 772

我正在创建一个粗略的页面来检查输入是否正确。关键是你输入文本,如果它与原始文本匹配,那么你的字符是白色的,否则是红色的。问题是我使用textblockc来显示文本textwrapping,如果单词不适合,则会将其转移到新行,但是当输入我的单词时,一半保留在同一行,一半保留在新行(由于wrappanel),并且我需要正确的传输,而发生这种情况是因为我以某种方式注意到该人输入了当前行的最后一个单词,然后会从文本中删除该行,但我不知道该怎么做,我附上问题的代码和截图如下:

截屏: 在此输入图像描述

正如你在这里看到的,这个词bloomed一半在上面,一点在下面。为了防止这种情况,我想以某种方式捕获一个人输入了当前行的最后一个单词(如示例中所示freshly)的事实,然后通过删除当前行并在集合中清除它来更新文本。

查看代码:

<Grid Background="#282a36">
    <TextBlock Text="{Binding OriginalText}"
               FontFamily="Consolas"
               Foreground="Gray" 
               FontSize="16" 
               IsHitTestVisible="False"
               TextWrapping="Wrap"/>
    <TextBox Text="{Binding UserInput, UpdateSourceTrigger=PropertyChanged}" 
             FontSize="16" 
             FontFamily="Consolas"
             Background="Transparent" 
             Foreground="Transparent" 
             BorderBrush="Transparent"
             TextWrapping="Wrap"/>
    <ItemsControl ItemsSource="{Binding ColoredUserInput}" VerticalAlignment="Top">
        <ItemsControl.ItemsPanel>
            <ItemsPanelTemplate>
                <WrapPanel Orientation="Horizontal"/>
            </ItemsPanelTemplate>
        </ItemsControl.ItemsPanel>
        <ItemsControl.ItemTemplate>
            <DataTemplate>
                <TextBlock Text="{Binding Character}" 
                           Foreground="{Binding Color}" 
                           FontSize="16"
                           FontFamily="Consolas"/>
            </DataTemplate>
        </ItemsControl.ItemTemplate>
    </ItemsControl>
</Grid>

视图模型代码:

public class MainViewModel : INotifyPropertyChanged
{
    private string _originalText = "The curious cat explored every corner of the garden, chasing butterflies and sniffing at freshly bloomed flowers. Meanwhile, the old oak tree stood tall, its branches swaying gently in the summer breeze. Birds chirped happily overhead, creating a symphony of natural sounds. The sun painted the sky in shades of orange and pink as evening approached.";
    private string _userInput;
    private ObservableCollection<ColoredCharacter> _coloredUserInput;

    public MainViewModel()
    {
        ColoredUserInput = new ObservableCollection<ColoredCharacter>();
    }

    public string OriginalText
    {
        get => _originalText;
        set
        {
            _originalText = value;
            OnPropertyChanged();
        }
    }

    public string UserInput
    {
        get => _userInput;
        set
        {
            _userInput = value;
            OnPropertyChanged();
            UpdateColoredUserInput();
        }
    }

    public ObservableCollection<ColoredCharacter> ColoredUserInput
    {
        get => _coloredUserInput;
        set
        {
            _coloredUserInput = value;
            OnPropertyChanged();
        }
    }

    private void UpdateColoredUserInput()
    {
        ColoredUserInput.Clear();
        for (int i = 0; i < OriginalText.Length; i++)
        {
            if (i < UserInput.Length)
            {
                var character = OriginalText[i];
                var color = UserInput[i] == character ? Brushes.White : Brushes.Red;
                ColoredUserInput.Add(new ColoredCharacter { Character = character.ToString(), Color = color });
            }
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;

    protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}

public class ColoredCharacter
{
    public string Character { get; set; }
    public Brush Color { get; set; }
}

因为我上面说过页面是原始的(它仅用于创建我将来要传输到项目的内容),那么这不是我在大多数情况下了解的纯代码,但如果这里有一些非常糟糕的东西,我会很高兴知道!

更新:根据我的想法,我自己将通过该方法在文本中创建连字符,并简单地UpdateColoredUserInput检查下一个字符是否存在并且等于\n,然后删除当前行,但是出现了新问题创建此方法是因为\n需要插入这些方法来检查它是否能按字符将新单词放入当前行(这里您需要弄清楚如何根据字体大小和宽度进行计算,textblock一般来说这听起来不像一个非常好的方法,但至少它看起来有点不像神奇地(或通过formattedtext)找出这个词是否适合的方法)。

c#
  • 2 2 个回答
  • 50 Views

2 个回答

  • Voted
  1. Best Answer
    EvgeniyZ
    2024-07-06T23:52:18Z2024-07-06T23:52:18Z

    让我向您展示我正在谈论的一个例子。这不是您问题的答案,而是一种替代方案。

    • 我们需要当前按下的按钮。在这里,您可以像以前一样通过文本字段进行操作,但这是一个会产生其他拐杖的拐杖,您只需要其中的当前按钮。我应该怎么办?有 2 个选项:

      1. 通过“互动”

        • 下载 NuGet 包Microsoft.Xaml.Behaviors.Wpf

        • 在 XAML 的开头包含它:xmlns:i="http://schemas.microsoft.com/xaml/behaviors"

        • 接下来,对所需的对象(例如,窗口)执行类似的操作:

          <i:Interaction.Triggers>
              <i:EventTrigger EventName="KeyDown">
                  <i:InvokeCommandAction Command="{Binding KeyDownCommand}" PassEventArgsToCommand="True" />
              </i:EventTrigger>
          </i:Interaction.Triggers>
          

          正如您所看到的,我们与特定事件绑定,当触发时将调用命令。好吧,因为我们需要这个命令的数据,所以我们EventArgs向它传递参数。命令本身将是标准的,参数是KeyEventArgs。这种方法的缺点是它不完全是 MVVM,因为KeyEventArgs即使Key这样也System.Windows.Input清楚地向我们暗示这是一个视图层。如果这对您来说并不重要,那么这个选择就相当不错了。 PS 该事件KeyDown不会给出本地化字母(这是您需要的),因此最好使用PreviewTextInput,它会立即给出当前布局的本地化文本。除了参数类型之外,绑定将类似。

      2. 通过接口

        MVVM 是将所有内容划分为松散连接的层,这反过来意味着我们可以与 View 进行通信,就像 View 可以与 VM 进行通信一样,但这必须通过抽象、接口小心地完成。

        • 我们创建一个接口来描述接收按下的按钮的逻辑,例如,如下所示:

          public interface IKeyHandle
          {
              void KeyDown(string key);
          }
          
        • 接下来,在窗口构造函数中,或者更好的是,在窗口事件中Loaded,我们检查“DataContext这是一个接口吗?”如果是,那么我们订阅所需的事件并将必要的数据传输到虚拟机。

          if (DataContext is IKeyHandle keyHandle)
          {
              PreviewTextInput += (_, args) => keyHandle.KeyDown(args.Text);
          }
          
        • 接下来,我们从这个接口继承VM类并实现它,这实际上是传输数据的另一种选择。

    有了按下的符号,我们就可以继续前进,即编写界面和基本逻辑。让我们从逻辑开始...

    • 我们需要每个字母的“状态”。让它成为“无状态”、“已选择”、“正确”、“无效”。

      public enum CharacterState
      {
          None,
          Selected,
          Correct,
          Incorrect
      }
      
    • 接下来,我们将为每个字母创建一个 VM,其中包含符号本身和状态。运行过程中状态发生变化,这意味着必须调用INPC。我将使用CommunityToolkit,你可以使用任何其他方法。

      public partial class CharacterViewModel(char character)  : ObservableObject
      {
          public char Character { get; } = character;
      
          [ObservableProperty]
          private CharacterState _state;
      }
      
    • 接下来,我们创建这些虚拟机的集合:

      public ObservableCollection<CharacterViewModel> Characters { get; }
      
    • 我们以任何方便的方式填充它,我将在构造函数中执行以下操作:

      Characters = new("Сьешь ещё этих мягких французких булок, да выпей чаю.".Select(x => new CharacterViewModel(x)));
      
    • 现在让我们创建一个选择下一个字符的方法,如下所示:

      private int _currentCharIndex;
      private CharacterViewModel? _currentCharacter;
      
      private void MoveSelection()
      {
          if (_currentCharacter is { State: CharacterState.Selected }) return;
      
          // TODO: Проверка индекса
          _currentCharacter = Characters[_currentCharIndex++];
          _currentCharacter.State = CharacterState.Selected;
      }
      
    • 我们需要在“测试开始”时调用此方法一次(如果您是这样做的话),或者像我一样,仅在构造函数中调用此方法。

    • 现在点击时调用的方法,我们在其中编写验证逻辑,同时我们移动到下一个符号:

      public void KeyDown(string key)
      {
          if (_currentCharacter is null || !char.TryParse(key, out var pressedChar)) return;
      
          _currentCharacter.State = pressedChar == _currentCharacter?.Character ? CharacterState.Correct : CharacterState.Incorrect;
      
          MoveSelection();
      }
      

    就是这样,我们已经完成了逻辑,现在是 UI,它将尽可能简单,即ItemsControl像您一样指定一个ItemsPanel,但是ItemTemplate将会Border突出显示TextBlock符号本身,还会有触发器来设置必要的颜色:

    <ItemsControl ItemsSource="{Binding Characters}">
        <ItemsControl.ItemsPanel>
            <ItemsPanelTemplate>
                <WrapPanel />
            </ItemsPanelTemplate>
        </ItemsControl.ItemsPanel>
        <ItemsControl.ItemTemplate>
            <DataTemplate>
                <Border x:Name="border" BorderThickness="0,0,0,1">
                    <TextBlock
                        x:Name="textBox"
                        FontSize="16"
                        Text="{Binding Character}" />
                </Border>
                <DataTemplate.Triggers>
                    <DataTrigger Binding="{Binding State}" Value="Selected">
                        <Setter TargetName="border" Property="BorderBrush" Value="Black" />
                    </DataTrigger>
                    <DataTrigger Binding="{Binding State}" Value="Incorrect">
                        <Setter TargetName="textBox" Property="Foreground" Value="Red" />
                    </DataTrigger>
                    <DataTrigger Binding="{Binding State}" Value="Correct">
                        <Setter TargetName="textBox" Property="Foreground" Value="Green" />
                    </DataTrigger>
                </DataTemplate.Triggers>
            </DataTemplate>
        </ItemsControl.ItemTemplate>
    </ItemsControl>
    

    以防万一,整个代码:

    
    public interface IKeyHandle
    {
        void KeyDown(string key);
    }
    
    public enum CharacterState
    {
        None,
        Selected,
        Correct,
        Incorrect
    }
    
    public partial class CharacterViewModel(char character) : ObservableObject
    {
        public char Character { get; } = character;
    
        [ObservableProperty]
        private CharacterState _state;
    }
    
    public partial class MainViewModel : ObservableObject, IKeyHandle
    {
        public ObservableCollection<CharacterViewModel> Characters { get; }
    
        public MainViewModel()
        {
            Characters = new("Сьешь ещё этих мягких французких булок, да выпей чаю.".Select(x => new CharacterViewModel(x)));
            MoveSelection();
        }
    
        private int _currentCharIndex;
        private CharacterViewModel? _currentCharacter;
    
        private void MoveSelection()
        {
            if (_currentCharacter is { State: CharacterState.Selected }) return;
    
            // TODO: Проверка индекса
            _currentCharacter = Characters[_currentCharIndex++];
            _currentCharacter.State = CharacterState.Selected;
        }
    
        public void KeyDown(string key)
        {
            if (_currentCharacter is null || !char.TryParse(key, out var pressedChar)) return;
    
            _currentCharacter.State = pressedChar == _currentCharacter?.Character ? CharacterState.Correct : CharacterState.Incorrect;
    
            MoveSelection();
        }
    
    }
    
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            DataContext = new MainViewModel();
    
            if (DataContext is IKeyHandle keyHandle)
            {
                PreviewTextInput += (_, args) => keyHandle.KeyDown(args.Text);
            }
        }
    }
    

    嗯,结果是:

    结果

    正如您所看到的,它相对简单,没有不必要的拐杖,一组简单的字母,您可以轻松缩放,更改字体等,即使您将其放在一列中,一切都会按其应有的方式工作和传输。

    • 1
  2. ikikaidesu
    2024-07-06T18:41:26Z2024-07-06T18:41:26Z

    我解决了这个问题,除此之外,正如我所写,EvgeniyZ我还有几个问题我也会解决,但稍后会解决。正如我所说,我创建了一种方法,当当前行的长度大于最大值时,通过创建人为中断来格式化行。每行的字符数。事实上,我的代码也不是很好,因为FormattedText我在分配ui值的地方,也就是说,我必须将它们存储在某个地方并将它们绑定到,textblock以便不存在差异。正如我已经说过的,问题之一是我将字体大小和文本块大小存储在视图模型中,但我真的不知道如何以其他方式做到这一点

    // метод для получения средней ширины символа в строке
    private double GetAverageCharacterWidth()
    {
        // создаем "виртуальный" текстбокс где подставляем данные как у нашего оригинального, это нужно чтобы получить среднюю ширину символа
        // так как у нас шрифт Consolas, то и средняя ширина у всех одинаковая
        var formattedText = new FormattedText(
            "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789",
            CultureInfo.CurrentCulture,
            FlowDirection.LeftToRight,
            new Typeface("Consolas"),
            _charSize,
            Brushes.Black,
            new NumberSubstitution(),
            1);
    
        return formattedText.Width / formattedText.Text.Length;
    }
    
    // этот метод определяет макс. кол-во допустимых символов в строке
    private int GetMaxCharsPerLine()
    {
        // получаем ширину символа
        double averageCharWidth = GetAverageCharacterWidth();
        // - 10 так как при оригинальном размере текст выходит за пределы буквально на символ
        return (int)((_textbLockWidth-10) / averageCharWidth);
    }
    // метод добавляющий переносы в строках
    private string AddLineBreaks(string text, int maxCharsPerLine)
    {
        // делаем из нашего текста массив для корректного обхода текста по словам
        var words = text.Split(' ');
        // создаем стрингбилдер
        var result = new StringBuilder();
        // создаем отчет для кол-ва символов в строке текущей
        var currentLineLength = 0;
    
        // обходим слова
        foreach (var word in words)
        {
            // если длина строки + длина слова + 1 больше макс. допущенного значения
            if (currentLineLength + word.Length + 1 > maxCharsPerLine)
            {
                // создаем перенос и обнуляем счетчик
                result.Append('\n');
                currentLineLength = 0;
            }
            // тут мы делаем проверку что слово в строке не первое и если это так добавляем пробел и увеличиваем счетчик
            else if (currentLineLength > 0)
            {
                result.Append(' ');
                currentLineLength++;
            }
            // добавляем слово 
            result.Append(word);
            // увеличиваем счетчик
            currentLineLength += word.Length;
        }
        // возвращаем отформатированную строку
        return result.ToString();
    }
    

    但这里是我在将 UpdateColoredUserInput 添加到集合后调用的方法,传递当前的 i,该方法检查字符是否为 \n,如果是,则删除当前行并清除用户输入。

    // метод проверки на новую строку
        private void CheckForNewLine(int LastCharIndex)
        {
            // если символ есть и он равен \n
            if (OriginalText.Length > LastCharIndex + 1 && OriginalText[LastCharIndex + 1] == '\n')
            {
                // пропускаем текущую строку чтобы удалить ее и создать перенос на новую
                OriginalText = new string(OriginalText.Skip(LastCharIndex + 2).ToArray());
                // очищаем пользовательский ввод
                UserInput = string.Empty;
            }
        }
    

    代码很可能并不完美,所以我希望您能提出改进它的想法。

    • 0

相关问题

  • 使用嵌套类导出 xml 文件

  • 分层数据模板 [WPF]

  • 如何在 WPF 中为 ListView 手动创建列?

  • 在 2D 空间中,Collider 2D 挂在玩家身上,它对敌人的重量相同,我需要它这样当它们碰撞时,它们不会飞向不同的方向。统一

  • 如何在 c# 中使用 python 神经网络来创建语音合成?

  • 如何知道类中的方法是否属于接口?

Sidebar

Stats

  • 问题 10021
  • Answers 30001
  • 最佳答案 8000
  • 用户 6900
  • 常问
  • 回答
  • Marko Smith

    我看不懂措辞

    • 1 个回答
  • Marko Smith

    请求的模块“del”不提供名为“default”的导出

    • 3 个回答
  • Marko Smith

    "!+tab" 在 HTML 的 vs 代码中不起作用

    • 5 个回答
  • Marko Smith

    我正在尝试解决“猜词”的问题。Python

    • 2 个回答
  • Marko Smith

    可以使用哪些命令将当前指针移动到指定的提交而不更改工作目录中的文件?

    • 1 个回答
  • Marko Smith

    Python解析野莓

    • 1 个回答
  • Marko Smith

    问题:“警告:检查最新版本的 pip 时出错。”

    • 2 个回答
  • Marko Smith

    帮助编写一个用值填充变量的循环。解决这个问题

    • 2 个回答
  • Marko Smith

    尽管依赖数组为空,但在渲染上调用了 2 次 useEffect

    • 2 个回答
  • Marko Smith

    数据不通过 Telegram.WebApp.sendData 发送

    • 1 个回答
  • Martin Hope
    Alexandr_TT 2020年新年大赛! 2020-12-20 18:20:21 +0000 UTC
  • Martin Hope
    Alexandr_TT 圣诞树动画 2020-12-23 00:38:08 +0000 UTC
  • Martin Hope
    Air 究竟是什么标识了网站访问者? 2020-11-03 15:49:20 +0000 UTC
  • Martin Hope
    Qwertiy 号码显示 9223372036854775807 2020-07-11 18:16:49 +0000 UTC
  • Martin Hope
    user216109 如何为黑客设下陷阱,或充分击退攻击? 2020-05-10 02:22:52 +0000 UTC
  • Martin Hope
    Qwertiy 并变成3个无穷大 2020-11-06 07:15:57 +0000 UTC
  • Martin Hope
    koks_rs 什么是样板代码? 2020-10-27 15:43:19 +0000 UTC
  • Martin Hope
    Sirop4ik 向 git 提交发布的正确方法是什么? 2020-10-05 00:02:00 +0000 UTC
  • Martin Hope
    faoxis 为什么在这么多示例中函数都称为 foo? 2020-08-15 04:42:49 +0000 UTC
  • Martin Hope
    Pavel Mayorov 如何从事件或回调函数中返回值?或者至少等他们完成。 2020-08-11 16:49:28 +0000 UTC

热门标签

javascript python java php c# c++ html android jquery mysql

Explore

  • 主页
  • 问题
    • 热门问题
    • 最新问题
  • 标签
  • 帮助

Footer

RError.com

关于我们

  • 关于我们
  • 联系我们

Legal Stuff

  • Privacy Policy

帮助

© 2023 RError.com All Rights Reserve   沪ICP备12040472号-5