How to set multiple links in RichEditBox without AccessViolationException

雨燕双飞 提交于 2019-12-11 04:22:38

问题


Essentially my problem is that I receive AccessViolationException when I try to programmatically apply more than 2 links to an ITextDocument, if a user has edited the content. I've put together a simple demo app, based on the windows phone (8.1) Blank App template.

I add to the main page:

<StackPanel Margin="19,0,0,0">
    <Button
        Content="Apply Links"
        Click="Button_Click"
        />
    <RichEditBox
        x:Name="RtfBox"
        Height="300"
        Loaded="RtfBox_Loaded"
        Margin="0,0,19,0"
        TextWrapping="Wrap"
        />
</StackPanel>

And to the code behind for the same page I add (using statements not included):

    private void RtfBox_Loaded(object sender, RoutedEventArgs e)
    {
        //RtfBox.Document.SetText(TextSetOptions.None, "Links to demo, example, test. More links to demo, demo, example, test and test.");
    }

    private void Button_Click(object sender, RoutedEventArgs e)
    {
        var pages = new Dictionary<Guid, string> { { Guid.NewGuid(), "demo" }, { Guid.NewGuid(), "example" }, { Guid.NewGuid(), "test" } };

        // NOTE: Avoid performance implications of many small updates
        RtfBox.Document.BatchDisplayUpdates();

        ITextRange range;
        foreach (var page in pages)
        {
            var link = string.Format("\"richtea.demo://pages/{0}\"", page.Key);
            var skip = 0;

            while ((range = RtfBox.Document.GetRange(skip, TextConstants.MaxUnitCount)).FindText(page.Value, TextConstants.MaxUnitCount, FindOptions.None) != 0)
            {
                if (range.Link == "")
                {
                    // TODO: Stop this throw exceptions
                    System.Diagnostics.Debug.WriteLine("Setting text at position {0} to link: '{1}'.", range.StartPosition, link);
                    range.Link = link;
                }

                skip = range.EndPosition;
            }
        }

        RtfBox.Document.ApplyDisplayUpdates();
    }

If you start this up and type something like "A link to the demo page" and click the button, it becomes a link correctly. You can keep putting the same text and clicking the button and it continues to work.

However if you put in three or more (for some reason for me it's always 3 or more) of the words demo, example or test (my keywords) and hit the button, it errors on an AccessViolationException on setting range.Link = link. It's worth noting if you check while debugging, the range.Link property has actually been set.

More interestingly, if you uncomment RtfBox_Loaded's contents, and run the app and click the button right away, it handles it fine. So it seems to relate to the selection having been set on the RichEditBox? I've tried disabling the control before applying the links, but that's not helped me.

Some other things which have made it harder for me to diagnose the issue here include:

  • It seems to work more often if I'm debugging line by line, so might be timing related too
  • I can't use the ITextDocument not on the UI thread it seems (the COM object fails to cast) so while it seems async might be a better approach, I haven't succeeded at it here.

Also for the record, the reason I'm attempting to do all the updates on mass, rather than as the user types them is that I don't want to deal with the cleanup when notes are renamed or deleted, and I don't really want those links in at edit time or saved, but I could live with the later.


回答1:


This solution was posted on the MSDN forums by Eric Fleck and worked for me:

RtfBox.Document.Selection.StartPosition = RtfBox.Document.Selection.EndPosition = range.StartPosition;
range.Link = link;
RtfBox.Document.Selection.StartPosition = RtfBox.Document.Selection.EndPosition = range.EndPosition;

It seemed that it's important to do it around each link being set, because unless I'm very much mistaken I tried this before updating all my links and it didn't help.

I'm not yet using the ability to restore the selection to it's original location, but I might want to in the future, so I made this little utility class. Also so that I can wrap places like these in a using() block for a bit of syntactic sugar.

public static class ITextDocumentExtensions
{
    public static IDisposable SuppressSelection(this ITextDocument document)
    {
        var start = document.Selection.StartPosition;
        var end = document.Selection.EndPosition;

        var disposable = new ActionDisposable(() => document.Selection.SetRange(start, end));
        document.Selection.SetRange(0, 0);

        return disposable;
    }

    private sealed class ActionDisposable : IDisposable
    {
        private readonly Action dispose;

        public ActionDisposable(Action dispose)
        {
            this.dispose = dispose;
        }

        public void Dispose()
        {
            dispose();
        }
    }
}

Which allows me to write

using (RtfBox.Document.SuppressSelection())
{
    range.Link = link;
}


来源:https://stackoverflow.com/questions/27678815/how-to-set-multiple-links-in-richeditbox-without-accessviolationexception

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!