自定义RichTextBlock引发异常" Element已经是另一个元素的子元素了#34;

时间:2015-01-25 23:40:07

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

我使用FlipView控件并在ItemTemplate内部,我创建了自定义RichTextBlock,其Elements属性创建为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中动态创建所有元素。

1 个答案:

答案 0 :(得分: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<Run>更改为IList<string>

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