如何在没有 AccessViolationException 的情况下在 RichEditBox 中设置多个链接 [英] How to set multiple links in RichEditBox without AccessViolationException

查看:35
本文介绍了如何在没有 AccessViolationException 的情况下在 RichEditBox 中设置多个链接的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

基本上我的问题是当我尝试以编程方式将超过 2 个链接应用到 ITextDocument 时,如果用户已编辑内容,我会收到 AccessViolationException.我已经根据 Windows Phone (8.1) 空白应用模板制作了一个简单的演示应用.

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.

我添加到主页:

<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.

但是,如果您输入三个或更多(出于某种原因,对我来说总是三个或更多)demoexampletest(我的关键字)并点击按钮,它在设置 range.Link = link 时出现 AccessViolationException 错误.值得注意的是,如果你在调试时检查,range.Link 属性实际上已经设置了.

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.

更有趣的是,如果您取消注释 RtfBox_Loaded 的内容,然后运行应用程序并立即单击按钮,它会很好地处理它.所以它似乎与 RichEditBox 上设置的选择有关?我尝试在应用链接之前禁用控件,但这对我没有帮助.

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:

  • 如果我逐行调试,它似乎工作得更频繁,所以也可能与时间有关
  • 我似乎无法在 UI 线程上使用 ITextDocument(COM 对象无法强制转换),因此虽然异步可能是更好的方法,但我在这里没有成功.

另外,为了记录,我尝试对所有更新进行批量更新,而不是在用户键入它们时进行的原因是我不想在重命名或删除笔记时处理清理,并且我真的不希望在编辑时或保存这些链接,但我可以忍受后者.

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.

推荐答案

这个解决方案是 在 MSDN 论坛上发布,由 Eric Fleck 并为我工作:

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.

我还没有使用将选择恢复到其原始位置的功能,但我将来可能想要,所以我制作了这个小实用程序类.也这样我可以将这些地方包装在 using() 块中以获得一些语法糖.

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();
        }
    }
}

允许我写

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

这篇关于如何在没有 AccessViolationException 的情况下在 RichEditBox 中设置多个链接的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