使用画布的多个用户控件实例会在Silverlight中导致异常

时间:2010-07-17 14:10:00

标签: c# wpf silverlight xaml user-controls

我有一个用户控件,它有一个网格(创建用户控件时自动获得的网格)和一个画布。

<Grid x:Name="LayoutRoot" Background="White">
    <Canvas x:Name="SurfaceCanvas">
    </Canvas>
</Grid>

在CS文件中,我定义了一个“Items”集合。

public ObservableCollection<TestItem> Items {
    get { return (ObservableCollection<TestItem>)GetValue(ItemsProperty); }
    set { SetValue(ItemsProperty, value); }
}

public static readonly DependencyProperty ItemsProperty =
    DependencyProperty.Register("Items", typeof(ObservableCollection<TestItem>),
    typeof(TestControl),
    new PropertyMetadata(new ObservableCollection<TestItem>()));

TestItem类声明:

public class TestItem : ContentControl { ... }

通过XAML将项目添加到其中。

<my:TestControl x:Name="ControlOne" Height="100" Width="100">
    <my:TestControl.Items>
        <my:TestItem x:Name="ItemOne">One</my:TestItem>
    </my:TestControl.Items>
</my:TestControl>

<my:TestControl x:Name="ControlTwo" Height="100" Width="100">
    <my:TestControl.Items>
        <my:TestItem x:Name="ItemTwo">Two</my:TestItem>
    </my:TestControl.Items>
</my:TestControl>

将项目添加到集合中时,我会将它们添加到画布中。

void Items_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e) {
    switch(e.Action) {
        case NotifyCollectionChangedAction.Add:
            foreach(Control item in e.NewItems) {
                SurfaceCanvas.Children.Add(item);
            }
        break;
    }
}

现在,问题。

如果有一个这个控件的实例,一切都很好。但是当我定义第二个实例时,我会在添加到InvalidOperationException的项目中收到ControlTwo:“元素已经是另一个元素的子元素。”

当我单步执行并观察元素是否添加到画布时,会发生什么?它会创建ItemOne并将其添加到ControlOne,然后创建ItemTwo并将其添加到{在尝试将其添加到ControlOne之前{1}}。这会导致异常,因为您不能同时将一个元素作为两个控件的父级。

我的猜测是它与每个实例中的画布具有相同名称的事实有关,因此当它解析“SurfaceCanvas”时,它会返回两个实例并按顺序添加到每个实例。这只是基于观察的猜测。

我做错了什么?

1 个答案:

答案 0 :(得分:3)

花了我一分钟,但这很棘手:

您的DependencyProperty有一个默认值。默认值仅为DependencyProperty创建一次,然后分配给TestControl的两个实例。这样,当您向TestControl.Items添加内容时,您将其添加到公共ObservbleCollection,后者现在有两个CollectionChanged事件委托,每个委托都将新项目添加到各自的Canvas。

删除ItemsProperty的默认值。在构造函数中创建一个ObservbleCollection,或者xaml解释器将为每个TestControl创建一个。

规则:仅对值类型使用DependencyProperty的默认值,而不是引用类型。