【问题标题】:ItemsPanelTemplate in XAML ignores [ContentProperty] attributeXAML 中的 ItemsPanelTemplate 忽略 [ContentProperty] 属性
【发布时间】:2012-07-15 03:18:48
【问题描述】:

我有一个自定义面板,我在其中声明了一个自定义属性来保存内容(我不想使用 Children 作为内容):

[ContentProperty(Name = "PanelContent")]
public class CustomPanel : Panel
{
    public static readonly DependencyProperty PanelContentProperty =
       DependencyProperty.Register("PanelContent", 
       typeof(Collection<UIElement>), typeof(CustomPanel), 
       new PropertyMetadata(new Collection<UIElement>(), null));

    public Collection<UIElement> PanelContent
    {
        get
        {
            return (Collection<UIElement>)GetValue(PanelContentProperty);
        }
    }
}

这样使用时效果很好:

<CustomPanel>
   <TextBlock>A</TextBlock>
   <TextBlock>B</TextBlock>
</CustomPanel>

但是当我想将面板用作 ItemsControl 中的 ItemsPanelTemplate 时,ContentProperty 属性将被忽略,并将所有内容添加到 Children 集合中,而不是 PanelContent 集合中:

<ItemsControl ItemTemplate="{StaticResource ReviewTemplate}" ItemsSource="{Binding Reviews}">
   <ItemsControl.ItemsPanel>
      <ItemsPanelTemplate>
         <CustomPanel></CustomPanel>
      </ItemsPanelTemplate>
   </ItemsControl.ItemsPanel>
</ItemsControl>

这不是它应该如何工作的。根据documentation

ItemsPanelTemplate 对象元素应该包含一个FrameworkElement 派生类,该类用作项目的根元素。在大多数情况下,这是一个 Panel 派生类。扩展模板作为已实现项的父项,通常有多个项。因此,ItemsPanelTemplate 的预期根元素的 XAML 内容属性应该支持集合,就像 Panel.Children 所做的那样。

【问题讨论】:

    标签: c# xaml windows-8 microsoft-metro


    【解决方案1】:

    因为 ItemsControl 使用 ItemsPanelTemplate 创建和使用 Panel 而不是 XAML 做任何事情。无论 ContentProperty 设置为什么,ItemsControl 都会简单地调用 Panel.Children.Add。

    Panel 应该只对子元素进行布局,而不是对它们进行样式设置。所以你必须做的只是覆盖 Arrange 和 Measure 方法。所有面板都只这样做。

    对于样式和任何其他逻辑,您必须使用另一种方法。

    【讨论】:

    • 这并不完全正确。查看RichTextColums 面板,它是您在 Visual Studio 中创建的每个 Metro 应用程序的一部分。
    • Panel 只更改布局,RichTextColumns 支持布局其子级,它进行分页、列布局。它所做的只是衡量和安排它的孩子。我将通过反射器查看源代码,但我很确定,面板不会添加/更改样式。它们可能会覆盖样式资源,但 Panel 的源代码不会包含任何样式代码。
    • 确实,这正是我想要实现的。我从来没有说过任何关于造型的事情。我只想为内容使用不同的属性(不是Children)。遗憾的是,ItemsControl 不支持指定的属性名称。
    • 那你为什么要创建属性并应用 ContentProperty 呢?为什么不直接覆盖 Measure 和 Arrange 方法并为面板的子项设置边界 (x,y,width,height)。面板已有名为“Children”的属性
    • 因为我希望面板创建 StackPanels 作为将插入内容的列。换句话说,内容元素不会是面板的直接子元素。这正是 RichTextColumns 面板的作用,但只针对一个元素而不是多个元素。
    【解决方案2】:

    我认为 ItemsPanelTemplate 只能以 Panel.Children 作为目标来布局项目。事实上,如果您从 ContentControl 派生您的 CustomPanel,您会在 Metro 风格应用中发现以下异常消息:

    Exception: The ItemsControl.ItemsPanelTemplate must have a derivative of Panel as the root element.
    

    您链接的文档可能是从 WPF 时间复制的文档错误,此时您仍然可以以编程方式将视觉效果插入视觉树。在 Metro 应用程序中,不再有办法在不使用 Panel.Children 的情况下将您自己的 UIElements 列表放入可视化树中。

    【讨论】:

      【解决方案3】:

      负责此任务的 Panel 的 GenerateChildren 方法看起来(如在 ILSpy 中看到的那样)

      internal virtual void GenerateChildren()
      {
          IItemContainerGenerator itemContainerGenerator = this._itemContainerGenerator;
          if (itemContainerGenerator != null)
          {
              using (itemContainerGenerator.StartAt(new GeneratorPosition(-1, 0), GeneratorDirection.Forward))
              {
                  UIElement uIElement;
                  while ((uIElement = (itemContainerGenerator.GenerateNext() as UIElement)) != null)
                  {
                      this._uiElementCollection.AddInternal(uIElement);
                      itemContainerGenerator.PrepareItemContainer(uIElement);
                  }
              }
          }
      }
      

      如您所见,它总是添加到 this._uiElementCollection,这是支持 Children 属性的字段。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2011-12-31
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-12-11
        相关资源
        最近更新 更多