ItemsPanelTemplate в XAML игнорирует атрибут [ContentProperty]
У меня есть пользовательская панель, где я объявила пользовательское свойство для хранения содержимого (я не хочу использовать 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>
Но когда я хочу использовать панель в качестве элемента ItemsPanelTemplate внутри ItemsControl, атрибут ContentProperty игнорируется и добавляет все в коллекцию Дети, а не в коллекцию PanelContent:
<ItemsControl ItemTemplate="{StaticResource ReviewTemplate}" ItemsSource="{Binding Reviews}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<CustomPanel></CustomPanel>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
Это не так, как должно работать. Согласно документации:
Элемент объекта ItemsPanelTemplate должен содержать ровно один класс, основанный на FrameworkElement, который служит в качестве корневого элемента для элементов. В большинстве случаев это класс, созданный Panel. Расширенный шаблон служит в качестве родителя для реализованных элементов, и в целом имеется более одного элемента. Поэтому свойство содержимого XAML для предполагаемого корневого элемента элемента ItemsPanelTemplate должно поддерживать коллекцию, как это делает Panel.Children.
Ответы
Ответ 1
Метод GenerateChildren Panel, который отвечает за эту задачу, выглядит (как показано в 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);
}
}
}
}
Как вы можете видеть, он всегда добавляет к этому._uiElementCollection, который является полем, поддерживающим свойство Children.
Ответ 2
Я думаю, что ItemsPanelTemplate может принимать только Panel.Children как цель для элементов макета. Фактически, если вы выберете свой CustomPanel, скажем, ContentControl, вы найдете следующее сообщение об исключении в стиле Metro:
Exception: The ItemsControl.ItemsPanelTemplate must have a derivative of Panel as the root element.
Связанная с документацией документа может быть ошибкой документации, которая была скопирована из времени WPF, когда вы все еще можете программно вставлять визуальные изображения в визуальное дерево. В приложении Metro больше нет способа разместить собственный список UIElements в визуальное дерево без использования Panel.Children.
Ответ 3
Поскольку ItemsPanelTemplate используется ItemsControl для создания и использования Panel, и это не XAML, который что-то делает. ItemsControl просто вызовет Panel.Children.Add независимо от того, для чего установлен ContentProperty.
Предполагается, что панель предназначена только для размещения детей и никогда не стирает их. Таким образом, все, что вы должны сделать os, только переопределяет методы Arrange and Measure. Все панели только делают это.
Для стилизации и любой другой логики вы должны использовать другой подход.