В WPF как я могу ограничить тип детей в Panel? - PullRequest
3 голосов
/ 15 февраля 2011

Мы хотим создать подкласс Canvas, который позволяет только дочерним элементам определенного типа (они должны иметь глубокие знания нашего подкласса и наоборот.) Тем не менее, есть ли способ заставить панель принимать только дочерниеопределенного типа (или типов)?

M

Ответы [ 2 ]

2 голосов
/ 30 марта 2011

Решение, которое мы придумали, состояло в том, чтобы просто создать подкласс Canvas, а затем следить за детьми.Если добавляется не тот тип, который нам нужен, мы немедленно удаляем его и выдаем ошибку.Не остановит ошибки во время компиляции, но сделает свое дело.

Расширяя это далее, я думал также о создании подкласса холста, а затем обмениваясь свойством Children для возврата нашей собственной коллекции, которую мы внутренне синхронизировали сдети панели через связывание.Таким образом, мы также можем получить поддержку во время компиляции.Конечно, если кто-то преобразует наш подкласс в прямой холст, то очевидно, что свойство 'new'd Children' не будет доступно (это 'new', а не переопределение), но вышеупомянутый мониторинг коллекции все равно даст нам то, что мы хотим.

Было бы было бы хорошо, если бы команда WPF придумала общий холст, чтобы мы могли сделать что-то вроде холста, но это явно не сработало бы в XAML, если бы они как-то не придумали синтаксис длятот.Опять же, холст чертовски простой, так что, может быть, мы просто выпустим нашу собственную оригинальную версию, где мы могли бы сделать что-то вроде этого ...

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

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

... из которых мы могли бы , затем используйте FooCanvas и LaaCanvas через XAML, при этом все еще получая все преимущества использования дженериков.

Еще лучше, сделайте его TypedPanelBase, чтобы мы могли использовать его с любой другой пользовательской панелью в качестве базового типа.

На самом деле, теперь, когда я набрал это ... Я думаю, что я собираюсь переписать наш холст, чтобы попробовать этот подход!(В любом случае, теперь у меня есть решение, к которому мы стремились.)

0 голосов
/ 15 февраля 2011

На самом деле ... ни за что.

Кроме того, я не понимаю ваших целей. Если вам нужно работать с некоторыми конкретными контейнерами, просто приведите Panel.InternalChildren:

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

Рассмотрим сценарий: у вас есть коллекция строк, которая является источником ItemsControl. В DataTemplate у нас есть кнопка, содержимое которой привязано к элементу из упомянутой коллекции. И ItemsControl.ItemsPanel - это 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>

Итак, какие типы предметов вы хотите ограничить? Кнопки или строки? Проблема в этом сценарии заключается в том, что ContentPresenters будут эффективными визуальными потомками Canvas. Но в переопределенном методе OnVisualChildrenChanged (где вы можете попытаться проверить тип элемента) свойства Content и ContentTemplate устанавливаются в null из-за отложенной привязки.

Таким образом, единственное приемлемое решение, которое я могу предложить, - это создать свой собственный ItemsControl, который возвращает некоторый конкретный контейнер вместо 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>

При таком подходе вы гарантируете, что ваши контейнеры предметов (Panel.InternalChilder) являются кнопками (или чем-то еще), а в MyPanel вы можете безопасно разыграть:

this.InternalChildren.Cast<Button>()
...