XAML 集合填充创建重复项
XAML collection filling creates duplicates
我创建了一个 UserControl
,它通过其 ContentProperty
属性接收对象集合,并通过为每个对象创建一个 ContentControl
将它们布置在屏幕上。在此示例中,这些对象是字符串以使其简单,但我的实际应用程序使用更复杂的对象和一些逻辑来选择足够的 DataTemplates;布局也更复杂。
此屏幕截图显示了我的目标:
但我得到的是这个:
问题是 MyControl
的每个实例都被赋予了分配给它们的所有字符串,而不是每个实例只获得专门分配给它的字符串。
Window 代码:
<Window x:Class="WpfApp1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
xmlns:local="clr-namespace:WpfApp1">
<StackPanel Orientation="Horizontal">
<local:MyControl Margin="10">
<sys:String>AAA</sys:String>
<sys:String>BBB</sys:String>
<sys:String>CCC</sys:String>
</local:MyControl>
<local:MyControl Margin="10">
<sys:String>DDD</sys:String>
<sys:String>EEE</sys:String>
<sys:String>FFF</sys:String>
</local:MyControl>
<local:MyControl Margin="10">
<sys:String>GGG</sys:String>
<sys:String>HHH</sys:String>
<sys:String>III</sys:String>
</local:MyControl>
</StackPanel>
</Window>
用户控件代码:
[ContentProperty(nameof(MyProperty))]
public class MyControl : UserControl, IAddChild
{
public static readonly DependencyProperty MyPropertyProperty =
DependencyProperty.Register(nameof(MyProperty), typeof(IList), typeof(MyControl), new PropertyMetadata(new List<object>()));
public IList MyProperty
{
get { return (IList)GetValue(MyPropertyProperty); }
set { SetValue(MyPropertyProperty, value); }
}
public MyControl()
{
var sp = new StackPanel();
Content = sp;
Loaded += (_, __) =>
{
foreach (string e in MyProperty)
sp.Children.Add(new ContentControl { Content = e });
};
}
}
您的 UserControl
重用在 PropertyMetadata
中作为默认值创建的 相同 列表实例。来自集合类型依赖属性的documentation:
If your property is a reference type, the default value specified in dependency property metadata is not a default value per instance; instead it is a default value that applies to all instances of the type.
To correct this problem, you must reset the collection dependency property value to a unique instance, as part of the class constructor call.
要在您的代码中执行此操作,请像这样在构造函数中创建并设置一个新的列表实例。
public MyControl()
{
MyProperty = new List<object>();
// .. rest of the constructor.
}
额外答案:如果您使用的集合类型 属性 不应从控件外部写入,您可以考虑像下面这样创建依赖关系 属性 read-only。
[ContentProperty(nameof(MyProperty))]
public class MyControl : UserControl, IAddChild
{
private static readonly DependencyPropertyKey MyPropertyPropertyKey =
DependencyProperty.RegisterReadOnly(nameof(MyProperty), typeof(IList),
typeof(MyControl), new PropertyMetadata(new List<object>()));
public static readonly DependencyProperty MyPropertyProperty =
MyPropertyPropertyKey.DependencyProperty;
public IList MyProperty => (IList)GetValue(MyPropertyProperty);
public MyControl()
{
SetValue(MyPropertyPropertyKey, new List<object>());
var sp = new StackPanel();
Content = sp;
Loaded += (_, __) =>
{
foreach (string e in MyProperty)
sp.Children.Add(new ContentControl { Content = e });
};
}
}
我创建了一个 UserControl
,它通过其 ContentProperty
属性接收对象集合,并通过为每个对象创建一个 ContentControl
将它们布置在屏幕上。在此示例中,这些对象是字符串以使其简单,但我的实际应用程序使用更复杂的对象和一些逻辑来选择足够的 DataTemplates;布局也更复杂。
此屏幕截图显示了我的目标:
但我得到的是这个:
问题是 MyControl
的每个实例都被赋予了分配给它们的所有字符串,而不是每个实例只获得专门分配给它的字符串。
Window 代码:
<Window x:Class="WpfApp1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
xmlns:local="clr-namespace:WpfApp1">
<StackPanel Orientation="Horizontal">
<local:MyControl Margin="10">
<sys:String>AAA</sys:String>
<sys:String>BBB</sys:String>
<sys:String>CCC</sys:String>
</local:MyControl>
<local:MyControl Margin="10">
<sys:String>DDD</sys:String>
<sys:String>EEE</sys:String>
<sys:String>FFF</sys:String>
</local:MyControl>
<local:MyControl Margin="10">
<sys:String>GGG</sys:String>
<sys:String>HHH</sys:String>
<sys:String>III</sys:String>
</local:MyControl>
</StackPanel>
</Window>
用户控件代码:
[ContentProperty(nameof(MyProperty))]
public class MyControl : UserControl, IAddChild
{
public static readonly DependencyProperty MyPropertyProperty =
DependencyProperty.Register(nameof(MyProperty), typeof(IList), typeof(MyControl), new PropertyMetadata(new List<object>()));
public IList MyProperty
{
get { return (IList)GetValue(MyPropertyProperty); }
set { SetValue(MyPropertyProperty, value); }
}
public MyControl()
{
var sp = new StackPanel();
Content = sp;
Loaded += (_, __) =>
{
foreach (string e in MyProperty)
sp.Children.Add(new ContentControl { Content = e });
};
}
}
您的 UserControl
重用在 PropertyMetadata
中作为默认值创建的 相同 列表实例。来自集合类型依赖属性的documentation:
If your property is a reference type, the default value specified in dependency property metadata is not a default value per instance; instead it is a default value that applies to all instances of the type.
To correct this problem, you must reset the collection dependency property value to a unique instance, as part of the class constructor call.
要在您的代码中执行此操作,请像这样在构造函数中创建并设置一个新的列表实例。
public MyControl()
{
MyProperty = new List<object>();
// .. rest of the constructor.
}
额外答案:如果您使用的集合类型 属性 不应从控件外部写入,您可以考虑像下面这样创建依赖关系 属性 read-only。
[ContentProperty(nameof(MyProperty))]
public class MyControl : UserControl, IAddChild
{
private static readonly DependencyPropertyKey MyPropertyPropertyKey =
DependencyProperty.RegisterReadOnly(nameof(MyProperty), typeof(IList),
typeof(MyControl), new PropertyMetadata(new List<object>()));
public static readonly DependencyProperty MyPropertyProperty =
MyPropertyPropertyKey.DependencyProperty;
public IList MyProperty => (IList)GetValue(MyPropertyProperty);
public MyControl()
{
SetValue(MyPropertyPropertyKey, new List<object>());
var sp = new StackPanel();
Content = sp;
Loaded += (_, __) =>
{
foreach (string e in MyProperty)
sp.Children.Add(new ContentControl { Content = e });
};
}
}