WPF - 在自定义控件的内容项中使用数据绑定必须满足哪些条件?

时间:2009-04-27 20:36:31

标签: wpf binding dependency-properties

我正在制作自定义控件 - 特别是饼图。我希望标记看起来像这个例子:

<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输出窗口中的警告。在这种情况下,我什么都看不见。

2 个答案:

答案 0 :(得分:3)

看起来Slice控件没有连接到可视树。它们只是成员变量,而WPF并不知道它是将它们显示为PieChart的子元素。几种可能的方法:

PieChart调用AddLogicalChild添加Slice个对象。您需要处理在运行时将Slice添加到集合或从集合中删除的可能性。这是相当低的级别:MSDN建议,“对于控制作者,在此级别操作逻辑树不是推荐的做法,除非可用的基本控件类的内容模型都不适合您的控制方案。考虑在该级别进行子类化ContentControlItemsControlHeaderedItemsControl。“

PieChart成为Panel。然后Slice将成为其子项,但请注意Children属性的类型较弱,因此人们可以向PieChart添加任何内容。这本身并不是坏事或好事;这是一个设计考虑因素(见下面的注释)。但它可能不是你想要的。

PieChart成为ItemsControl,并覆盖GetContainerForItemOverrideIsItemItsOwnContainerOverride以及可能PrepareContainerForItemOverride来创建/尊重Slice控件。 (参见ListBoxListBoxItemComboBoxComboBoxItem等。)这样做的一个很好的功能是用户可以使用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>

我认为这很接近,但我可能会离开。我现在无法真正了解它。但这对我来说听起来不错。 (对不起,如果它完全错了:))