类型列表的附加属性

发布于 2024-08-04 18:44:32 字数 972 浏览 9 评论 0原文

我想创建一个可以与此语法一起使用的附加属性:

<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 不是属性的有效值'显示过滤器'。那么如何配置我的附加属性以便我可以使用所需的 XAML 语法?

I want to create an attached property that can be used with this syntax:

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

This is my attempt at doing so:

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 );
  }
}

This, however, is causing an exception at parse-time -- System.ArgumentException: TabItem is not a valid value for property 'DisplayFilter'. So how do I configure my attached property so that I can use the desired XAML syntax?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

地狱即天堂 2024-08-11 18:44:32

请记住,XAML 基本上只是对象创建的简写形式。因此,要创建一个集合/列表作为附加的 DisplayFilter 属性的值,您必须将这些 TabItems 包含在另一个集合标记内。如果您不想这样做(这是可以理解的),则必须在第一次访问该属性时初始化该集合。

这样做只有一个问题:作为一种优化,XAML 读取器会跳过 getter 方法。您可以通过为 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<中初始化集合/code> 方法:

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)CollectionList 也实现非泛型 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);
    //}
}

Remember that XAML is basically just a shorthand form of object creation. So to create a collection/list as the value for the attached DisplayFilter property you would have to enclose those TabItems inside another collection tag. If you don't want to do that, which is understandable, you have to initialize the collection the first time the property is accessed.

There is just one problem with this: The getter method is skipped by the XAML reader as an optimization. You can prevent this behavior by choosing a different name for the name argument to the RegisterAttached call:

DependencyProperty.RegisterAttached("DisplayFilterInternal", ...)

Then the property getter will be called and you can check for null. You can read more about that in this blog post.

Edit: Seems like the linked blog post isn't that clear. You change only the name of the string passed to RegisterAttached, not the name of the static get/set methods:

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)
{ ... }

You have to initialize the collection in the GetDisplayFilter method:

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;
}

It seems that you only add TabItem elements to that collection. Then you can make the collection type-safe, but using IList<T> does not work since the XAML parser cannot invoke the generic method Add(T). Collection<T> and List<T> also implement the non-generic IList interface and can be used in this case. I would suggest to create a new collection type in case you want to do some changes to the collection in the future:

public class TabItemCollection : Collection<TabItem>
{
}

If you don't care about setting the collection explicitly like this:

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

you can remove the SetDisplayFilter method.

To summarize:

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);
    //}
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文