我正在使用this code尝试将我的Rtf文本从我的UI中的RichTextBox
转换为Html。我的解决方案中有2个项目:UI&用MVVM- IsesTextEditor
编写的应用程序和链接中提供的预先编写的转换器 - MarkupConverter
。
我视图中的btn绑定到我的视图模型中的命令,该命令将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();
}
}
}
}
在创建RichTextBox
对象时,我收到了The calling thread cannot access this object because a different thread owns it.
例外。
有人可以建议修复此问题吗?我敢肯定这是一个相对简单的线程问题,但我是一个初级开发人员。没有太多线程或STA的经验...
答案 0 :(得分:1)
如异常建议,将GUI相关代码从工作线程移动到GUI线程。
请参阅此post,我从中复制了一些文字:
与用户界面的框架一样,与许多Windows窗体一样,WPF也强制使用单个线程模型,这意味着您只能访问创建它的指定派生DispatcherObject线程。在实现接口ISynchronizeInvoke的Windows窗体控件中,此接口公开一组方法(如Invoke和BeginInvoke)以强制执行合约公共线程同步,我们可以使用该同步从另一个线程访问控件。在WPF中,我们也有这样的东西,但这些操作都涉及一个名为Dispatcher的类,Dispatcher WPF是允许这种线程同步模型的方式。
答案 1 :(得分:1)
要在不是主UI线程的线程上使用WPF控件,您需要执行一些管道工作,例如启动和完成WPF Dispatcher循环。
我已经整理了一个示例应用应用,展示了如何执行此操作,使用我之前发布的一些帮助代码here。
这是一个控制台应用,尽管您应该能够在任何其他执行环境中使用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()));
}
}
}