Question

Assume a simple MVVM Application implemented in WPF:

There is a view with an ItemsControl that uses DataTemplates to resolve a view for various ViewModels in a collection.

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?

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. (much like binding the selected item in a 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:

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); }
        }
    }
}
Was it helpful?

Solution

I would use a regular Button styled to have no UI, and would pass the ViewModel to it using the CommandParameter

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.

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.

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?

OTHER TIPS

Any reason why it is a StackPanel yet you want something to know if it is 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

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top