C# 从不同线程更改WPF RichTextBox中文本的颜色
这可能是一个非常简单的问题,如果有重复的问题,请提前道歉,但我还没有找到关于这个具体问题的任何信息 我对RichTextBox中来自不同线程的文本的特定部分着色有问题。我想在RichTextBox中添加一行文本,同时用指定的颜色为该行着色。我能够附加文本并给它上色,但无法从另一个线程执行此操作,尽管我能够从Dispatcher附加纯文本C# 从不同线程更改WPF RichTextBox中文本的颜色,c#,wpf,richtextbox,dispatcher,C#,Wpf,Richtextbox,Dispatcher,这可能是一个非常简单的问题,如果有重复的问题,请提前道歉,但我还没有找到关于这个具体问题的任何信息 我对RichTextBox中来自不同线程的文本的特定部分着色有问题。我想在RichTextBox中添加一行文本,同时用指定的颜色为该行着色。我能够附加文本并给它上色,但无法从另一个线程执行此操作,尽管我能够从Dispatcher附加纯文本 private void UpdateTextbox(string message, Color color) { rtbOutput.Dispatch
private void UpdateTextbox(string message, Color color)
{
rtbOutput.Dispatcher.Invoke((Action)(() =>
{
TextRange range = new TextRange(rtbOutput.Document.ContentEnd, rtbOutput.Document.ContentEnd);
range.Text = message;
range.ApplyPropertyValue(TextElement.ForegroundProperty, new SolidColorBrush((Color)color));
})
);
}
这是我的密码:
private void UpdateTextbox(string message, Brush color)
{
rtbOutput.Dispatcher.Invoke((Action)(() =>
{
rtbOutput.Selection.Select(rtbOutput.Document.ContentEnd, rtbOutput.Document.ContentEnd);
rtbOutput.Selection.Text = message;
rtbOutput.Selection.ApplyPropertyValue(TextElement.ForegroundProperty, color);
})
);
}
当我尝试从另一个线程运行它时,会收到一条错误消息,告诉我无法访问该对象,因为它属于另一个线程。我如何解决这个问题
编辑:似乎RichTextBox不是问题所在,但Brush对象是问题所在,因为如果在Invoke方法中指定颜色,我可以很好地更改颜色,但如果我将其作为参数传递,则无法更改颜色。您需要将实际更改工作推回到GUI线程。在应用程序启动期间,您可以运行:
SynchronizationContext ctxt=SynchronizationContext.Current然后将它传递给多线程代码可以到达的地方。然后使用它:
//Code that determines the color to use
Color clr = result;
ctxt.Send((clr_obj) =>
{
//Code to set color
}, clr);
编辑:
下面是一篇很好的文章,它更深入地解释了SyncronizationContext
:使用Dispatcher是正确的,但并不总是需要调用Invoke。我使用以下扩展方法来处理这类事情:
public static void InvokeIfNeeded(this Dispatcher dispatcher, Action action)
{
if (dispatcher == null || dispatcher.CheckAccess())
action();
else
dispatcher.Invoke(action, DispatcherPriority.Normal);
}
您将按如下方式使用它:
private void UpdateTextbox(string message, Brush Color)
{
rtbOutput.Dispatcher.InvokeIfNeeded(() =>
{
rtbOutput.Selection.Select(rtbOutput.Document.ContentEnd, rtbOutput.Document.ContentEnd);
rtbOutput.Selection.Text = message;
rtbOutput.Selection.ApplyPropertyValue(TextElement.ForegroundProperty, Color);
}
);
}
事实证明,另一个线程拥有的不是RichTextBox,而是Brush对象。如果我只是将一个Color对象传递给该方法,并将其转换为Dispatcher中的SolidColorBrush,我就设法让它与下面的所有解决方案一起工作
private void UpdateTextbox(string message, Color color)
{
rtbOutput.Dispatcher.Invoke((Action)(() =>
{
TextRange range = new TextRange(rtbOutput.Document.ContentEnd, rtbOutput.Document.ContentEnd);
range.Text = message;
range.ApplyPropertyValue(TextElement.ForegroundProperty, new SolidColorBrush((Color)color));
})
);
}
它也适用于pquest建议的代码:
private void UpdateTextbox(string message, Brush color)
{
uiContext.Send(o =>
{
TextRange range = new TextRange(rtbOutput.Document.ContentEnd, rtbOutput.Document.ContentEnd);
range.Text = message;
range.ApplyPropertyValue(TextElement.ForegroundProperty, color);
}, null);
}
UI对象只能由UI线程更新。如果您正在使用任务,则可以使用task.ContinueWith(()=>{//code here},TaskScheduler.FromCurrentSynchronizationContext())代码>这很有用,但它不会改变我的问题,即无法从Dispatcher中访问ApplyPropertyValue函数。如果您在UI线程上创建rtbOutput,这应该对您有效。您是对的,您的方法有效。这并没有解决我的问题,因为我在错误的地方寻找解决方案。这看起来应该是可行的,但我在ApplyPropertyValue函数上得到了相同的错误消息。我使用的SynchronizationContext如下:uiContext.Send(newsendorPostCallback(委托(对象状态){//operations on Richtextbox}),null);我应该用不同的方式吗?在这一点上你不需要使用委托。您可以正常地调用这些方法。把所有的东西都放在你的调度器里。另外,我刚刚用CodePlex的一篇关于类的文章编辑了我的答案,现在它开始工作了。结果我的代码有一个不同于我想象的问题。但是你的答案也很有效,谢谢你的文章,它非常有趣。