我最近将我的应用程序从WinForms转换为WPF,我对大多数新功能感到满意。然而,我遇到了一个重大绊脚石。当我不断地将文字附加到我的文本框时,UI线程变得如此封闭,以至于我无法做任何事情,只能看它附加文字!我需要能够在我的选项卡控件,单击按钮等等中切换选项卡。奇怪的是,我在WinForms的UI线程中绝对没有减速!
所以,这里有一个关于我的应用程序的小背景:它运行其他进程作为"动作队列"的一部分,并将这些进程的stdout和stderr吐出到两个单独的文本框中,以及日志文本框(这些是受影响的文本框)。在低输出进程中,没有减速,但是当我使用SVN checkout和文件复制等进程时,我会立即获得大量文本输出,它只能附加文本。
这是我的打印代码:
public void PrintOutput(String s)
{
String text = s + Environment.NewLine;
Window.Dispatcher.Invoke(new StringArgDelegate(Window.PrintOutput), text);
Debug.Log("d " + text);
}
public void PrintLog(String s)
{
ClearLogButtonEnabled = true;
String text = s + Environment.NewLine;
Window.Dispatcher.Invoke(new StringArgDelegate(Window.PrintLog), text);
}
和匹配的代码隐藏:
public void PrintOutput(String s)
{
outputTextBox.AppendText(s);
outputTextBox.ScrollToEnd();
if (!clearOutputButton.IsEnabled) clearOutputButton.IsEnabled = true;
}
public void PrintLog(String s)
{
logTextBox.AppendText(s);
logTextBox.ScrollToEnd();
}
就这样,我没有得到一堆指控说我也在UI线程上做我的工作,这是我启动单独的代码工作线程:
Thread actionThread = new Thread(new ThreadStart(ActionManager.Instance().ExecuteActions));
actionThread.Name = "Action Manager Work Thread";
actionThread.Start();
这是处理所有辅助进程的启动,运行和清理的线程。这些进程使用上面显示的打印方法来打印它们的stdout / stderr输出。此外,每个进程都有自己的线程!
Thread procThread = new Thread(new ThreadStart(StartProcess));
procThread.Name = action.Name + "_" + Guid.NewGuid();
procThread.Start();
我担心的是WPF在某种程度上对Invokes的速度较慢而且我搞砸了。我花了很多精力将这个应用程序从WinForms切换到WPF,所以如果有人知道为什么我的打印速度会大幅下降,请告诉我!
编辑:
我还应该补充一点,我使用的是RichTextBox
,而不是TextBox
,并且我需要RichTextBox
的功能来加粗某些文字。如果有办法提供粗体文本与不太繁琐的TextBox类请告诉我。
答案 0 :(得分:5)
WPF RichTextBox
是一个非常重量级的UI元素,因为它不仅允许WPF Document形式的丰富内容,而且还具有编辑功能。
在这种情况下,您真正需要的是FlowDocumentScrollViewer。
这是我Log Viewer Sample的一个小改编版,它使用FlowDocumentScrollViewer
代替ItemsControl
。优点是此UI元素允许文本选择和复制,同时保留您需要的富文本功能:
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<FlowDocumentScrollViewer Document="{Binding}"/>
</Window>
代码背后:
public partial class MainWindow : Window
{
private System.Random random;
private string TestData = "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum";
private List<string> words;
private int maxword;
private int index;
private FlowDocument doc;
private Paragraph paragraph;
public MainWindow()
{
InitializeComponent();
DataContext = doc = new FlowDocument();
doc.Blocks.Add(paragraph = new Paragraph());
Task.Factory.StartNew(AddDataLoop);
}
private void AddDataLoop()
{
random = new Random();
words = TestData.Split(' ').ToList();
maxword = words.Count - 1;
while (true)
{
Thread.Sleep(10);
Dispatcher.BeginInvoke((Action) (AddRandomEntry));
}
}
private void AddRandomEntry()
{
var run = new Run(string.Join(" ", Enumerable.Range(5, random.Next(10, 50))
.Select(x => words[random.Next(0, maxword)])));
var isBold = random.Next(1, 10) > 5;
if (isBold)
paragraph.Inlines.Add(new Bold(run));
else
paragraph.Inlines.Add(run);
paragraph.Inlines.Add(new LineBreak());
}
}
结果:
再一次,这证明了你可以用WPF无法实现的绝对nothing
,而显然不能说相反。这使得winforms成为一种几乎过时的技术,通过更新,更强大的技术实际上replaced
。
请注意,我在每个新条目之间放置了 10毫秒延迟。这实际上是实时的,并且UI不显示任何减速或闪烁或任何类型的任何质量下降。
与我的另一个例子一样,请注意大多数背后的代码都是用于生成随机文本的样板文件,唯一相关的代码行是paragraph.Inlines.Add(...)
。
WPF Rocks。 - 只需将我的代码复制并粘贴到File -> New Project -> WPF Application
中,然后自行查看结果。
如果您需要进一步的帮助,请与我们联系。