Search code examples
c#wpf

WPF MouseBinding inside of Grid


Hi I'm new to WPF development. I have a Grid with a few columns and need to handle clicks on one column cells. Here template of this column:

`<DataGridTemplateColumn Width="10*">
                        <DataGridTemplateColumn.CellTemplate>
                            <DataTemplate>
                                <Grid Background="{Binding BackgroundColor}" UIElement.MouseRightButtonDown="Grid_MouseRightButtonDown">
                                    <Grid.InputBindings>
                                        <MouseBinding MouseAction="RightClick" Command="{Binding RightClickCommand}"/>
                                        <MouseBinding MouseAction="LeftClick" Command="{Binding LeftClickCommand}" CommandParameter="{Binding Property1}"/>
                                    </Grid.InputBindings>

                                    <Grid.ColumnDefinitions>
                                        <ColumnDefinition Width="7*"></ColumnDefinition>
                                        <ColumnDefinition Width="3*"></ColumnDefinition>
                                    </Grid.ColumnDefinitions>
                                    <Rectangle>
                                        <Rectangle.Fill>
                                            <LinearGradientBrush StartPoint="0,0" EndPoint="1,0">
                                                <GradientStop Color="Orange" Offset="0.0" />
                                                <GradientStop Color="Orange" Offset="{Binding Property2Offset}" />
                                                <GradientStop Color="{Binding BackgroundColor}" Offset="{Binding Property2Offset}" />
                                                <GradientStop Color="{Binding BackgroundColor}" Offset="1.00" />
                                            </LinearGradientBrush>
                                        </Rectangle.Fill>
                                    </Rectangle>
                                    <TextBlock Grid.Column="0" Text="{Binding Property2}" HorizontalAlignment="Left" PreviewMouseRightButtonDown="DataGridTemplateColumn_MouseRightButtonDown">
                                        <TextBlock.InputBindings>
                                            <MouseBinding MouseAction="RightClick" Command="{Binding RightClickCommand}"/>
                                            <MouseBinding MouseAction="LeftClick" Command="{Binding LeftClickCommand}" CommandParameter="{Binding Property1}"/>
                                        </TextBlock.InputBindings>
                                    </TextBlock>
                                    <TextBlock Grid.Column="1" Text="{Binding Property1}" HorizontalAlignment="Left">
                                        <TextBlock.InputBindings>
                                            <MouseBinding MouseAction="RightClick" Command="{Binding RightClickCommand}"/>
                                            <MouseBinding MouseAction="LeftClick" Command="{Binding LeftClickCommand}" CommandParameter="{Binding Property1}"/>
                                        </TextBlock.InputBindings>
                                    </TextBlock>
                                </Grid>                                
                            </DataTemplate>
                        </DataGridTemplateColumn.CellTemplate>
                    </DataGridTemplateColumn>`

Could you please clarify how can I triger command for the concrete cell and pass needed parameters? Or I just able to handle click on the whole Grid?

If I set TextBlock with binding outside the Grid commands work correctly.

`<TextBlock Grid.Column="1" Text="Test text" HorizontalAlignment="Left">
                                        <TextBlock.InputBindings>
                                            <MouseBinding MouseAction="RightClick" Command="{Binding RightClickCommand}"/>
                                            <MouseBinding MouseAction="LeftClick" Command="{Binding LeftClickCommand}" CommandParameter="{Binding Property1}"/>
                                        </TextBlock.InputBindings>
                                    </TextBlock>`

Solution

  • You didn't show how your VM is implemented (in the part relevant to your question). So I will give my answer based on my assumptions from the code snippets you showed.
    In my opinion, the RightClickCommand and LeftClickCommand commands are located at the ItemsSource level of the DataGrid. In such a case, one solution is to use the AncestorType binding.
    I also assume that the "Property1" property is a property of the ItemsSource collection element.
    An example of such a binding for the children of the DataGridCell:

        <TextBlock.InputBindings>
            <MouseBinding MouseAction="RightClick"
                          Command="{Binding DataContext.RightClickCommand,
                                            RelativeSource={RelativeSource AncestorType={x:Type DataGrid}}}"/>
            <MouseBinding MouseAction="LeftClick"
                          Command="{Binding DataContext.LeftClickCommand,
                                            RelativeSource={RelativeSource AncestorType={x:Type DataGrid}}}"
                          CommandParameter="{Binding Property1}"/>
        </TextBlock.InputBindings>
    

    F̲u̲l̲l̲ ̲e̲x̲a̲m̲p̲l̲e̲:̲

    using Simplified; // namespace for my implementation of ViewModelBase and RelayCommand
    using System.Collections.Generic;
    
    namespace Core2023.SO.Alex.ItemMouse
    {
        public class ItemClass : ViewModelBase
        {
            public int RightClickCount { get => Get<int>(); set => Set(value); }
            public int LeftClickCount { get => Get<int>(); set => Set(value); }
            public string Title { get; }
    
            public ItemClass(string title)
            {
                Title = title;
            }
        }
    
        public class ViewModel : ViewModelBase
        {
            public IEnumerable<ItemClass> ItemsCollection { get; }
            public RelayCommand RightClickCommand => GetCommand<ItemClass>(item => item.RightClickCount++);
            public RelayCommand LeftClickCommand => GetCommand<ItemClass>(item => item.LeftClickCount++);
    
            public ViewModel()
            {
                ItemsCollection = new ItemClass[]
                {
                    new ("First"),
                    new ("Second"),
                    new ("Third"),
                };
            }
        }
    }
    
    <Window x:Class="Core2023.SO.Alex.ItemMouse.ItemMouseCommandWindow"
            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:Core2023.SO.Alex.ItemMouse"
            mc:Ignorable="d"
            Title="ItemMouseCommandWindow" Height="450" Width="800">
        <Window.DataContext>
            <local:ViewModel/>
        </Window.DataContext>
        <Grid>
            <DataGrid ItemsSource="{Binding ItemsCollection}" AutoGenerateColumns="False">
                <DataGrid.Columns>
                    <DataGridTemplateColumn Header="Title">
                        <DataGridTemplateColumn.CellTemplate>
                            <DataTemplate DataType="{x:Type local:ItemClass}">
                                <TextBlock Text="{Binding Title}" Padding="5" Background="Aqua">
                                    <TextBlock.InputBindings>
                                        <MouseBinding MouseAction="RightClick"
                                                      Command="{Binding DataContext.RightClickCommand,
                                                                        RelativeSource={RelativeSource AncestorType=DataGrid}}"
                                                      CommandParameter="{Binding}"/>
                                        <MouseBinding MouseAction="LeftClick"
                                                      Command="{Binding DataContext.LeftClickCommand,
                                                                        RelativeSource={RelativeSource AncestorType=DataGrid}}"
                                                      CommandParameter="{Binding}"/>
                                    </TextBlock.InputBindings>
                                </TextBlock>
                            </DataTemplate>
                        </DataGridTemplateColumn.CellTemplate>
                    </DataGridTemplateColumn>
                    <DataGridTextColumn Header="Left" Binding="{Binding LeftClickCount}"/>
                    <DataGridTextColumn Header="Right" Binding="{Binding RightClickCount}"/>
                </DataGrid.Columns>
            </DataGrid>
        </Grid>
    </Window>
    

    P.S. Personally, in such cases, I prefer to use RoutedCommand instead of binding to ViewModel properties from ItemsControl lines (including DataGrid). And then at the window level to set the processing of commands.