C# STA线程、Rtf到HTML转换器问题

C# STA线程、Rtf到HTML转换器问题,c#,wpf,multithreading,mvvm,C#,Wpf,Multithreading,Mvvm,我正在使用尝试将我的Rtf文本从UI中的RichTextBox转换为Html。我的解决方案中有两个项目:用MVVM编写的UI和应用程序-IsesTextEditor和链接中提供的预写转换器-MarkupConverter my view中的btn绑定到my view模型中的命令,该命令将RichTextBox文本传递到ConvertRtfToHtml方法中,如示例所示: private string ConvertRtfToHtml(string PastedText) {

我正在使用尝试将我的Rtf文本从UI中的
RichTextBox
转换为Html。我的解决方案中有两个项目:用MVVM编写的UI和应用程序-
IsesTextEditor
和链接中提供的预写转换器-
MarkupConverter

my view中的btn绑定到my view模型中的命令,该命令将RichTextBox文本传递到ConvertRtfToHtml方法中,如示例所示:

 private string ConvertRtfToHtml(string PastedText)
    {
        var thread = new Thread(ConvertRtfInSTAThread);
        var threadData = new ConvertRtfThreadData { RtfText = PastedText };
        thread.SetApartmentState(ApartmentState.STA);
        thread.Start(threadData);
        thread.Join();
        return threadData.HtmlText;
    }

    private void ConvertRtfInSTAThread(object rtf)
    {
        var threadData = rtf as ConvertRtfThreadData;
        threadData.HtmlText = markupConverter.ConvertRtfToHtml(threadData.RtfText);
    }

    private class ConvertRtfThreadData
    {
        public string RtfText { get; set; }
        public string HtmlText { get; set; }
    }
然后,
MarkupConverter.ConvertRtfToHtml
方法调用
ConvertRtfToXaml
,该方法实例化一个新的
RichTextBox
对象:

    public static class RtfToHtmlConverter
    {
     private const string FlowDocumentFormat = "<FlowDocument>{0}</FlowDocument>";

     public static string ConvertRtfToHtml(string rtfText)
     {
        var xamlText = string.Format(FlowDocumentFormat, ConvertRtfToXaml(rtfText));

        return HtmlFromXamlConverter.ConvertXamlToHtml(xamlText, false);
    }

    private static string ConvertRtfToXaml(string rtfText)
    {
        var richTextBox = new RichTextBox();
        if (string.IsNullOrEmpty(rtfText)) return "";

        var textRange = new TextRange(richTextBox.Document.ContentStart, richTextBox.Document.ContentEnd);

        using (var rtfMemoryStream = new MemoryStream())
        {
            using (var rtfStreamWriter = new StreamWriter(rtfMemoryStream))
            {
                rtfStreamWriter.Write(rtfText);
                rtfStreamWriter.Flush();
                rtfMemoryStream.Seek(0, SeekOrigin.Begin);

                textRange.Load(rtfMemoryStream, DataFormats.Rtf);
            }
        }

        using (var rtfMemoryStream = new MemoryStream())
        {

            textRange = new TextRange(richTextBox.Document.ContentStart, richTextBox.Document.ContentEnd);
            textRange.Save(rtfMemoryStream, DataFormats.Xaml);
            rtfMemoryStream.Seek(0, SeekOrigin.Begin);
            using (var rtfStreamReader = new StreamReader(rtfMemoryStream))
            {
                return rtfStreamReader.ReadToEnd();
            }
        }

    }
}
公共静态类RtfToHtmlConverter
{
私有常量字符串FlowDocumentFormat=“{0}”;
公共静态字符串转换器RTFTOHTML(字符串rtfText)
{
var xamlText=string.Format(FlowDocumentFormat,ConvertRtfToXaml(rtfText));
返回HtmlFromXamlConverter.ConvertXamlToHtml(xamlText,false);
}
专用静态字符串转换器RTFTOXAML(字符串rtfText)
{
var richTextBox=新的richTextBox();
if(string.IsNullOrEmpty(rtfText))返回“”;
var textRange=new textRange(richTextBox.Document.ContentStart,richTextBox.Document.ContentEnd);
使用(var rtfMemoryStream=new MemoryStream())
{
使用(var rtfStreamWriter=newstreamwriter(rtfMemoryStream))
{
rtfStreamWriter.Write(rtfText);
rtfStreamWriter.Flush();
rtfMemoryStream.Seek(0,SeekOrigin.Begin);
加载(rtfMemoryStream,DataFormats.Rtf);
}
}
使用(var rtfMemoryStream=new MemoryStream())
{
textRange=新的textRange(richTextBox.Document.ContentStart,richTextBox.Document.ContentEnd);
保存(rtfMemoryStream,DataFormats.Xaml);
rtfMemoryStream.Seek(0,SeekOrigin.Begin);
使用(var rtfStreamReader=newstreamreader(rtfMemoryStream))
{
返回rtfStreamReader.ReadToEnd();
}
}
}
}
在创建
RichTextBox
对象时,我得到了一个
调用线程无法访问此对象,因为它是另一个线程拥有的。
异常


