WPF 确定用户在 ItemsControl 中单击的数据模板对象

本文关键字:数据 对象 单击 用户 ItemsControl WPF | 更新日期: 2023-09-27 17:58:45

假设在WPF中实现一个简单的MVVM应用程序:

有一个视图具有ItemsControl,该视图使用 DataTemplate s 解析集合中各种 ViewModel 的视图。

我想知道的是,我将如何添加功能以允许我在ItemsControl中单击给定的 ViewModel,以在容器 ViewModel 中返回该元素?

也就是说,对于给定的示例,我可能希望能够单击我的WrapPanel,然后从我的BarnYardViewModel返回ItemSource标签中的特定 ViewModel。(很像在ComboBox中绑定所选项目(

<UserControl x:Class="AGI_ServiceTool.View.DataMonitorView"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
             xmlns:vm="clr-namespace:MyProject.ViewModel">
    <UserControl.Resources>
        <DataTemplate DataType="{x:Type vm:MooCowViewModel}">
            <StackPanel>
                <Image Source="/View/Images/MooCow.png"/>
                <label content="This is a Moo Cow"/>
            </StackPanel>
        </DataTemplate>
        <DataTemplate DataType="{x:Type vm:ChickenViewModel}">
            <StackPanel>
                <Image Source="/View/Images/Chicken.png"/>
                <label content="This is a Chicken"/>
            </StackPanel>
        </DataTemplate>
        <DataTemplate DataType="{x:Type vm:HorseViewModel}">
            <StackPanel>
                <Image Source="/View/Images/SarahJessicaParker.png"/>
                <label content="This is a Horse"/>
            </StackPanel>
        </DataTemplate>
    </UserControl.Resources>
    <Grid>
        <ItemsControl ItemsSource="{Binding MyAnimals}">
            <ItemsControl.ItemsPanel>
                <ItemsPanelTemplate>
                    <WrapPanel Orientation="Horizontal" ScrollViewer.CanContentScroll="True" HorizontalAlignment="Center" VerticalAlignment="Center"/>
                </ItemsPanelTemplate>
            </ItemsControl.ItemsPanel>
        </ItemsControl>
    </Grid>
</UserControl>

以及一些基本的视图模型:

namespace MyProject.ViewModel
{
    class AbstractViewModel : INotifyPropertyChanged { ... }
    class MooCowViewModel   : AbstractViewModel {}
    class ChickenViewModel  : AbstractViewModel {}
    class HorseViewModel    : AbstractViewModel {}
    class BarnYardViewModel : AbstractViewModel 
    {
        public BarnYardViewModel()
        {
            _myAnimals.add(new MooCowViewModel());
            _myAnimals.add(new ChickenViewModel());
            _myAnimals.add(new HorseViewModel());
        }
        private ObservableCollection<AbstractViewModel> _myAnimals = new ObservableCollection<AbstractViewModel>();
        public ICollectionView MyAnimals{
            get { return System.Windows.Data.CollectionViewSource.GetDefaultView(_myAnimals); }
        }
    }
}

WPF 确定用户在 ItemsControl 中单击的数据模板对象

我会使用一个没有UI的常规按钮,并使用CommandParameterViewModel传递给它

例如

<ControlTemplate x:Key="ContentOnlyTemplate" TargetType="{x:Type Button}">
    <ContentPresenter />
</ControlTemplate>
... 
<ItemsControl.ItemTemplate>
    <DataTemplate>
        <Button Command="{Binding ElementName=MyItemsControl, Path=DataContext.MyClickCommand}"
                CommandParameter="{Binding }"
                Template="{StaticResource ContentOnlyTemplate}"
                Content="{Binding }" />
    </ItemsPanelTemplate>
</ItemsControl.ItemTemplate>

这将导致ItemsControl中的每个项目都使用按钮进行渲染,并且按钮将执行您指定的任何命令,并将当前 ViewModel 作为CommandParameter传入。

在这种情况下,我指定应覆盖Button.Template以使用没有 UI 的ContentPresenter,因此基本上它将是您对每种动物的DataTemplate

如果您有兴趣,在这个相关的 WPF 问题中也发布了一些其他解决方案:模拟单击、MouseUp 和 MouseDown 事件或其他方式的最佳方法是什么?

为什么

它是一个StackPanel但你想知道它是否Clicked

我建议您将其更改为Button并更改其Template,然后将Command属性连接到ViewModel中的属性,然后将CommandParamter称为Type