简体   繁体   English

WPF确定用户在ItemsControl中单击的DataTemplate对象

[英]WPF Determining DataTemplate object a user clicked on in ItemsControl

Assume a simple MVVM Application implemented in WPF: 假设在WPF中实现了一个简单的MVVM应用程序:

There is a view with an ItemsControl that uses DataTemplate s to resolve a view for various ViewModels in a collection. 有一个带有ItemsControl的视图,该视图使用DataTemplate来解决集合中各种ViewModel的视图。

What I want to know is, how would I add functionality to allow me to click on a given ViewModel in my ItemsControl to return that element in the container ViewModel? 我想知道的是,如何添加功能以允许我单击ItemsControl的给定ViewModel以返回容器ViewModel中的该元素?

That is to say, for the given example, I may want be able to click on my WrapPanel and then from my BarnYardViewModel have the particular ViewModel from the ItemSource tag returned. 也就是说,对于给定的例子,我可能希望能够点击我的WrapPanel ,然后从我的BarnYardViewModel具有特定视图模型从ItemSource标签返回。 (much like binding the selected item in a ComboBox ) (很像将选定的项目绑定到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>

And a few basic ViewModels: 还有一些基本的ViewModel:

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

I would use a regular Button styled to have no UI, and would pass the ViewModel to it using the CommandParameter 我将使用样式为没有UI的常规Button,并使用CommandParameterViewModel传递给它

For example, 例如,

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

This will result in each item in your ItemsControl being rendered using a Button, and the Button will execute whatever command you specify and pass the current ViewModel in as the CommandParameter . 这将导致使用Button渲染ItemsControl中的每个项目,并且Button将执行您指定的任何命令,并将当前ViewModel作为CommandParameter传入。

In this case, I've specified that the Button.Template should be overwritten to use a ContentPresenter which has no UI, so basically it will be whatever your DataTemplate for each animal is. 在这种情况下,我指定应改写Button.Template以使用没有UI的ContentPresenter ,因此从根本Button.Template ,它将是每种动物的DataTemplate

There are some other solutions posted at this related WPF question too if you're interested: What is the best way to simulate a Click, with MouseUp & MouseDown events or otherwise? 如果您有兴趣,也可以在与此相关的WPF问题上发布其他一些解决方案: 用MouseUp和MouseDown事件或其他方式模拟Click的最佳方法是什么?

Any reason why it is a StackPanel yet you want something to know if it is Clicked ? 它为什么是StackPanel任何原因,但是您想知道是否Clicked它?

I suggest you to change it to a Button and change the Template for it then hookup the Command property to a property in your ViewModel then said the CommandParamter as the Type 我建议您将其更改为Button并为其更改Template ,然后将Command属性连接到ViewModel的属性,然后将CommandParamter称为Type

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM