代码之家  ›  专栏  ›  技术社区  ›  hassan Ma

从顶部将文本插入WPF文本块

  •  0
  • hassan Ma  · 技术社区  · 2 年前

    我有一个文本块,显示连续从网络接收的多行消息以及接收时间。以下是代码:

    private async Task ReadMessage(TcpClient client, bool ownsClient)
        {
            
                using NetworkStream stream = client.GetStream();
    
                byte[] buffer = new byte[4096];
    
                int bytesRead = await stream.ReadAsync(buffer, 0, buffer.Length);
    
                string message = Encoding.UTF8.GetString(buffer, 0, bytesRead);
                
                DateTime now = DateTime.Now;
                string receptiontime = now.ToString("HH:mm");
                Dispatcher.Invoke(new Action(() =>
                {
                    NotamsTextBlock.Text += "-->" + receptiontime + Environment.NewLine;
                    NotamsTextBlock.Text += message;
                   
                }), DispatcherPriority.Background);
                
        }
    

    以下是它在应用程序中的显示方式: enter image description here

    默认情况下,收到的新消息会插入到旧消息后的文本块中。我想做的是改变它。新消息应该从顶部插入,这意味着在阅读文本块的内容时,你总是从最新消息开始。

    你知道我怎样才能做到吗?

    谢谢

    Ps:我没有使用MVVM

    0 回复  |  直到 2 年前
        1
  •  0
  •   Kelly    2 年前

    我在WPF窗口上放了一个文本块和一个按钮作为测试。这是有效的:

    using System.Windows;
    
    namespace WpfApp7;
    
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
    
            myTextBlock.Text = "Before";
        }
    
        private void ButtonBase_OnClick(object sender, RoutedEventArgs e)
        {
            myTextBlock.Text = myTextBlock.Text.Insert(0, "after");
        }
    }
    
        2
  •  0
  •   BionicCode    2 年前

    不要使用 TextBlock 完成这项任务。这个 控件 优化为最多只显示几行。它不是为显示多行文档而设计的。

    建议的解决方案是使用 ListBox .它提供了虚拟化,以提供平滑的滚动体验。
    您可以覆盖 ListBoxItem 移除鼠标并突出显示效果。这边 列表框 将看起来像纯文本文档或公共控制台输出。
    不要使用 Dispatcher 在这种情况下也是如此。如果你需要更新 ObservableCollection ,这是 列表框 ,在后台线程中,使用 BindingOperations.EnableCollectionSynchronization .

    留言。反恐精英

    public class MessageLine : INotifyPropertyChanged
    {
      public MessageLine(string message) => this.Message = message;
    
      public string Message { get; }
    
      public event PropertyChangedEventHandler PropertyChanged;}
    
      public override string ToString()
        => $"Formatted message content: {this.Message}.";
    }
    

    主窗口。xaml。反恐精英

    partial class MainWindow : Window
    {
      public ObservableCollection<MessageLine> Messages { get; }
    
      publiv MainWindow()
      {
        InitializeComponent();
      
        this.Messages = new ObservableCollection<MessageLine>();
      }
    
      private void InsertLineAtBeginning(MessageLine message) => this.Messages.Insert(0, message);
    
      private void AppendLine(MessageLine message) => this.Messages.Add(message);
    
      private async Task ReadMessageAsync(TcpClient client, bool ownsClient)
      {
        await using NetworkStream stream = client.GetStream();
        
        ...
    
       // Since we insert at the beginning but want to have a proper line order of the multi-line message,
       // we must insert the last line first (reverse order - you can use a Stack to collect the lines)
    
        string messageBody = Encoding.UTF8.GetString(buffer, 0, bytesRead);
        InsertLineAtBeginning(new MessageLine(messageBody));   
    
        var messageHeader = $"-->{receptiontime}";
        InsertLineAtBeginning(new MessageLine(messageHeader));   
      }
    
      private async void SaveMessages_OnClick(object sender, EventArgs e)
      {
        await using var fileWriter = new StreamWriter("destination_file.txt", false);
        foreach (var message in this.Messages)
        {
          // Call ToString() for a defined string representation of the instance.
          // Requires to override ToString to get a useful value. 
          await fileWriter.WriteLineAsync(message.ToString());
    
          // Alternatively, format message inline
          await fileWriter.WriteLineAsync($"Formatted message content: {message.Message}");
        }
      }
    }
    

    主窗口。xaml

    <Window>
      <ListBox ItemsSource="{Binding RlativeSource={RelativeSource AncestorType Window}, Path=Messages}">
        <ListBox.ItemTemplate>
          <DataTemplate DataType="{x:Type local:MessageLine}">
            <TextBlock Text="{Binding Message}" />
          </DataTemplate>
        </ListBox.ItemTemplate>
        
        <!-- Remove interaction effects -->
        <ListBox.ItemContainerStyle>
          <Style TargetType="ListBoxItem">
            <Setter Property="Template">
              <Setter.Value>
                <ControlTemplate TargetType="ListBoxItem">
                  <ContenPresenter />
                </ControlTemplate>
              </Setter.Value>
            </Setter>
          </Style>
        </ListBox.ItemContainerStyle>
      </ListBox>
    </Window>
    

    您还可以移动 列表框 UserControl 还是习俗 Control .