在WPF中,如何限制面板中子级的类型?

 2021-04-27 

In WPF how can I restrict the type of children in a Panel?

我们想创建一个Canvas子类,该子类仅允许特定类型的子类(他们需要对我们的子类有深入的了解,反之亦然。)也就是说,有什么方法可以强制面板仅接受子类?某种类型(或几种)的类型?

M


我们想出的解决方案是简单地将Canvas子类化,然后监视孩子。如果添加的不是我们想要的类型,我们会立即将其删除并抛出错误。不会停止编译时错误,但是可以解决问题。

进一步扩展我的想法是,我还考虑了画布的子类化,然后是Children属性的Newing以返回我们自己的集合,我们已通过绑定将其内部同步到面板的子代。这样,我们还可以获得编译时支持。如果有人将我们的子类转换为纯画布,则很显然,'new'd Children属性将不会被访问(它是一个'new',而不是替代项),但是前面提到的集合监视仍将提供我们想要的东西。 >

如果WPF团队提出了一个通用画布,那就太好了,所以我们可以做画布之类的事情,但是除非他们以某种方式提出了语法,否则在XAML中这显然是行不通的。再说一遍,画布是非常不可思议的基础,所以也许我们只需要滚动我们自己的geeneric版本,就可以做这样的事情...

1
2
3
4
5
6
7
public class TypedCanvas< T > : PanelBase
{
    // Implementation here
}

public class FooCanvas : TypedCanvas<Foo>{}
public class LaaCanvas : TypedCanvas<Laa>{}

...然后我们可以通过XAML使用FooCanvas和LaaCanvas,同时仍然获得使用泛型的所有好处。

更好的是,将其设置为TypedPanelBase,以便我们可以将其与其他任何自定义面板一起用作基本类型。

实际上,现在我已经输入了这个……我想我将重新编写画布以尝试这种方法! (无论哪种方式,我现在都有一个解决方案,这正是我们所追求的。)


实际上...没办法。

此外,我不了解您的目标。如果您需要使用某些特定的容器,则只需转换Panel.InternalChildren:

1
this.InternalChildren.OfType<MyType>().Do(...);

考虑场景:您有一个字符串集合,这是ItemsControl的源。在DataTemplate中,我们有一个按钮,该按钮将内容绑定到提到的集合中的项目。而且ItemsControl.ItemsPanel是Canvas。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public IEnumerable<string> Items
{
    get;
}

<ItemsControl ItemsSource="{Binding Items}">
    <ItemsControl.ItemTemplate>
        <DataTemplate>
            <Button Content="{Binding}"/>
        </DataTemplate>
    </ItemsControl.ItemTemplate>
    <ItemsControl.ItemsPanel>
        <ItemsPanelTemplate>
            <Canvas/>
        </ItemsPanelTemplate>
    </ItemsControl.ItemsPanel>
</ItemsControl>

那么,您想限制哪些项目类型?按钮或字符串?
在这种情况下,问题在于ContentPresenters将成为Canvas的有效视觉子级。但是在重写的方法OnVisualChildrenChanged(您可以尝试在其中检查项目类型)中,由于延迟绑定,Content和ContentTemplate属性设置为null。

所以我可以建议的一个可接受的解决方案是创建自己的ItemsControl,它返回一些具体的容器而不是ContentPresenter:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class MyItemsControl : ItemsControl
{
    protected override DependencyObject GetContainerForItemOverride()
    {
        return new Button();
    }

    protected override bool IsItemItsOwnContainerOverride(object item)
    {
        return item is Button;
    }
}


    <self:MyItemsControl ItemsSource="{Binding Items}">
        <ItemsControl.ItemsPanel>
            <ItemsPanelTemplate>
                <self:MyPanel/>
            </ItemsPanelTemplate>
        </ItemsControl.ItemsPanel>
    </self:MyItemsControl>

通过这种方法,您可以确保项目容器(Panel.InternalChilder)是按钮(或其他东西),并且在MyPanel中可以安全地投射:

1
this.InternalChildren.Cast<Button>()