0

I am trying to add a Paragraph to a RichTextBox control from another thread and my app crashes as soon as I try to do that

.Net Core 3.1

This is my Extensions source

public static class ParagraphExtention
    {
        public static void Append(this Paragraph paragraph, string value = "", Brush background = null, Brush foreground = null, bool bold = false, bool italic = false, bool underline = false, bool waitUntilReturn = false)
        {
            Action append = () =>
            {
                Inline run = new Run(value);

                if (background != null) run.Background = background;
                if (foreground != null) run.Foreground = foreground;
                if (bold) run = new Bold(run);
                if (italic) run = new Italic(run);
                if (underline) run = new Underline(run);

                paragraph.Inlines.Add(run);
            };
            if (paragraph.CheckAccess())
            {
                append();
            }
            else if (waitUntilReturn)
            {
                paragraph.Dispatcher.Invoke(append);
            }
            else
            {
                paragraph.Dispatcher.BeginInvoke(append);
            }
        }
    }

    public static class RichTextBoxExtensions
    {
        public static void CheckAppendText(this RichTextBox richtextBox, Paragraph msg, bool waitUntilReturn = false)
        {
            //Action append = () =>
            Action append = () =>
            {
                richtextBox.Document.CheckAppendText(msg);
            };
            if (richtextBox.CheckAccess())
            {
                append();
            }
            else if (waitUntilReturn)
            {
                richtextBox.Dispatcher.Invoke(append);
            }
            else
            {
                richtextBox.Dispatcher.BeginInvoke(append);
            }
        }
    }

    public static class FlowDocumentExtensions
    {
        public static void CheckAppendText(this FlowDocument fDoc, Paragraph msg, bool waitUntilReturn = false)
        {
            //Action append = () =>
            Action append = () =>
            {
                //Paragraph msgx = msg;
                // msgx.Inlines.Add(Environment.NewLine);

                fDoc.Blocks.Add(msg);
            };
            if (fDoc.CheckAccess())
            {
                append();
            }
            else if (waitUntilReturn)
            {
                fDoc.Dispatcher.Invoke(append);
            }
            else
            {
                fDoc.Dispatcher.BeginInvoke(append);
            }
        }
    }

Which is how I add a Paragraph but no idea why its not working

Also this is the error I am getting

MS.Internal.PtsHost.UnsafeNativeMethods.PTS.SecondaryException
  HResult=0x80131500
  Message=The calling thread cannot access this object because a different thread owns it.
  Source=WindowsBase
  StackTrace:
   at System.Windows.Threading.Dispatcher.VerifyAccess()
   at System.Windows.DependencyObject.GetValue(DependencyProperty dp)
   at System.Windows.Documents.Paragraph.get_KeepWithNext()
   at MS.Internal.Text.DynamicPropertyReader.GetKeepWithNext(DependencyObject element)
   at MS.Internal.PtsHost.BaseParagraph.GetParaProperties(FSPAP& fspap, Boolean ignoreElementProps)
   at MS.Internal.PtsHost.ContainerParagraph.GetParaProperties(FSPAP& fspap)
   at MS.Internal.PtsHost.PtsHost.GetParaProperties(IntPtr pfsclient, IntPtr nmp, FSPAP& fspap)

  This exception was originally thrown at this call stack:
    [External Code]

Inner Exception 1:
InvalidOperationException: The calling thread cannot access this object because a different thread owns it.

There isn't anything else really and I just use the Extentions like this

Paragraph p1 = new Paragraph();
p1.Append("Hello", Brushes.Transparent, Brushes.Green, true, false, false);
iSAPPRemoteUI.richtextBox_console.CheckAppendText(p1);

All this use to be fine when i just used plain text all problem started to happen when i tried to add the formatting

