Pergunta

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;
        }
Foi útil?

Solução

Without seeing the code I'm guessing that the FlowDocument is created (and loaded) when the property is read. This is done in a background thread since the property binding is async.

Since a FlowDocument is a dispatcher object it can only be accessed from the thread that created it, which in this case is the background thread that read the property.

So, the created FlowDocument cannot be accessed by your UI-thread.

You'll need another approach for loading the document asynchronously.

Perhaps you could use synchronous (normal) binding and use XamlReader.LoadAsync to load the document? I haven't tried it myself but I'm guessing it's worth experimenting with.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top