我有一个用户控件,它有一个网格(创建用户控件时自动获得的网格)和一个画布。
<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”时,它会返回两个实例并按顺序添加到每个实例。这只是基于观察的猜测。
我做错了什么?
答案 0 :(得分:3)
花了我一分钟,但这很棘手:
您的DependencyProperty有一个默认值。默认值仅为DependencyProperty创建一次,然后分配给TestControl的两个实例。这样,当您向TestControl.Items添加内容时,您将其添加到公共ObservbleCollection,后者现在有两个CollectionChanged事件委托,每个委托都将新项目添加到各自的Canvas。
删除ItemsProperty的默认值。在构造函数中创建一个ObservbleCollection,或者xaml解释器将为每个TestControl创建一个。
规则:仅对值类型使用DependencyProperty的默认值,而不是引用类型。