C# 作为WPF中日志的只读文本框的有效替代方案
我的应用程序以数据绑定的只读C# 作为WPF中日志的只读文本框的有效替代方案,c#,wpf,logging,C#,Wpf,Logging,我的应用程序以数据绑定的只读文本框为特色,以捕获和显示其“记录”的活动。每次记录某个内容时,都会将其连接到绑定字符串。这对于有限数量的日志文本来说已经足够好了,但随着文本数量的增加,它(可以理解)会陷入停滞。我在前面的问题中看到了这个建议:以及使用列表框的建议。我可以这样做,但我会失去一个很好的功能——允许用户选择和复制任意文本块。是否有其他解决方案?您可以使用列表框,并允许用户使用一些项目模板复制部分日志: <ListBox Name="viewList"> <Lis
文本框
为特色,以捕获和显示其“记录”的活动。每次记录某个内容时,都会将其连接到绑定字符串。这对于有限数量的日志文本来说已经足够好了,但随着文本数量的增加,它(可以理解)会陷入停滞。我在前面的问题中看到了这个建议:以及使用列表框的建议。我可以这样做,但我会失去一个很好的功能——允许用户选择和复制任意文本块。是否有其他解决方案?您可以使用列表框
,并允许用户使用一些项目模板
复制部分日志:
<ListBox Name="viewList">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBox Text="{Binding Mode=OneWay}" IsReadOnly="True"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
然后用一些可观察的集合填充它:
ObservableCollection<string> mvList = new ObservableCollection<string>();
viewList.ItemsSource = mvList;
ObservableCollection mvList=新的ObservableCollection();
viewList.ItemsSource=mvList;
好消息:列表框
会自动实现一些虚拟化,以确保非常长的列表具有良好的性能 您可以使用列表框
,并允许用户使用一些项目模板
复制部分日志:
<ListBox Name="viewList">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBox Text="{Binding Mode=OneWay}" IsReadOnly="True"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
然后用一些可观察的集合填充它:
ObservableCollection<string> mvList = new ObservableCollection<string>();
viewList.ItemsSource = mvList;
ObservableCollection mvList=新的ObservableCollection();
viewList.ItemsSource=mvList;
好消息:列表框会自动实现一些虚拟化,以确保非常长的列表具有良好的性能 问题简单地解决了 我的新用户控件的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;
}
}
}
公共部分类LogControl:UserControl
{
私有只读队列_logQueue=新队列();
私人定时器;
已同步私人布尔;
公共整数MaxLines{get;set;}=1000;
公共后勤管制()
{
_timer=newtimer(state=>((LogControl)state).Refresh(),this,10001000);
初始化组件();
}
私有无效刷新()
{
锁定(_logQueue)
{
如果(!\u已同步)
{
var sb=新的StringBuilder();
foreach(日志队列中的var行)
{
给某人加上一行(一行);
}
Dispatcher.Invoke(()=>
{
TextBox.Text=sb.ToString();
TextBox.ScrollToEnd();
});
_synced=true;
}
}
}
公共无效日志(字符串str)
{
锁定(_logQueue)
{
_logQueue.Enqueue(str);
而(_logQueue.Count>MaxLines)
{
_logQueue.Dequeue();
}
_同步=假;
}
}
公共空间清除()
{
锁定(_logQueue)
{
_logQueue.Clear();
_同步=假;
}
}
}
问题已简单解决
我的新用户控件的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;
}
}
}
公共部分类LogControl:UserControl
{
私有只读队列_logQueue=新队列();
私人定时器;
已同步私人布尔;
公共整数MaxLines{get;set;}=1000;
公共后勤管制()
{
_timer=newtimer(state=>((LogControl)state).Refresh(),this,10001000);
初始化组件();
}
私有无效刷新()
{
锁定(_logQueue)
{
如果(!\u已同步)
{
var sb=新的StringBuilder();
foreach(日志队列中的var行)
{
给某人加上一行(一行);
}
Dispatcher.Invoke(()=>
{
TextBox.Text=sb.ToString();
TextBox.ScrollToEnd();
});
_synced=true;
}
}
}
公共无效日志(字符串str)
{
锁定(_logQueue)
{
_logQueue.Enqueue(str);
而(_logQueue.Count>MaxLines)
{
_logQueue.Dequeue();
}
_同步=假;
}
}
公共空间清除()
{
锁定(_logQueue)
{
_logQueue.Clear();
_同步=假;
}
}
}
您应该设置日志中要保留多少文本的限制,并在应用程序运行时开始删除这些文本。所有日志历史记录都应该写入一个文本文件,以便用户复制它在什么时候停止(在多少行上)?要求是什么(要显示的最大行数)?@Evk有点主观,但随着字符串长度达到大约100k(不确定有多少行,可能只有1000行左右),它开始变得明显。如果它仍然可以使用几行megs-10000-20000行,那就太好了。您应该设置日志中要保留的文本数量的限制,并在应用程序运行时开始删除它们。所有日志历史记录都应该写入一个文本文件,以便用户复制它在什么时候停止(在多少行上)?要求是什么(要显示的最大行数)?@Evk有点主观,但随着字符串长度达到大约100k(不确定有多少行,可能只有1000行左右),它开始变得明显。如果它仍然可以处理几个MEG-10000-20000行,那就太好了。我在我引用的答案中看到了解决方案,但它没有做到我的目标是让日志可以任意选择,就像它是一个大文本块一样。当然,你可以进行多行选择,但这与我的目标是完全不同的用户体验。我在参考答案中看到了解决方案,但它没有做到我的目标是让日志可以任意选择,就像它是一个大文本块一样。当然,你可以进行多行选择,但这与我的目标是完全不同的用户体验。