有人能建议解决这个问题吗?我确信这是一个相对简单的线程问题,但我是一个初级开发人员&在线程或STA方面没有太多经验…

例外情况是,将GUI相关代码从工作线程移动到GUI线程

请参考,我从中复制了一些文本:


与用户界面的框架一样,与许多Windows窗体一样,WPF也强制使用单个线程模型,这意味着您只能访问创建它的指定派生DispatcherObject线程。在实现ISynchronizeInvoke接口的Windows窗体控件中,此接口公开了一组方法,如Invoke和BeginInvoke,以强制执行协定公共线程同步,我们可以使用这些方法从另一个线程访问控件。在WPF中,我们也有这类操作,但是这些操作涉及到一个名为Dispatcher的类,Dispatcher WPF是允许这种线程同步模型的方法。

要在不是主UI线程的线程上使用WPF控件,您需要做一些准备,比如启动和完成WPF Dispatcher循环

我已经用我之前发布的一些帮助代码编写了一个示例应用程序,展示了如何做到这一点

这是一个控制台应用程序,不过您应该能够在任何其他执行环境中使用
RunOnWpfThreadAsync(()=>ConvertRtfToXaml(RTF)).Result

using System;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Threading;

namespace ConsoleApplication_22717365
{
    // by Noseratio - https://stackoverflow.com/q/22717365/1768303
    public class Program
    {
        const string RTF = @"{\rtf1\ansi{\fonttbl\f0\fswiss Helvetica;}\f0\pard This is some {\b bold} text.\par}";

        static void Main()
        {
            var xaml = RunOnWpfThreadAsync(() => ConvertRtfToXaml(RTF)).Result;
            Console.WriteLine(xaml);
        }

        // http://code.msdn.microsoft.com/windowsdesktop/Converting-between-RTF-and-aaa02a6e
        private static string ConvertRtfToXaml(string rtfText)
        {
            var richTextBox = new RichTextBox();
            if (string.IsNullOrEmpty(rtfText)) return "";
            var textRange = new TextRange(richTextBox.Document.ContentStart, richTextBox.Document.ContentEnd);
            using (var rtfMemoryStream = new MemoryStream())
            {
                using (var rtfStreamWriter = new StreamWriter(rtfMemoryStream))
                {
                    rtfStreamWriter.Write(rtfText);
                    rtfStreamWriter.Flush();
                    rtfMemoryStream.Seek(0, SeekOrigin.Begin);
                    textRange.Load(rtfMemoryStream, DataFormats.Rtf);
                }
            }
            using (var rtfMemoryStream = new MemoryStream())
            {
                textRange = new TextRange(richTextBox.Document.ContentStart, richTextBox.Document.ContentEnd);
                textRange.Save(rtfMemoryStream, DataFormats.Xaml);
                rtfMemoryStream.Seek(0, SeekOrigin.Begin);
                using (var rtfStreamReader = new StreamReader(rtfMemoryStream))
                {
                    return rtfStreamReader.ReadToEnd();
                }
            }
        }

