开发者

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

开发者 https://www.devze.com 2023-02-10 07:00 出处:网络
We want to create a subclass of Canvas that only allows children of a specific type (they need to have intimate knowledge of开发者_运维技巧 our subclass and vice-versa.)That said, is there any way to

We want to create a subclass of Canvas that only allows children of a specific type (they need to have intimate knowledge of开发者_运维技巧 our subclass and vice-versa.) That said, is there any way to force a panel to only accept children of a certain type (or types)?

M


The solution we came up with was to simply subclass the Canvas, then monitor the children. If one is added that's not of the type we want, we instantly remove it and throw an error. Won't stop compile-time errors but does the trick.

Extending this further I was thinking about also subclassing the canvas, then Newing over the Children property to return our own collection which we've internally synced to the panel's children via binding. That way we can also have compile-time support. Granted if someone casts our subclass to a straight canvas, then obviously the 'new'd Children property won't be accessed (its a 'new' not an override) but the aforementioned collection monitoring will still give us what we want.

It would have been nice if the WPF team had come up with a generic canvas so we could do something like canvas but that obviously wouldn't work in XAML unless they somehow came up with syntax for that. Then again, a canvas is pretty damn basic so maybe we'll just roll our own geeneric version where we could do something like this...

public class TypedCanvas<t> : PanelBase
{
    // Implementation here
}

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

...of which we could then use FooCanvas and LaaCanvas via XAML while still getting all the benefits of using generics.

Even better, make it TypedPanelBase so we could use it with any other custom panel as the base type.

Actually, now that I've typed this... I think I'm about to go re-write our canvas to try this approach! (Either way, I now have a solution which is what we were after.)


Actually... no way.

Besides, I don't understand your goals. If you need to work with some specific containers just cast Panel.InternalChildren:

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

Consider about scenario: you have a collection of strings, which is the source for ItemsControl. In DataTemplate we have button which content is binded to item from mentioned collection. And ItemsControl.ItemsPanel is Canvas.

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>

So, what items types do you want to restrict? Buttons or strings? The problem in this scenario is that ContentPresenters will be effective visual children of Canvas. But in overriden method OnVisualChildrenChanged (where you could try to check item type) Content and ContentTemplate properties are set to null due to deferred binding.

So the one acceptable solution I can propose is creating your own ItemsControl, which returns some concrete container instead of ContentPresenter:

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>

With this approach, you guarantee that your item containers (Panel.InternalChilder) are buttons (or something) and in MyPanel you could safely cast:

this.InternalChildren.Cast<Button>()
0

精彩评论

暂无评论...
验证码 换一张
取 消