2015-10-28 2 views
3

с использованием ItemsControl с WrapPanel установить как ItemsPanel, я пытаюсь добиться того, что показано на этом изображении: ItemsControl with WrapPanel as ItemsPanelItemsControl с 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 в ее решили для меня с двумя незначительными изменениями:

  1. Это не работает, если 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)
  2. Мне пришлось использовать «связующий прокси», чтобы получить 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)); 
} 

ответ

5

Вы можете сделать это в очень аккуратный способ, используя CompositeCollection

<ItemsControl> 
    <ItemsControl.Resources> 
     <CollectionViewSource x:Key="AnimalCollection" Source="{Binding Animals}"/> 
    </ItemsControl.Resources> 
    <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.ItemsSource> 
     <CompositeCollection> 
      <CollectionContainer Collection="{Binding Source={StaticResource AnimalCollection}}"/> 
      <Border Margin="5"> 
       <Button Command="{Binding AddAnimal}"> 
        <Image Source="YourAddButtonSource"/> 
       </Button> 
      </Border> 
     </CompositeCollection> 
    </ItemsControl.ItemsSource> 
</ItemsControl> 

Конечно, если вы хотите, чтобы кнопка добавления отображалась первой, просто поменяйте порядок Border (содержащий Button) с CollectionContainer в теге CompositeCollection.

0

Посмотрите на CompositeCollection. Он позволяет добавлять дополнительные предметы к связыванию ItemsSource:

<Window.Resources> 
    <CollectionViewSource x:Key="AnimalViewSource" Source="{Binding Animals}"/> 
</Window.Resources> 

<ItemsControl> 
    <ItemsControl.ItemsSource> 
     <CompositeCollection> 
      <local:Animal ImageUrl="somepath/plussign.png" /> 
      <CollectionContainer Collection="{Binding Source={StaticResource AnimalViewSource}}"/> 
     </CompositeCollection> 
    </ItemsControl.ItemsSource> 

    ... ItemsPanel, ItemsTemplate, etc. follow here ... 
</ItemsControl> 
Смежные вопросы