簡體   English   中英

ItemsControl與WrapPanel一起作為ItemsPanel - 組合一個“靜態”子項和ItemsSource

[英]ItemsControl with WrapPanel as ItemsPanel - combine a “static” child and ItemsSource

使用帶有WrapPanel設置為ItemsPanel的ItemsControl,我試圖實現此圖像中說明的內容: 使用WrapPanel作為ItemsPanel的ItemsControl

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; }
}

ItemsControl的DataContext設置為Zoo的一個實例,並填充4個Animals。

問題:如何添加一個看起來像其他元素的“靜態”子元素,是WrapPanel的子元素,並與其他子元素包裝? 具體來說,我希望最后一個元素是一個添加按鈕(上圖中顯示的綠色加號),它綁定到Zoo的AddAnimal Command屬性。

要求:

  • add-button-element必須與WrapPanel的其他子項一起包裝。
  • add-button-element必須始終是最后一個(或第一個)元素,如果Zoo中沒有Animals,也應該是可見的。
  • 將一個虛擬Animal添加到Animals集合中然后使用樣式來修改最后一個子節點不是一個選項。 底層模型在應用程序中使用了很多地方,因此在動物集合中放置一個虛擬動物太過於愚蠢。

我考慮過:

  • 使用將Animals-collection復制到新集合的轉換器,將一個虛擬Animal添加到副本,然后將其返回到ItemsControl的ItemsSource綁定。 然后我可以將最后一個元素設置為添加按鈕。 但是,這不是一個選項,因為一些拖放邏輯需要能夠通過ItemsControl的ItemsSource屬性處理原始的ObservableCollection。
  • 對WrapPanel進行子類化並將add-button-element添加為子元素而不修改ItemsSource屬性似乎是最好的選擇(如果可能的話),但我無法弄清楚如何做到這一點。

附加信息:我使用:WPF,PRISM,C#6.0,.NET 4.0 Client Profile和MVVM模式。

有關這個問題的任何想法?

解:

@ kyriacos_k的答案為我解決了兩個小修改:

  1. 如果DataTemplate是通過ItemsControl.ItemTemplate屬性設置的,那么它不起作用,即Add-button會拾取DataTemplate並顯示錯誤。 我想這是設計的,因為MSDN聲明:“ItemsControl使用CompositeCollection中的數據根據​​其ItemTemplate生成其內容”,來源: https//msdn.microsoft.com/en-us/library/system。 windows.data.compositecollection%28v = vs.110%29.aspx
  2. 我不得不使用“綁定代理”來使AddAnimal Command綁定工作。 “直接”綁定語法或相關源都不起作用。 我想這是因為添加按鈕不是同一個可視樹的一部分,並且它不會從ItemsControl中拾取DataContext。

最后這對我有用:

<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的代碼在這里(直接從WPF中的DataGridColumn的綁定可見性中獲取 ):

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));
}

您可以使用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 )的順序與CompositeCollection標記中的CollectionContainer進行交換。

看看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>

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM