Have an object with most of properties very lightweight - text up to 200 character. One property is FlowDocument that can be large and want to retrieve it Async. It fails when I set Async = True with the message: "The calling thread cannot access this object because a different thread owns it."
<FlowDocumentReader Name="FlowDocumentPageViewer1" HorizontalAlignment="Stretch"
Document="{Binding Source={x:Static Application.Current}, Path=MyGabeLib.Search.SelectedDoc.DocFlowDocument, Mode=OneWay, IsAsync=True}" />
Production Get is more complex but the same failure on IsAsyc True even with a simple hard coded FlowDocument.
public FlowDocument DocFlowDocument
{
get
{
FlowDocument docFlowDocumentFast = new FlowDocument();
Paragraph p = new Paragraph();
Run r = new Run();
r.Foreground = System.Windows.Media.Brushes.Red;
r.Text = "Hard Code Simple FlowDocument";
p.Inlines.Add(r);
docFlowDocumentFast.Blocks.Add(p);
return docFlowDocumentFast;
}
{
It does call SelectedDoc.DocFlowDocument and the document is returned! With IsAsync = False it runs just fine. I think the problem is the Static Source but clearly I don't know how to fix it.
public partial class App : Application
{
private static GabeLib staticGabeLib = new GabeLib();
private GabeLib myGabeLib = staticGabeLib;
public GabeLib MyGabeLib
{ get { return myGabeLib; } }
public static GabeLib StaticGabeLib
{ get { return staticGabeLib; } }
}
When GabeLib starts it reads in application and user setting from a database.
If there is a better way to approach this I will give it a try. Since the FlowDocument can be 3 mb and all the other properties 10K combined this is big performance hit and the most used button is next object. The FlowDocument comes from from a varchar(max) in SQL and gets formatted with line breaks and words highlighted. It is not just big - compared to the other properties it is also expensive.
The FlowDocumentReader itself appears to have some async support as on a large document I get the first page fast and then pages load at about 100/second. But I would still like it to get page 1 after all the other properties are retrieved.
The problem was as martin stated "Since a FlowDocument is a dispatcher object it can only be accessed from the thread that created it".
The solution was to serialize to a XAML string.
public string XAMLdocFlowDocument
{
get
{
Thread.Sleep(6000);
return XamlWriter.Save(FlowDocumentSlow);
}
}
Bind to the string with a converter
<FlowDocumentReader Grid.Row="3" Grid.Column="0" VerticalAlignment="Stretch"
Document="{Binding Path=XAMLdocFlowDocument, IsAsync=True,
Converter={StaticResource flowDocumentToXamlConverter}, Mode=OneWay}" />
Converter
[ValueConversion(typeof(string), typeof(FlowDocument))]
public class FlowDocumentToXamlConverter : IValueConverter
{
#region IValueConverter Members
/// <summary>
/// Converts from XAML markup to a WPF FlowDocument.
/// </summary>
public object Convert(object value, System.Type targetType,
object parameter, System.Globalization.CultureInfo culture)
{
var flowDocument = new FlowDocument();
if (value != null)
{
var xamlText = (string)value;
flowDocument = (FlowDocument)XamlReader.Parse(xamlText);
}
// Set return value
return flowDocument;
}