简体   繁体   English

WPF 自定义控件 - 如何正确循环并显示子内容

[英]WPF Custom Control - How to properly loop through and display child content

Attempting to write a custom control which permits a collection of children content.试图编写一个自定义控件来允许子内容的集合。 The end goal is to have some functional filtering of the child elements based on custom attached dependency properties;最终目标是根据自定义附加依赖属性对子元素进行一些功能过滤; however, before getting that far, any attempts I've made to collect and re-display child UIElements have ended in exceptions during XAML parsing/display.然而,在此之前,我为收集和重新显示子 UIElements 所做的任何尝试都以在 XAML 解析/显示期间出现异常而告终。

I have a class CustomFilter : Control which has an associated default style in a nearby xaml file.我有一个类CustomFilter : Control在附近的 xaml 文件中具有关联的默认样式。 It has a dependency property for Children, which currently collect the child elements in a UIElementCollection.它具有 Children 的依赖属性,该属性当前收集 UIElementCollection 中的子元素。

I believe my problem may be how I am attempting to render the collection of children.我相信我的问题可能是我试图呈现孩子的集合。 The only way I know of looping through content in XAML involves using an ItemsControl, to which I am passing my Children collection as the ItemsSource, which feels rather backwards (using UIElements as data models?)我知道的在 XAML 中循环内容的唯一方法涉及使用 ItemsControl,我将我的 Children 集合作为 ItemsSource 传递给它,这感觉相当落后(使用 UIElements 作为数据模型?)

Afterwards, we attempt to render the elements via a content presenter inside of the ItemTemplate.之后,我们尝试通过 ItemTemplate 内的内容展示器呈现元素。 I've seen in many other examples which render control children using content presenters, so I expect this is probably at least partially correct (though all only using single elements).我已经在许多其他示例中看到使用内容展示器呈现控件子级,所以我希望这可能至少部分正确(尽管所有都只使用单个元素)。

The sample class样本类

  [ContentProperty(nameof(Children))]
  public class CustomFilter : Control
  {
    static CustomFilter()
    {
      DefaultStyleKeyProperty.OverrideMetadata(typeof(CustomFilter), new FrameworkPropertyMetadata(typeof(CustomFilter)));
    }

    public CustomFilter()
    {
      Children = new UIElementCollection(this, this);
    }

    public static readonly DependencyProperty ChildrenProperty = DependencyProperty.Register(nameof(Children), typeof(UIElementCollection), typeof(CustomFilter), new FrameworkPropertyMetadata(RefreshFilter));
    [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
    public UIElementCollection Children
    {
      get { return (UIElementCollection)GetValue(ChildrenProperty); }
      private set { SetValue(ChildrenProperty, value); }
    }
  }

The sample template示例模板

  <Style TargetType="{x:Type local:CustomFilter}">
    <Setter Property="Template">
      <Setter.Value>
        <ControlTemplate TargetType="{x:Type local:CustomFilter}">
          <StackPanel>
            <ItemsControl ItemsSource="{TemplateBinding Children}">
              <ItemsControl.ItemTemplate>
                <DataTemplate>
                  <StackPanel>
                    <ContentPresenter Content="{Binding}"/>
                  </StackPanel>
                </DataTemplate>
              </ItemsControl.ItemTemplate>
            </ItemsControl>
          </StackPanel>
        </ControlTemplate>
      </Setter.Value>
    </Setter>
  </Style>

The use case用例

  <local:CustomFilter>
    <TextBlock Text="X"/>
    <TextBlock Text="Y"/>
    <TextBlock Text="Z"/>
  </local:CustomFilter>

Attempting to run use this control whatsoever causes it to fail when it reaches the ItemsControl, without reaching the ItemTemplate, with the following exception:尝试运行使用此控件会导致它在到达 ItemsControl 时失败,而不会到达 ItemTemplate,但以下情况除外:

ArgumentException: Specified Visual is already a child of another Visual or the root of a CompositionTarget.

I've attempted to create a wrapper class for the Children, passing one child to each instance of the wrapper, and binding to that as an ItemsSource - it successfully loops through the ItemTemplate, but attempting to use the ContentPresenter there on the wrapped child element provides me with the following exception instead:我试图为 Children 创建一个包装类,将一个孩子传递给包装的每个实例,并将其作为 ItemsSource 绑定到它 - 它成功地循环遍历 ItemTemplate,但尝试在包装的子元素上使用 ContentPresenter为我提供以下异常:

ArgumentException: Must disconnect specified child from current parent Visual before attaching to new parent Visual.

Why are you using an UIElementCollection instead of a List<UIElement> ?为什么要使用UIElementCollection而不是List<UIElement> You are not supposed to bind to an UIElementCollection in a template.您不应该绑定到模板中的UIElementCollection

Your example should work if you define a List<UIElement> dependency property:如果您定义List<UIElement>依赖项属性,您的示例应该可以工作:

[ContentProperty(nameof(Children))]
public class CustomFilter : Control
{
    static CustomFilter()
    {
        DefaultStyleKeyProperty.OverrideMetadata(typeof(CustomFilter), new FrameworkPropertyMetadata(typeof(CustomFilter)));
    }

    public CustomFilter()
    {
        SetValue(ChildrenPropertyKey, new List<UIElement>());
    }

    private static readonly DependencyPropertyKey ChildrenPropertyKey =
            DependencyProperty.RegisterReadOnly(
              nameof(Children),
              typeof(List<UIElement>),
              typeof(CustomFilter),
              new FrameworkPropertyMetadata(new List<UIElement>())
            );
    public static readonly DependencyProperty ChildrenProperty =
        ChildrenPropertyKey.DependencyProperty;

    public List<UIElement> Children
    {
        get { return (List<UIElement>)GetValue(ChildrenProperty); }
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM