我正在制作自定义控件 - 特别是饼图。我希望标记看起来像这个例子:
<c:PieChart>
<!-- These dependency properties are never set -->
<c:Slice Value="{Binding RedCount}" />
<c:Slice Value="{Binding BlueCount}" />
<c:Slice Value="{Binding GreenCount}" />
</c:PieChart>
PieChart
来自Control
:
[ContentProperty("Slices")]
public class PieChart : Control
{
public PieChart()
{
Slices = new ObservableCollection<Slice>();
}
public ObservableCollection<Slice> Slices { get; private set; }
}
上述XAML导致Slices
属性填充了三个Slice
实例。
以下是Slice
的摘录:
public class Slice : ContentControl
{
public static DependencyProperty ValueProperty
= DependencyProperty.Register(
"Value", typeof(double), typeof(Slice),
new PropertyMetadata((p,a) => ((Slice)p).OnValueChanged(a)));
private void OnValueChanged(DependencyPropertyChangedEventArgs e)
{
// This code never called!
}
}
问题是从未设置Value
上的Slice
属性。如果我将依赖属性放在PieChart
上并复制绑定表达式,那么设置值,所以我相信我理解依赖属性和绑定的基础知识。
那么,我需要做些什么来让我的依赖属性在子项上设置一个值?由于这些项目都在我自己的集合中,它们是否在某种程度上不在逻辑树中识别出什么魔法使绑定工作?
一个简单的“做这个”答案会有所帮助,但我真的很想知道这里出了什么问题,所以我会接受最有见地的解释性答案。
编辑我没有说清楚,但DataContext
的{{1}}上有一些环境对象包含属性PieChart
等。在过去,当我在绑定路径中出现拼写错误(或其他一些错误)时,我在运行时看到了VS输出窗口中的警告。在这种情况下,我什么都看不见。
答案 0 :(得分:3)
看起来Slice
控件没有连接到可视树。它们只是成员变量,而WPF并不知道它是将它们显示为PieChart
的子元素。几种可能的方法:
让PieChart
调用AddLogicalChild
添加Slice
个对象。您需要处理在运行时将Slice
添加到集合或从集合中删除的可能性。这是相当低的级别:MSDN建议,“对于控制作者,在此级别操作逻辑树不是推荐的做法,除非可用的基本控件类的内容模型都不适合您的控制方案。考虑在该级别进行子类化ContentControl
,ItemsControl
和HeaderedItemsControl
。“
让PieChart
成为Panel
。然后Slice
将成为其子项,但请注意Children
属性的类型较弱,因此人们可以向PieChart
添加任何内容。这本身并不是坏事或好事;这是一个设计考虑因素(见下面的注释)。但它可能不是你想要的。
让PieChart
成为ItemsControl
,并覆盖GetContainerForItemOverride
,IsItemItsOwnContainerOverride
以及可能PrepareContainerForItemOverride
来创建/尊重Slice
控件。 (参见ListBox
和ListBoxItem
,ComboBox
和ComboBoxItem
等。)这样做的一个很好的功能是用户可以使用ItemsSource
和{ {1}}(如DataTemplate
),而不是手动创建子控件。 (但是用户仍然可以根据您的语法在XAML中创建固定的ListBox
,就像他们可以在XAML中创建Slice
一样。)
您甚至可以考虑使用混合方法,例如将径向布局功能分解为ListBoxItems
并将饼图生成分解为RadialLayoutPanel
,其中使用ItemsControl
作为其RadialLayoutPanel
。
答案 1 :(得分:0)
我认为您遇到的问题是绑定试图使用错误的来源。我怀疑这样的事情会对你有用:
<c:PieChart>
<!-- These dependency properties are never set -->
<c:Slice Value="{Binding RedCount, RelativeSource={AncestorType c:PieChart}}" />
<c:Slice Value="{Binding BlueCount, RelativeSource={AncestorType c:PieChart}}" />
<c:Slice Value="{Binding GreenCount, RelativeSource={AncestorType c:PieChart}}" />
</c:PieChart>
我认为这很接近,但我可能会离开。我现在无法真正了解它。但这对我来说听起来不错。 (对不起,如果它完全错了:))