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集合复制到新集合的转换器,向副本添加一个虚拟Animal,并将其返回到ItemsControl的ItemsSource绑定.然后我可以将最后一个元素设置为添加按钮.但是,这不是一个选项,因为一些拖放逻辑需要能够通过ItemsControl的ItemsSource属性处理原始的ObservableCollection.
>对WrapPanel进行子类化并将add-button-element添加为子元素而不修改ItemsSource属性似乎是最佳选择(如果可能),但我无法弄清楚如何做到这一点.
附加信息:我使用:WPF,PRISM,C#6.0,.NET 4.0 Client Profile和MVVM模式.
有关这个问题的任何想法?
解:
>如果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)
>我不得不使用“绑定代理”来使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的代码在这里(直接从: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)); }
解决方法
<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进行交换.
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。