        // https://stackoverflow.com/a/22626704/1768303
        public static async Task<TResult> RunOnWpfThreadAsync<TResult>(Func<Task<TResult>> funcAsync)
        {
            var tcs = new TaskCompletionSource<Task<TResult>>();

            Action startup = async () =>
            {
                // this runs on the WPF thread
                var task = funcAsync();
                try
                {
                    await task;
                }
                catch
                {
                    // propagate exception with tcs.SetResult(task)
                }
                // propagate the task (so we have the result, exception or cancellation)
                tcs.SetResult(task);

                // request the WPF tread to end
                // the message loop inside Dispatcher.Run() will exit
                System.Windows.Threading.Dispatcher.ExitAllFrames();
            };

            // the WPF thread entry point
            ThreadStart threadStart = () =>
            {
                // post the startup callback
                // it will be invoked when the message loop starts pumping
                System.Windows.Threading.Dispatcher.CurrentDispatcher.BeginInvoke(
                    startup, DispatcherPriority.Normal);
                // run the WPF Dispatcher message loop
                System.Windows.Threading.Dispatcher.Run();
            };

            // start and run the STA thread
            var thread = new Thread(threadStart);
            thread.SetApartmentState(ApartmentState.STA);
            thread.IsBackground = true;
            thread.Start();
            try
            {
                // propagate result, exception or cancellation
                return await tcs.Task.Unwrap().ConfigureAwait(false);
            }
            finally
            {
                // make sure the thread has fully come to an end
                thread.Join();
            }
        }

        // a wrapper to run synchronous code
        public static Task<TResult> RunOnWpfThreadAsync<TResult>(Func<TResult> func)
        {
            return RunOnWpfThreadAsync(() => Task.FromResult(func()));
        }
    }
}
使用系统;
使用System.IO;
使用系统线程;
使用System.Threading.Tasks;
使用System.Windows;
使用System.Windows.Controls;
使用System.Windows.Documents;
使用System.Windows.Threading;
命名空间控制台应用程序\u 22717365
{
//按比例—https://stackoverflow.com/q/22717365/1768303
公共课程
{
常量字符串RTF=@“{\rtf1\ansi{\fonttbl\f0\fswiss Helvetica;}\f0\pard这是一些{\b bold}文本。\par}”;
静态void Main()
{
var xaml=RunOnWpfThreadAsync(()=>ConvertRtfToXaml(RTF))。结果;
控制台写入线(xaml);
}
// http://code.msdn.microsoft.com/windowsdesktop/Converting-between-RTF-and-aaa02a6e
专用静态字符串转换器RTFTOXAML(字符串rtfText)
{
var richTextBox=新的richTextBox();
if(string.IsNullOrEmpty(rtfText))返回“”;
var textRange=new textRange(richTextBox.Document.ContentStart,richTextBox.Document.ContentEnd);
使用(var rtfMemoryStream=new MemoryStream())
{
使用(var rtfStreamWriter=newstreamwriter(rtfMemoryStream))
{
rtfStreamWriter.Write(rtfText);
rtfStreamWriter.Flush();
rtfMemoryStream.Seek(0,SeekOrigin.Begin);
加载(rtfMemoryStream,DataFormats.Rtf);
}
}
使用(var rtfMemoryStream=new MemoryStream())
{
textRange=新的textRange(richTextBox.Document.ContentStart,richTextBox.Document.ContentEnd);
保存(rtfMemoryStream,DataFormats.Xaml);
rtfMemoryStream.Seek(0,SeekOrigin.Begin);
使用(var rtfStreamReader=newstreamreader(rtfMemoryStream))
{
返回rtfStreamReader.ReadToEnd();
}
}
}
// https://stackoverflow.com/a/22626704/1768303
公共静态异步任务RunOnWpfThreadAsync(Func-funcAsync)
{
var tcs=new TaskCompletionSource();
操作启动=异步()=>
{
//这在WPF线程上运行
var task=funcAsync();
尝试
{