【问题标题】:Efficient alternative to a read-only TextBox as a log in WPF作为 WPF 中日志的只读文本框的有效替代方案
【发布时间】:2017-11-20 21:15:50
【问题描述】:

我的应用程序具有数据绑定只读TextBox 作为捕获和显示其“记录”活动的手段。每次要记录某些内容时,都会将其连接到绑定字符串。这对于有限数量的记录文本来说足够好,但是随着文本数量的增加,它(可以理解)陷入困境。我在之前的问题中看到了建议:Efficient live log-viewer in WPFWhat is a fast way to render a log view in WPF? 使用 ListBox。我可以这样做,但我会失去一个很好的功能——允许用户选择和复制任意文本块。有没有其他解决办法?

【问题讨论】:

  • 您应该限制要在日志中保留多少文本,并在应用程序运行时开始删除它们。应将所有日志历史记录写入文本文件以供用户复制
  • 在什么时候它陷入困境(在多少行上)?有什么要求(显示的最大行数)?
  • @Evk 有点主观,但随着字符串长度达到大约 100k 左右(不确定有多少行,可能只有 1000 左右),它开始变得明显。如果它仍然可以处理几兆 - 10,000 - 20,000 行,那就太好了。

标签: c# wpf logging


【解决方案1】:

您可以使用Listbox 并允许用户使用ItemTemplate 复制部分日志:

<ListBox Name="viewList">
    <ListBox.ItemTemplate>
        <DataTemplate>
            <TextBox Text="{Binding Mode=OneWay}" IsReadOnly="True"/>
        </DataTemplate>
    </ListBox.ItemTemplate>
</ListBox>

然后用一些 ObservableCollection 填充它:

ObservableCollection<string> mvList = new ObservableCollection<string>();
viewList.ItemsSource = mvList;

很高兴知道:ListBox 自动实现了一些虚拟化,以确保在非常长的列表中具有良好的性能。 Here for more details

【讨论】:

  • 我已经在我引用的答案中看到了解决方案,但是我的目标是让日志可以任意选择,就好像它是一大块文本一样.当然,您可以进行多行选择,但这与我的目标是完全不同的用户体验。
  • 从这里添加一个自动滚动底部,也很有帮助 - stackoverflow.com/a/28433208/353147 这里有一个 DoEvents - stackoverflow.com/a/11899439/353147
【解决方案2】:

问题简单解决了……

我的新用户控件的 XAML:

<UserControl x:Class="DbHelper.Controls.LogControl"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             mc:Ignorable="d" 
             d:DesignHeight="300" d:DesignWidth="300">
    <TextBox x:Name="TextBox" FontFamily="Consolas" FontSize="12" IsReadOnly="True" VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Disabled" TextWrapping="Wrap"></TextBox>
</UserControl>

...及其代码隐藏:

public partial class LogControl : UserControl
{
    private readonly Queue<string> _logQueue = new Queue<string>();
    private Timer _timer;
    private bool _synced;

    public int MaxLines { get; set; } = 1000;

    public LogControl()
    {
        _timer = new Timer(state => ((LogControl)state).Refresh(), this, 1000, 1000);
        InitializeComponent();
    }

    private void Refresh()
    {
        lock (_logQueue)
        {
            if (!_synced)
            {
                var sb = new StringBuilder();
                foreach (var line in _logQueue)
                {
                    sb.AppendLine(line);
                }

                Dispatcher.Invoke(() =>
                {
                    TextBox.Text = sb.ToString();
                    TextBox.ScrollToEnd();
                });
                _synced = true;
            }
        }
    }

    public void Log(string str)
    {
        lock (_logQueue)
        {
            _logQueue.Enqueue(str);
            while (_logQueue.Count > MaxLines)
            {
                _logQueue.Dequeue();
            }
            _synced = false;
        }
    }

    public void Clear()
    {
        lock (_logQueue)
        {
            _logQueue.Clear();
            _synced = false;
        }
    }
}

【讨论】:

    猜你喜欢
    • 2010-10-09
    • 2011-09-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-10-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多