[XAML]ContentProperty集合属性

用过WPF的童鞋都知道,Panel或者ItemsControl之类的控件能够在XAML里直接为它们设置子项:

<Grid>
  <Label Content="1" />
  <Label Content="2" />
  <Label Content="3" />
</Grid>
<ListBox>
  <Label Content="1" />
  <Label Content="2" />
  <Label Content="3" />
</ListBox>

Panel之类的控件,它们的子项都是在Children这个属性里。

ItemsControl之类的控件,则是在Items这个属性里。

那么它们在XAML里是怎么做到不需要用到类似如下的方法而直接对子项赋值呢?

<Grid>
  <Grid.Children>
    <Label Content="1" />
    <Label Content="2" />
    <Label Content="3" />
  </Grid.Children>
</Grid>
<ListBox>
  <ListBox.Items>
    <Label Content="1" />
    <Label Content="2" />
    <Label Content="3" />
  </ListBox.Items>
</ListBox>

在Panel的代码里,有这样一部分代码:

[ContentProperty("Children")]
public class Panel
{
  public UIElementCollection Children { get; }
}

这段代码里,ContentProperty是注明Children属性是作为Panel的子项集合属性。这样在XAML中,就可以省略掉Panel.Children而直接给Panel的子项赋值了。

相应的,在ItemsControl里也有这样的代码:

[ContentPropery("Items")]
public class ItemsControl
{
  public ItemCollection Items { get; }
}

如果我们自己的类也要定义一个这样的东西,怎么办才好呢?

为类加上ContentProperty,然后设置一个集合属性。

根据MSDN上的说明,我们只需要在类里这样做:

[ContentProperty("Children")]
public class Test
{
  public List<string> Children { get; set; }
}

这样呢,我们就可以直接在XAML里为Test设置string子项了。

有童鞋会发现,这里是带set的,为何之前的Panel和ItemsControl里是没有set的呢?

因为有时候,我们的Children是有特殊功能的,不能让XAML对Children属性进行赋值,只能让我们的类进行初始化。

在Panel里,Children是UIElementCollection,它附带的功能是每当你Add和Remove控件的时候,会自动的设置该控件的Parent等等之类的信息。

如果我们自己的类,在子项增删时需要调用其它代码,那么,我们就需要自己创建一个集合类。

根据MSDN上的说法,只要我们的集合类继承IList<>接口即可。

[ContentProperty(Children)]
public class Test
{
  public Test()
  {
    _Children = new TestCollection();
  }
  
  private TestCollection _Children;
  public TestCollection Children { get { return _Children; } }
}

public class TestCollection : IList<string>
{
  ///...省略
}

但是作者在实际测试的时候遇到了一个问题,无论如何XAML也不认可没有set的Children属性。
研究了好一阵后,发现只要把IList<string>改为Collection<string>就可以达到目的了。(当然实际使用的时候不是string)

[ContentProperty("Children")]
public class Test
{
  public Test()
  {
    _Children = new TestCollection();
  }
  
  private TestCollection _Children;
  public TestCollection Children { get{ return _Children; } }
}

public class TestCollection : Collection<string>
{
  protected override void ClearItems()
  {
    //...省略
  }
  
  protected override void InsertItem(int index, UIElement item)
  {
    //...省略
  }
  
  protected override void RemoveItem(int index)
  {
    //...省略
  }
  
  protected override void SetItem(int index, UIElement item)
  {
    //...省略
  }
}

 

 

posted @ 2013-05-02 14:19  Kation  阅读(2158)  评论(0编辑  收藏  举报