【问题标题】:Custom RichTextBlock raise exception "Element is already the child of another element"自定义 RichTextBlock 引发异常“元素已经是另一个元素的子元素”
【发布时间】:2015-01-25 23:40:07
【问题描述】:

我使用FlipView 控件并在ItemTemplate 内部使用Elements 属性创建自定义RichTextBlock,就像Run 元素的集合一样。它可以工作,但是,有时我会收到异常“WinRT 信息:元素已经是另一个元素的子元素。” 我真的不明白为什么。

有人可以解释一下为什么会这样吗?

示例 - http://1drv.ms/1yZM8a5

模型(包含测试数据)

public class Page
{
    public Page()
    {
        Data = new List<Run>();
        Data.Add(new Run { Text = "1" });
        Data.Add(new Run { Text = "2" });
        Data.Add(new Run { Text = "3" });
        Data.Add(new Run { Text = "4" });
        Data.Add(new Run { Text = "5" });
    }
    public IList<Run> Data { get; set; }
}

ViewModel(FlipView 的页面集合)

public ObservableCollection<Page> Pages { get; set; }

// test data
Pages = new ObservableCollection<Page>();
Pages.Add(new Page()); // etc

XAML

<FlipView ItemsSource="{Binding Pages}">
    <FlipView.ItemTemplate>
        <DataTemplate>
            <local:ExtendedRichTextBlock Elements="{Binding Data}" />
        </DataTemplate>
    </FlipView.ItemTemplate>
</FlipView>

ExtendedRichTextBlock

Elements 是依赖属性,变化在这里被捕获:

private static void OnElementsChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
    ExtendedRichTextBlock sender = d as ExtendedRichTextBlock;

    if (sender != null)
    {
        sender.Content.Blocks.Clear();

        var x = e.NewValue as IList<Run>;
                
        if (x != null)
        {
            foreach (var item in x)
            {
                var p = new Paragraph();
                p.Inlines.Add(item);    // WinRT information: Element is already the child of another element.

                sender.Content.Blocks.Add(p);
            }
        }
    }
}

结论

我在chat 中与 Romasz 讨论的简短结论。

每次触发事件时,您都会清除内容,然后重新使用 Run 元素填充它。在后面它可能非常复杂——可能是一些动画、过渡等等,我也不确定这是否不是异步制作的——如果是这样的话,可能会有元素仍然有父元素并且你尝试重用它的情况。考虑更好的做法,每次都使用 UI 元素填充这不是一个好主意 - 使您的数据模板与这些 UI 元素相结合,并将它们的内容绑定到某种集合。

所以必须在 OnElementsChanged 中动态创建所有元素。

【问题讨论】:

    标签: c# xaml windows-phone-8.1 windows-8.1


    【解决方案1】:

    您的程序似乎有时试图使 Run 元素成为另一个控件的子元素,而它已经有一个父元素,而 UI 元素只能有一个父元素。

    在这种情况下,请考虑对您的应用进行少许更改:

    private static void OnElementsChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        ExtendedRichTextBlock sender = d as ExtendedRichTextBlock;
    
        if (sender != null)
        {
            sender.Content.Blocks.Clear();
    
            var x = e.NewValue as IList<string>;
    
            if (x != null)
            {
                foreach (var item in x)
                {
                    var p = new Paragraph();
                    p.Inlines.Add(new Run { Text = item });    // WinRT information: Element is already the child of another element.
                    sender.Content.Blocks.Add(p);
                }
            }
        }
    }
    

    当然,要完成这项工作,您需要将所有 IList&lt;Run&gt; 更改为 IList&lt;string&gt;

    这将确保每个 Run 元素都是没有父元素的新元素。

    【讨论】:

    • 我猜它不会编译。 e.NewValue (IList&lt;Run&gt;) 不能转换为 IList&lt;String&gt;
    • @kennyzx 你是对的。我认为它非常明显,OP 将不得不更改整个实现 - 我只是标记了有问题的代码。
    • “你的程序有时会尝试让 Run 元素成为另一个控件的子元素” 好的,我明白了,但知道为什么会这样吗?
    • @kubakista 每次触发事件时,您都在清除内容,然后重新使用 Run 元素填充它。在后面它可能非常复杂——可能是一些动画、过渡等等,我也不确定这是否不是异步制作的——如果是这样的话,可能会有元素仍然有父元素并且你尝试重用它的情况。考虑更好的事情,每次都使用 UI 元素填充这不是一个好主意 - 使您的数据模板与这些 UI 元素相结合,并将它们的内容绑定到某种集合。
    • “用这些 UI 元素制作你的数据模板,并将它们的内容绑定到某种集合。” 我不确定我是否理解,也无法绑定到 RichTextBlock.Blocks .
    猜你喜欢
    • 1970-01-01
    • 2013-05-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-04-05
    • 1970-01-01
    相关资源
    最近更新 更多