类型为列表的附加属性

19

我想创建一个附加属性,该属性可以使用这种语法:

<Button>
  <Image .../>
  <ui:ToolbarItem.DisplayFilter>
    <TabItem .../>
    <TabItem .../>
    <TabItem .../>
  </ui:ToolbarItem.DisplayFilter>
</Button> 

这是我尝试做到的:

public class ToolbarItem
{
  /// <summary>
  /// Identifies the DisplayFilter attached property. 
  /// </summary>
  public static readonly DependencyProperty DisplayFilterProperty =
    DependencyProperty.RegisterAttached(
     "DisplayFilter",
     typeof( IList ),
     typeof( ToolbarItem )
    );

  public static IList GetDisplayFilter( Control item ) {
    return (IList)item.GetValue( DisplayFilterProperty );
  }

  public static void SetDisplayFilter( Control item, IList value ) {
    item.SetValue( DisplayFilterProperty, value );
  }
}

然而,这会在解析时引发异常 -- System.ArgumentException:TabItem 不是属性 'DisplayFilter' 的有效值。那么我如何配置我的附加属性,以便我可以使用所需的 XAML 语法?

1个回答

35
记住,XAML基本上只是对象创建的简写形式。因此,要将集合/列表作为附加的DisplayFilter属性的值创建,您必须将那些TabItems包含在另一个集合标记中。如果您不想这样做,这是可以理解的,您必须在第一次访问该属性时初始化集合。
但是,这里有一个问题:getter方法被XAML阅读器跳过了,以进行优化。您可以通过选择RegisterAttached调用的name参数的不同名称来防止此行为:
DependencyProperty.RegisterAttached("DisplayFilterInternal", ...)

然后将调用属性getter,您可以检查null。您可以在这篇博客文章中了解更多信息。 编辑:似乎链接的博客文章并不是那么清晰。只需更改传递给RegisterAttached的字符串名称,而不是静态get/set方法的名称。
public static readonly DependencyProperty DisplayFilterProperty =
    DependencyProperty.RegisterAttached(
        "DisplayFilterInternal",
        typeof(IList),
        typeof(ToolbarItem));

public static TabItemCollection GetDisplayFilter(Control item)
{ ... }

public static void SetDisplayFilter(Control item, IList value)
{ ... }

您需要在GetDisplayFilter方法中初始化集合:

public static TabItemCollection GetDisplayFilter(Control item)
{
    var collection = (IList)item.GetValue(DisplayFilterProperty);
    if (collection == null) {
        collection = new List<object>();
        item.SetValue(DisplayFilterProperty, collection);
    }
    return collection;
}

似乎你只向该集合添加了“TabItem”元素。然后,你可以使集合类型更加安全,但使用“IList”不起作用,因为XAML解析器无法调用通用方法“Add(T)”。 “Collection ” 和 “List ” 也实现非泛型的 “IList” 接口,可以在这种情况下使用。我建议,在将来要对集合进行一些更改时创建新的集合类型:
public class TabItemCollection : Collection<TabItem>
{
}

如果您不介意像这样明确设置集合:

<ui:ToolbarItem.DisplayFilter>
    <ui:TabItemCollection>
        <TabItem/>
    </ui:TabItemCollection>
</ui:ToolbarItem.DisplayFilter>

你可以删除SetDisplayFilter方法。
总结一下:
public class TabItemCollection : Collection<TabItem>
{
}

public class ToolbarItem
{
    public static readonly DependencyProperty DisplayFilterProperty =
        DependencyProperty.RegisterAttached(
            "DisplayFilterInternal", // Shadow the name so the parser does not skip GetDisplayFilter
            typeof(TabItemCollection),
            typeof(ToolbarItem));

    public static TabItemCollection GetDisplayFilter(Control item)
    {
        var collection = (TabItemCollection)item.GetValue(DisplayFilterProperty);
        if (collection == null) {
            collection = new TabItemCollection();
            item.SetValue(DisplayFilterProperty, collection);
        }
        return collection;
    }

    // Optional, see above note
    //public static void SetDisplayFilter(Control item, TabItemCollection value)
    //{
    //    item.SetValue(DisplayFilterProperty, value);
    //}
}

那么,如果发布的代码可以正常工作,那么肯定还有其他问题。请参见http://nopaste.org/p/adqfm5EPi,其中包含一个简短而完整的示例。 - gix
看起来您不能这样使用资源。如果您不使用资源或显式集合,似乎可以工作。 - gix
@gix:这对我有效,但是传递给setter的集合为空。 XAML中定义的元素应该何时出现在列表中? - Steven Jeuris
@gix:你认为将其制作成类似于Int32Collection(请参见备注)的Freezable是否适用于用作资源? - Jake Berger
这里采用了相同的方法: http://blogs.msdn.com/b/johngossman/archive/2008/07/28/how-to-initialize-an-attached-dependencyproperty-of-type-collection.aspx - HappyNomad
显示剩余3条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接