POQDavid
  • 195
  • 1
  • 15
  • please ***include*** all relevant pieces information ***in*** your question. copy your source code and error message instead of linking to it. – Franz Gleichmann Jul 23 '20 at 11:35
  • 1
    @FranzGleichmann is that better? – POQDavid Jul 23 '20 at 11:55
  • Does this answer your question? [Updating GUI (WPF) using a different thread](https://stackoverflow.com/questions/4253088/updating-gui-wpf-using-a-different-thread) – Franz Gleichmann Jul 23 '20 at 11:57
  • You can't modify the UI from another thread, in any OS. Use `async/await` to get back to the UI thread after any asynchronous operation and update the UI. You *don't* need `Dispatcher` since 2012. If you want to update the UI in response to some event deep inside some other code you can use the `IProgress` interface and `Progress` class to publish some message from one thread and receive it in the UI thread. – Panagiotis Kanavos Jul 23 '20 at 12:04
  • The extension methods you created don't do what you think they do. For starters, they are a *very* convoluted way of writing `Task.Run()=>append())`. That's *all* they do, apart from not working. They *still* try to modify the UI inside that `append` resulting in an exception – Panagiotis Kanavos Jul 23 '20 at 12:06
  • @FranzGleichmann Well I tried to set DispatcherPriority which it didn't work also, and other than that my source its doing pretty much the same but in the link you sent they are just setting plain text not the FlowDocument – POQDavid Jul 23 '20 at 12:06
  • @POQDavid what are you trying to do? At best, you'll get back to the UI thread. You **can't** modify the UI from another thread. `Invoke` and `BeginInvoke` are used to get your method to run back in the UI thread. When you call `BeginInvoke` though, you start an *asynchronous* operation, while still on the UI thread. – Panagiotis Kanavos Jul 23 '20 at 12:09
  • All this code *at best* will be nothing more than an `append()` – Panagiotis Kanavos Jul 23 '20 at 12:10
  • @PanagiotisKanavos I am just trying to add formatted text to RichTextBox which uses FlowDocument this code used to be fine when i was just doing this with plain text – POQDavid Jul 23 '20 at 12:16
  • @PanagiotisKanavos As it turns out i can't just create the Paragraph object in my tcpclient thread and just simply add to my RichTextBox hmm didn't think a paragraph would count as control – POQDavid Jul 23 '20 at 12:51

1 Answers1

0

The thread that created the control (likely the main thread i.e. the main user interface thread) has ownership of the control. Other threads cannot use this.

In order to help with this, you need to run any interactions in the same thread.

In wpf you can use the dispatcher for this. There are many resources online, e.g: https://stackoverflow.com/a/1644254/4122889

In winforms, you can actually use control.Invoke(..) for this, e.g.

if (lblDescription.InvokeRequired)
    {
        DispatchHandler handler = delegate()
            {
                lblDescription.Text = description;
            };
            this.BeginInvoke(handler);
    }
    else
       lblDescription.Text = description;

source

sommmen
  • 6,570
  • 2
  • 30
  • 51
  • Such code became obsolete with `async/await`. A simple `await someAsyncOp(); lblDescription.Text = description;` would do the same. The OP is trying to use BeginInvoke to trick .NET and run the modification in the background though – Panagiotis Kanavos Jul 23 '20 at 12:10
  • I get your point but async/await programming is a whole different story - one that i rather won't tell to people that aren't specifically looking for that. its great and all, but you can run into some weird issues.. – sommmen Jul 23 '20 at 12:12
  • No, it's not. It's exactly the same code, only a lot simpler. *All* of this code is replaced by a simple `await SomeOp();` – Panagiotis Kanavos Jul 23 '20 at 12:17
  • @sommmen and i dont get any InvokeRequired option with .Net Core 3.1 – POQDavid Jul 23 '20 at 12:18
  • @POQDavid could be - but you dont need it. Just make sure the code runs on the right thread, one way is using Invoke. The check is pretty redundant – sommmen Jul 23 '20 at 13:26
  • @sommmen Yah the thing that I didn't noticed was that Paragraph object had to be created on the UI thread or it would have cased crash even tho clearly u see i invoked it – POQDavid Jul 23 '20 at 18:41