wpf 以WrapPanel做为ItemsPanel的ItemsControl-合并“静态”子系和ItemsSource

0yycz8jy  于 2022-11-18  发布在  其他
关注(0)|答案(3)|浏览(172)

使用ItemsControl并将WrapPanel设置为ItemsPanel,我尝试实现下图所示的功能:

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个动物。

**问题:**如何添加一个“static”子元素,使其看起来像其他元素,是WrapPanel的子元素,并与其他子元素一起 Package ?具体来说,我希望最后一个元素是一个add-button(上图中显示的绿色加号),它绑定到Zoo的AddAnimal Command属性。
要求:

  • add-button-element必须与WrapPanel的其他子元素 Package 在一起。
  • add-button-element必须始终是最后一个(或第一个)元素,并且如果Zoo中没有Animals,则也应该可见。
  • 在Animals-collection中添加一个虚拟的Animal,然后使用样式修改最后一个子对象,这是不可行的。底层模型在应用程序中的很多地方都被使用,因此在Animals-collection中浮动一个虚拟的Animal太过笨拙。
    我考虑过:
  • 使用一个转换器将Animals-Collection复制到一个新的集合中,向副本中添加一个虚拟Animal,并将其返回到ItemsControl的ItemsSource绑定。然后,我可以将最后一个元素的样式设置为add-button。但是,这不是一个选项,因为某些拖放逻辑需要能够通过ItemsControl的ItemsSource属性在原始ObservableCollection上工作。
  • 对WrapPanel进行子类化并添加add-button-element作为子元素,而不修改ItemsSource属性似乎是最好的选择(如果可能的话),但我不知道如何做到这一点。
    **其他信息:**我用途:WPF、PRISM、C# 6.0、.NET 4.0客户端配置文件和MVVM模式。

对这个问题有什么想法吗?

解决方案:

@kyriacos_k的答案为我解决了两个小修改:
1.如果DataTemplate是通过ItemsControl.ItemTemplate属性设置的,则它不起作用,也就是说,Add按钮将拾取DataTemplate并错误地显示。我猜这是设计使然,正如MSDN所述:“ItemsControl使用CompositeCollection中的数据根据其ItemTemplate生成其内容”,源:(第10页)
1.我不得不使用“绑定代理”来使AddAnimal命令绑定工作。“直接”绑定语法或相对源都不起作用。我猜这是因为添加按钮不是同一个可视树的一部分,并且不知何故,它没有从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的代码如下所示(直接取自: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));
}
camsedfj

camsedfj1#

您可以使用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>

当然,如果希望add按钮首先出现,只需交换CompositeCollection标记中Border(包含Button)与CollectionContainer的顺序。

eh57zj3b

eh57zj3b2#

看一下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>
ua4mk5z4

ua4mk5z43#

ItemsControl.ItemsPanel替换为

<ItemsControl.ItemsPanel>
      <ItemsPanelTemplate>
        <UniformGrid Columns="3" />
      </ItemsPanelTemplate>
 </ItemsControl.ItemsPanel>

其中3是每行的元素数

相关问题