You could implement an attached behaviour that hooks up the PreviewMouseLeftButtonDown of all UIElements in your source collection to a command of the view model:
public class MyBehavior
{
public static readonly DependencyProperty MouseLeftButtonDownCommandProperty
= DependencyProperty.RegisterAttached("MouseLeftButtonDownCommand",
typeof(ICommand), typeof(MyBehavior), new FrameworkPropertyMetadata(new PropertyChangedCallback(OnMouseLeftButtonDownCommandPropertyChanged)));
public static void SetMouseLeftButtonDownCommand(UIElement element, ICommand value)
{
element.SetValue(MouseLeftButtonDownCommandProperty, value);
}
public static ICommand GetMouseLeftButtonDownCommand(UIElement element)
{
return (ICommand)element.GetValue(MouseLeftButtonDownCommandProperty);
}
private static void OnMouseLeftButtonDownCommandPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
UIElement element = d as UIElement;
element.PreviewMouseLeftButtonDown += Element_MouseLeftButtonDown;
}
private static void Element_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
UIElement element = sender as UIElement;
ICommand command = GetMouseLeftButtonDownCommand(element);
if (command != null)
command.Execute(element);
}
}
View:
<Window x:Class="WpfApplication1.Window6"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WpfApplication1"
mc:Ignorable="d"
Title="Window6" Height="300" Width="300">
<Window.DataContext>
<local:ViewModel />
</Window.DataContext>
<Grid>
<ItemsControl ItemsSource="{Binding UiElementsCollection}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas HorizontalAlignment="Left" VerticalAlignment="Top">
</Canvas>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemContainerStyle>
<Style>
<Setter Property="local:MyBehavior.MouseLeftButtonDownCommand"
Value="{Binding DataContext.TheCommand, RelativeSource={RelativeSource AncestorType=Window}}" />
</Style>
</ItemsControl.ItemContainerStyle>
</ItemsControl>
</Grid>
</Window>
View Model:
public class ViewModel
{
public ObservableCollection<UIElement> UiElementsCollection { get; } = new ObservableCollection<UIElement>()
{
new Button { Content = "btn" },
new Border { Background = Brushes.Yellow, Width = 10, Height = 10 }
};
public ICommand TheCommand { get; } = new DelegateCommand<UIElement>(element =>
{
MessageBox.Show(element.GetType().ToString());
});
}
You will obviously need an implementation of the ICommand interface. The DelegateCommand class is available in Prism: https://github.com/PrismLibrary/Prism/blob/master/Source/Prism/Commands/DelegateCommand.cs
Commands are pretty essential to an MVVM application though so you probably knew this already. You could refer to the following blog post for more information about how to handle events using commands in MVVM: https://blog.magnusmontin.net/2013/06/30/handling-events-in-an-mvvm-wpf-application/