с использованием ItemsControl с WrapPanel установить как ItemsPanel, я пытаюсь добиться того, что показано на этом изображении: ItemsControl с WrapPanel как ItemsPanel - объединить «статический» ребенок и ItemsSource
Часть XAML выглядит это (изменен, чтобы сделать его более простым):
<ItemsControl ItemsSource="{Binding Animals}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel IsItemsHost="True" Orientation="Horizontal" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Border Margin="5">
<Image Source="{Binding ImageUrl}" />
</Border>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
Основополагающий ViewModel выглядит следующим образом:
public class Zoo
{
public ObservableCollection<Animal> Animals { get; set; } = new ObservableCollection<Animal>();
public ICommand AddAnimal() => new DelegateCommand(() => Animals.Add(new Animal()));
}
public class Animal
{
public string ImageUrl { get; set; }
}
DataContext ItemsControl установлен в экземпляр Zoo и заполнен 4 животными.
Вопрос: Как я могу добавить «статический» дочерний элемент, который выглядит, как и другие элементы, является дочерним WrapPanel и оборачивает с другими детьми? В частности, я хочу, чтобы последний элемент был добавочной кнопкой (зеленый знак плюс, показанный на изображении выше), который связан с свойством AddAnimal Command для Zoo.
Требование:
- Надстройка пуговицы элемент должен обернуть с другими детьми в WrapPanel.
- Элемент add-button всегда должен быть последним (или первым) элементом, а также должен быть видимым, если в Зоопарке нет животных.
- Добавление фиктивного животного в коллекцию Animals, а затем использование стиля для изменения последнего ребенка не является вариантом. В базовой модели используется множество мест в приложении, и поэтому слишком хаски, чтобы иметь фиктивный живот, плавающий вокруг в коллекции Animals.
Я подумал о:
- Используя конвертер, который копирует Animals-коллекция в новую коллекцию, добавляет фиктивное животное в копию, и возвращает его к связыванию ItemsSource в ItemsControl в. Затем я мог бы создать последний элемент в качестве кнопки добавления. Однако это не вариант, так как некоторые логики перетаскивания должны иметь возможность работать с исходным ObservableCollection с помощью свойства ItemsSource ItemsControl.
- Подкласс WrapPanel и добавление элемента add-button в качестве дочернего элемента без изменения свойства ItemsSource кажется лучшим вариантом (если возможно), но я не могу понять, как это сделать.
Дополнительная информация: Я использую: WPF, PRISM, C# 6.0, .NET 4.0 профиля клиента и шаблон MVVM.
Любые мысли по этой проблеме?
Решение:
@ ответы kyriacos_k в ее решили для меня с двумя незначительными изменениями:
- Это не работает, если DataTemplate был установлен с помощью ItemsControl.ItemTemplate собственности, т.е. Добавить -button подберет DataTemplate и отобразится неправильно.Я предполагаю, что это по дизайну, поскольку MSDN заявляет: «Элемент ItemsControl использует данные в CompositeCollection для генерации своего содержимого в соответствии с его ItemTemplate», source: https://msdn.microsoft.com/en-us/library/system.windows.data.compositecollection%28v=vs.110%29.aspx)
- Мне пришлось использовать «связующий прокси», чтобы получить AddAnimal Командная привязка к работе. Ни «прямой» синтаксис связывания, ни относительный источник не работал. Я думаю, это потому, что кнопка добавления не является частью одного и того же визуального дерева, и каким-то образом она не захватывает DataContext из ItemsControl.
В конце концов, это то, что работает для меня:
<ItemsControl>
<ItemsControl.Resources>
<CollectionViewSource x:Key="AnimalCollection" Source="{Binding Animals}"/>
<behaviors:BindingProxy x:Key="Proxy" DataContext="{Binding}"/>
<DataTemplate DataType="{x:Type local:Animal}">
<Border Margin="5">
<Image Source="{Binding ImageUrl}" />
</Border>
</DataTemplate>
</ItemsControl.Resources>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel IsItemsHost="True" Orientation="Horizontal" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemsSource>
<CompositeCollection>
<CollectionContainer Collection="{Binding Source={StaticResource AnimalCollection}}"/>
<Border Margin="5">
<Button Command="{Binding DataContext.AddAnimal, Source={StaticResource Proxy}}">
<Image Source="SourceToPlusSign"/>
</Button>
</Border>
</CompositeCollection>
</ItemsControl.ItemsSource>
</ItemsControl>
Код для BindingProxy здесь (вырвали прямо из: Binding Visibility for DataGridColumn in WPF):
public class BindingProxy : Freezable
{
protected override Freezable CreateInstanceCore()
{
return new BindingProxy();
}
public object DataContext
{
get { return GetValue(DataContextProperty); }
set { SetValue(DataContextProperty, value); }
}
public static readonly DependencyProperty DataContextProperty =
DependencyProperty.Register("DataContext", typeof(object),
typeof(BindingProxy));
}