C# - Click event on multiple UIElement types collection

146 views Asked by At

I'm displaying a collection of UI elements of different types (Rectangles and Images) in the canvas. Both derive from the UIElement type.

<ItemsControl ItemsSource="{Binding UiElementsCollection}">
    <ItemsControl.ItemsPanel>
        <ItemsPanelTemplate>
            <Canvas HorizontalAlignment="Left" VerticalAlignment="Top">
            </Canvas>
        </ItemsPanelTemplate>
    </ItemsControl.ItemsPanel>
</ItemsControl>

Everything displays fine, but I want to have an event trigger to mouse events - when I click on/drag the specific element over the canvas I would like to receive this object (Rectangle or Image). I would like to do it the MVVM pattern. How can I do it?

1

There are 1 answers

0
mm8 On

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/