从不同的线程更改WPF RichTextBox中的文本颜色

时间:2014-11-25 19:25:55

标签: c# wpf richtextbox dispatcher

这可能是一个非常简单的问题,如果有重复,请提前抱歉,但我还没有找到有关此特定问题的任何内容。

我在从不同的线程中对RichTextBox中文本的特定部分进行着色时遇到问题。我想在RichTextBox中附加一行文本,同时用指定颜色为该行着色。我能够附加文本和颜色,但是从其他线程无法这样做,尽管我能够从Dispatcher附加纯文本。

这是我的代码:

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方法中指定它,我可以更改颜色,但如果我将它作为参数传递则不能。

3 个答案:

答案 0 :(得分:1)

事实证明,不是另一个线程拥有的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);
}

答案 1 :(得分:0)

您需要将实际的更改工作重新推送到GUI线程。在启动应用程序期间,您可以运行:

SynchronizationContext ctxt = SynchronizationContext.Current;然后将你的多线程代码传递给它。然后使用它:

//Code that determines the color to use

Color clr = result;

ctxt.Send((clr_obj) =>
{
//Code to set color
}, clr);

编辑:

这是一篇很好的文章,更深入地解释了SyncronizationContextArticle

答案 2 :(得分:0)

您使用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);
        }
    );
}
相关问题