简体   繁体   English

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

[英]Insert text into a WPF textblock from the top

i have a textblock that displays multiline messages that are received continuously from the.network along with the time it was received.我有一个文本块,显示从 .network 连续接收的多行消息以及接收时间。 here is the code:这是代码:

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);
            
    }

Here is how it is displayed in the app: enter image description here以下是它在应用程序中的显示方式:在此处输入图片描述

By default the new message received is inserted in the textblock after the old message.默认情况下,收到的新消息插入到旧消息之后的文本块中。 What i want to do is change it.我想做的是改变它。 The new message should be inserted from the top which means that when reading the content of the textblock you always start with the newest message.新消息应该从顶部插入,这意味着在阅读文本块的内容时,您总是从最新的消息开始。

Any idea on how i can achieve that?关于如何实现这一目标的任何想法?

Thanks.谢谢。

Ps: i'm not using MVVM Ps:我没有使用MVVM

I put a TextBlock and a button on a WPF window as test.我在 WPF window 上放了一个 TextBlock 和一个按钮作为测试。 And this works:这有效:

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");
    }
}

Don't use a TextBlock for this task.不要为此任务使用TextBlock The TextBlock is optimized to display only a few lines at max. TextBlock经过优化,最多只显示几行。 It is not designed to display multiline documents.它不是为显示多行文档而设计的。

The recommended solution is to use a ListBox .推荐的解决方案是使用ListBox It offers virtualization to provide a smooth scrolling experience.它提供虚拟化以提供流畅的滚动体验。
You can override the default template of the ListBoxItem to remove the mouse over and highlight effects.您可以覆盖ListBoxItem的默认模板以移除鼠标悬停和突出显示效果。 This way the ListBox will look like a plain text document or a common console output.这样ListBox将看起来像纯文本文档或通用控制台 output。
Don't use the Dispatcher in this case too.在这种情况下也不要使用Dispatcher If you need to update the ObservableCollection , which is the data source for the ListBox , from a background thread, use the BindingOperations.EnableCollectionSynchronization .如果您需要从后台线程更新作为ListBox的数据源的ObservableCollection ,请使用BindingOperations.EnableCollectionSynchronization

MessageLine.cs消息行.cs

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}.";
}

MainWindow.xaml.cs MainWindow.xaml.cs

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}");
    }
  }
}

MainWindow.xaml主窗口.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>

You can also move the ListBox to a UserControl or custom Control .您还可以将ListBox移动到UserControl或自定义Control

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM