DataGridTemplateColumn InvokeCommandAction Template

1.5k views Asked by At

I'm using a UserControl where the main control inside is a datagrid. All is working. However, some lines of my code are redundant since I implement the same trigger in each of the 48 columns.

How do I make a template for the datagridtemplatecolumn of the following code snippet? The only thing different in each column is the bound property. All the triggers and formatting are the same.

I would appreciate your help!

<DataGridTemplateColumn Header="5" MaxWidth="10">
    <DataGridTemplateColumn.CellTemplate>
        <DataTemplate>
            <TextBlock
                Background="{Binding _5, Converter={StaticResource  ResourceKey=hourSlotColorConverter}, UpdateSourceTrigger=PropertyChanged}"
                Margin="-1">
                <i:Interaction.Triggers>
                    <i:EventTrigger EventName="PreviewMouseLeftButtonDown">
                        <i:InvokeCommandAction
                            Command="{Binding RelativeSource={RelativeSource AncestorType={x:Type DataGrid}},
                                                                    Path=DataContext.StartCellCommand}">
                            <i:InvokeCommandAction.CommandParameter>
                                <MultiBinding Converter="{StaticResource dgInfoConverter}">
                                    <Binding Path="Column"
                                             RelativeSource="{RelativeSource Mode=FindAncestor, AncestorType=DataGridCell}" />
                                    <Binding Path="DataContext"
                                             RelativeSource="{RelativeSource Mode=FindAncestor, AncestorType=DataGridCell}" />
                                </MultiBinding>
                            </i:InvokeCommandAction.CommandParameter>
                        </i:InvokeCommandAction>
                    </i:EventTrigger>
                    <i:EventTrigger EventName="PreviewMouseLeftButtonUp">
                        <i:InvokeCommandAction
                            Command="{Binding RelativeSource={RelativeSource AncestorType={x:Type DataGrid}},
                                            Path=DataContext.LastCellCommand}">
                            <i:InvokeCommandAction.CommandParameter>
                                <MultiBinding Converter="{StaticResource dgInfoConverter}">
                                    <Binding Path="Column"
                                             RelativeSource="{RelativeSource Mode=FindAncestor, AncestorType=DataGridCell}" />
                                    <Binding Path="DataContext"
                                             RelativeSource="{RelativeSource Mode=FindAncestor, AncestorType=DataGridCell}" />
                                </MultiBinding>
                            </i:InvokeCommandAction.CommandParameter>
                        </i:InvokeCommandAction>
                    </i:EventTrigger>
                    <i:EventTrigger EventName="MouseMove">
                        <i:InvokeCommandAction
                            Command="{Binding RelativeSource={RelativeSource AncestorType={x:Type DataGrid}},
                                                                    Path=DataContext.MouseDragCom}">
                            <i:InvokeCommandAction.CommandParameter>
                                <MultiBinding Converter="{StaticResource dgInfoConverter}">
                                    <Binding Path="Column"
                                             RelativeSource="{RelativeSource Mode=FindAncestor, AncestorType=DataGridCell}" />
                                    <Binding Path="DataContext"
                                             RelativeSource="{RelativeSource Mode=FindAncestor, AncestorType=DataGridCell}" />
                                </MultiBinding>
                            </i:InvokeCommandAction.CommandParameter>
                        </i:InvokeCommandAction>
                    </i:EventTrigger>
                </i:Interaction.Triggers>
            </TextBlock>
        </DataTemplate>
    </DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>

<DataGridTemplateColumn Header="" MaxWidth="10">
    <DataGridTemplateColumn.CellTemplate>
        <DataTemplate>
            <TextBlock
                Background="{Binding _5_5, Converter={StaticResource  ResourceKey=hourSlotColorConverter}, UpdateSourceTrigger=PropertyChanged}"
                Margin="-1">
                <i:Interaction.Triggers>
                    <i:EventTrigger EventName="PreviewMouseLeftButtonDown">
                        <i:InvokeCommandAction
                            Command="{Binding RelativeSource={RelativeSource AncestorType={x:Type DataGrid}},
                                                                    Path=DataContext.StartCellCommand}">
                            <i:InvokeCommandAction.CommandParameter>
                                <MultiBinding Converter="{StaticResource dgInfoConverter}">
                                    <Binding Path="Column"
                                             RelativeSource="{RelativeSource Mode=FindAncestor, AncestorType=DataGridCell}" />
                                    <Binding Path="DataContext"
                                             RelativeSource="{RelativeSource Mode=FindAncestor, AncestorType=DataGridCell}" />
                                </MultiBinding>
                            </i:InvokeCommandAction.CommandParameter>
                        </i:InvokeCommandAction>
                    </i:EventTrigger>
                    <i:EventTrigger EventName="PreviewMouseLeftButtonUp">
                        <i:InvokeCommandAction
                            Command="{Binding RelativeSource={RelativeSource AncestorType={x:Type DataGrid}},
                                            Path=DataContext.LastCellCommand}">
                            <i:InvokeCommandAction.CommandParameter>
                                <MultiBinding Converter="{StaticResource dgInfoConverter}">
                                    <Binding Path="Column"
                                             RelativeSource="{RelativeSource Mode=FindAncestor, AncestorType=DataGridCell}" />
                                    <Binding Path="DataContext"
                                             RelativeSource="{RelativeSource Mode=FindAncestor, AncestorType=DataGridCell}" />
                                </MultiBinding>
                            </i:InvokeCommandAction.CommandParameter>
                        </i:InvokeCommandAction>
                    </i:EventTrigger>
                    <i:EventTrigger EventName="MouseMove">
                        <i:InvokeCommandAction
                            Command="{Binding RelativeSource={RelativeSource AncestorType={x:Type DataGrid}},
                                                                    Path=DataContext.MouseDragCom}">
                            <i:InvokeCommandAction.CommandParameter>
                                <MultiBinding Converter="{StaticResource dgInfoConverter}">
                                    <Binding Path="Column"
                                             RelativeSource="{RelativeSource Mode=FindAncestor, AncestorType=DataGridCell}" />
                                    <Binding Path="DataContext"
                                             RelativeSource="{RelativeSource Mode=FindAncestor, AncestorType=DataGridCell}" />
                                </MultiBinding>
                            </i:InvokeCommandAction.CommandParameter>
                        </i:InvokeCommandAction>
                    </i:EventTrigger>
                </i:Interaction.Triggers>
            </TextBlock>
        </DataTemplate>
    </DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>

<DataGridTemplateColumn Header="6" MaxWidth="10">
    <DataGridTemplateColumn.CellTemplate>
        <DataTemplate>
            <TextBlock
                Background="{Binding _6, Converter={StaticResource  ResourceKey=hourSlotColorConverter}, UpdateSourceTrigger=PropertyChanged}"
                Margin="-1">
                <i:Interaction.Triggers>
                    <i:EventTrigger EventName="PreviewMouseLeftButtonDown">
                        <i:InvokeCommandAction
                            Command="{Binding RelativeSource={RelativeSource AncestorType={x:Type DataGrid}},
                                                                    Path=DataContext.StartCellCommand}">
                            <i:InvokeCommandAction.CommandParameter>
                                <MultiBinding Converter="{StaticResource dgInfoConverter}">
                                    <Binding Path="Column"
                                             RelativeSource="{RelativeSource Mode=FindAncestor, AncestorType=DataGridCell}" />
                                    <Binding Path="DataContext"
                                             RelativeSource="{RelativeSource Mode=FindAncestor, AncestorType=DataGridCell}" />
                                </MultiBinding>
                            </i:InvokeCommandAction.CommandParameter>
                        </i:InvokeCommandAction>
                    </i:EventTrigger>
                    <i:EventTrigger EventName="PreviewMouseLeftButtonUp">
                        <i:InvokeCommandAction
                            Command="{Binding RelativeSource={RelativeSource AncestorType={x:Type DataGrid}},
                                            Path=DataContext.LastCellCommand}">
                            <i:InvokeCommandAction.CommandParameter>
                                <MultiBinding Converter="{StaticResource dgInfoConverter}">
                                    <Binding Path="Column"
                                             RelativeSource="{RelativeSource Mode=FindAncestor, AncestorType=DataGridCell}" />
                                    <Binding Path="DataContext"
                                             RelativeSource="{RelativeSource Mode=FindAncestor, AncestorType=DataGridCell}" />
                                </MultiBinding>
                            </i:InvokeCommandAction.CommandParameter>
                        </i:InvokeCommandAction>
                    </i:EventTrigger>
                    <i:EventTrigger EventName="MouseMove">
                        <i:InvokeCommandAction
                            Command="{Binding RelativeSource={RelativeSource AncestorType={x:Type DataGrid}},
                                                                    Path=DataContext.MouseDragCom}">
                            <i:InvokeCommandAction.CommandParameter>
                                <MultiBinding Converter="{StaticResource dgInfoConverter}">
                                    <Binding Path="Column"
                                             RelativeSource="{RelativeSource Mode=FindAncestor, AncestorType=DataGridCell}" />
                                    <Binding Path="DataContext"
                                             RelativeSource="{RelativeSource Mode=FindAncestor, AncestorType=DataGridCell}" />
                                </MultiBinding>
                            </i:InvokeCommandAction.CommandParameter>
                        </i:InvokeCommandAction>
                    </i:EventTrigger>
                </i:Interaction.Triggers>
            </TextBlock>
        </DataTemplate>
    </DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>

EDIT

So I followed Sheridan's suggestion with some revisions to make it shorter and simpler. Although our project leader suggested this method before, I had no idea on how to implement it prior to Sheridan's input. Kudos to him

This is the code for the UserControl we used. What it contains is exactly the same triggers we used from our original code above:

<UserControl x:Class="Widget5.View.DGInfo"
             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="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
             xmlns:converter="clr-namespace:Widget5.Converter"  
             xmlns:dg="clr-namespace:Widget5.View"
             mc:Ignorable="d" 
             d:DesignHeight="20" d:DesignWidth="20">

    <UserControl.Resources>
        <converter:DataGridInfoToParamConverter x:Key="dgInfoConverter" />
    </UserControl.Resources>

    <TextBlock  Background="{Binding Background, RelativeSource={RelativeSource AncestorType={x:Type dg:DGInfo}}}" Margin="-1">
        <i:Interaction.Triggers>
            <i:EventTrigger EventName="PreviewMouseLeftButtonDown">
                                        <i:InvokeCommandAction Command="{Binding RelativeSource={RelativeSource AncestorType={x:Type DataGrid}},
                                                                Path=DataContext.StartCellCommand}" >
                                            <i:InvokeCommandAction.CommandParameter>
                                                <MultiBinding Converter="{StaticResource dgInfoConverter}">
                                                    <Binding Path="Column" RelativeSource="{RelativeSource Mode=FindAncestor, AncestorType=DataGridCell}" />
                                                    <Binding Path="DataContext" RelativeSource="{RelativeSource Mode=FindAncestor, AncestorType=DataGridCell}" />
                                                </MultiBinding>
                                            </i:InvokeCommandAction.CommandParameter>
                                        </i:InvokeCommandAction>
                                    </i:EventTrigger>
                                    <i:EventTrigger EventName="PreviewMouseLeftButtonUp">
                                        <i:InvokeCommandAction Command="{Binding RelativeSource={RelativeSource AncestorType={x:Type DataGrid}},
                                        Path=DataContext.LastCellCommand}" >
                                            <i:InvokeCommandAction.CommandParameter>
                                                <MultiBinding Converter="{StaticResource dgInfoConverter}">
                                                    <Binding Path="Column" RelativeSource="{RelativeSource Mode=FindAncestor, AncestorType=DataGridCell}" />
                                                    <Binding Path="DataContext" RelativeSource="{RelativeSource Mode=FindAncestor, AncestorType=DataGridCell}" />
                                                </MultiBinding>
                                            </i:InvokeCommandAction.CommandParameter>
                                        </i:InvokeCommandAction>
                                    </i:EventTrigger>
                                    <i:EventTrigger EventName="MouseMove">
                                        <i:InvokeCommandAction Command="{Binding RelativeSource={RelativeSource AncestorType={x:Type DataGrid}},
                                                                Path=DataContext.MouseDragCom}" >
                                            <i:InvokeCommandAction.CommandParameter>
                                                <MultiBinding Converter="{StaticResource dgInfoConverter}">
                                                    <Binding Path="Column" RelativeSource="{RelativeSource Mode=FindAncestor, AncestorType=DataGridCell}" />
                                                    <Binding Path="DataContext" RelativeSource="{RelativeSource Mode=FindAncestor, AncestorType=DataGridCell}" />
                                                </MultiBinding>
                                            </i:InvokeCommandAction.CommandParameter>
                                        </i:InvokeCommandAction>
                                    </i:EventTrigger>
        </i:Interaction.Triggers>
        </TextBlock>
</UserControl>

And on the DataGridTemplateColumn itself, we just added the UserControl(DGInfo) and set its background like we did before. Saved thousands of lines because of this.

<DataGrid ...>
...
<DataGridTemplateColumn Header="6" MaxWidth="10">
   <DataGridTemplateColumn.CellTemplate>
       <DataTemplate>
          <View:DGInfo Background="{Binding _6, Converter={StaticResource  ResourceKey=hourSlotColorConverter}, UpdateSourceTrigger=PropertyChanged}"/>
          </DataTemplate>
       </DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
...
</DataGrid>
1

There are 1 answers

2
Sheridan On BEST ANSWER

If your DataTemplates are exactly the same then just put one into a Resources section and then simply reference it:

<DataGrid.Resources>
    <DataTemplate x:Key="TheDataTemplate">
        <TextBlock
            Background="{Binding _5_5, Converter={StaticResource  ResourceKey=hourSlotColorConverter}, UpdateSourceTrigger=PropertyChanged}"
            Margin="-1">
            <i:Interaction.Triggers>
                <i:EventTrigger EventName="PreviewMouseLeftButtonDown">
                    <i:InvokeCommandAction
                        Command="{Binding RelativeSource={RelativeSource AncestorType={x:Type DataGrid}},
                                                                Path=DataContext.StartCellCommand}">
                        <i:InvokeCommandAction.CommandParameter>
                            <MultiBinding Converter="{StaticResource dgInfoConverter}">
                                <Binding Path="Column"
                                         RelativeSource="{RelativeSource Mode=FindAncestor, AncestorType=DataGridCell}" />
                                <Binding Path="DataContext"
                                         RelativeSource="{RelativeSource Mode=FindAncestor, AncestorType=DataGridCell}" />
                            </MultiBinding>
                        </i:InvokeCommandAction.CommandParameter>
                    </i:InvokeCommandAction>
                </i:EventTrigger>
                <i:EventTrigger EventName="PreviewMouseLeftButtonUp">
                    <i:InvokeCommandAction
                        Command="{Binding RelativeSource={RelativeSource AncestorType={x:Type DataGrid}},
                                        Path=DataContext.LastCellCommand}">
                        <i:InvokeCommandAction.CommandParameter>
                            <MultiBinding Converter="{StaticResource dgInfoConverter}">
                                <Binding Path="Column"
                                         RelativeSource="{RelativeSource Mode=FindAncestor, AncestorType=DataGridCell}" />
                                <Binding Path="DataContext"
                                         RelativeSource="{RelativeSource Mode=FindAncestor, AncestorType=DataGridCell}" />
                            </MultiBinding>
                        </i:InvokeCommandAction.CommandParameter>
                    </i:InvokeCommandAction>
                </i:EventTrigger>
                <i:EventTrigger EventName="MouseMove">
                    <i:InvokeCommandAction
                        Command="{Binding RelativeSource={RelativeSource AncestorType={x:Type DataGrid}},
                                                                Path=DataContext.MouseDragCom}">
                        <i:InvokeCommandAction.CommandParameter>
                            <MultiBinding Converter="{StaticResource dgInfoConverter}">
                                <Binding Path="Column"
                                         RelativeSource="{RelativeSource Mode=FindAncestor, AncestorType=DataGridCell}" />
                                <Binding Path="DataContext"
                                         RelativeSource="{RelativeSource Mode=FindAncestor, AncestorType=DataGridCell}" />
                            </MultiBinding>
                        </i:InvokeCommandAction.CommandParameter>
                    </i:InvokeCommandAction>
                </i:EventTrigger>
            </i:Interaction.Triggers>
        </TextBlock>
    </DataTemplate>
</DataGrid.Resources>
...
<DataGridTemplateColumn CellTemplate="{StaticResource TheDataTemplate}" 
Header="" MaxWidth="10" />
...

UPDATE >>>

Ok, so if you have different Bindings inside the DataTemplate, then you'll have to use a UserControl instead. You will then be able to declare DependencyPropertys for the variable properties and bind to them from the DataGridColumn:

<UserControl ... >
    <TextBlock
        Background="{Binding Background, RelativeSource={RelativeSource AncestorType={x:Type yourXmlNamespacePrefix:YourUserControl}}}"
        Margin="-1">
        <i:Interaction.Triggers>
            <i:EventTrigger EventName="PreviewMouseLeftButtonDown">
                <i:InvokeCommandAction
                    Command="{Binding StartCellCommand, RelativeSource={RelativeSource AncestorType={x:Type yourXmlNamespacePrefix:YourUserControl}}}">
                    <i:InvokeCommandAction.CommandParameter>
                        <MultiBinding Converter="{StaticResource dgInfoConverter}">
                            <Binding Path="Column"
                                     RelativeSource="{RelativeSource Mode=FindAncestor, AncestorType={x:Type yourXmlNamespacePrefix:YourUserControl}}" />
                            <Binding Path="SomeProperty"
                                     RelativeSource="{RelativeSource Mode=FindAncestor, AncestorType={x:Type yourXmlNamespacePrefix:YourUserControl}}" />
                        </MultiBinding>
                    </i:InvokeCommandAction.CommandParameter>
                </i:InvokeCommandAction>
            </i:EventTrigger>
            ...
        </i:Interaction.Triggers>
    </TextBlock>
</UserControl>

If you define a DependencyProperty for each different Binding, then you'll be able to set them like this:

...
<DataGridTemplateColumn Header="" MaxWidth="10">
    <DataGridTemplateColumn.CellTemplate>
        <DataTemplate>
            <yourXmlNamespacePrefix:YourUserControl Background="{Binding _5_5, 
Converter={StaticResource  ResourceKey=hourSlotColorConverter}, 
UpdateSourceTrigger=PropertyChanged}" StartCellCommand="{Binding RelativeSource=
{RelativeSource AncestorType={x:Type DataGrid}}, Path=DataContext.StartCellCommand}" 
Column="{Binding Column, RelativeSource={RelativeSource AncestorType={x:Type 
DataGridCell}}}" SomeProperty="{Binding DataContext, RelativeSource={RelativeSource 
AncestorType={x:Type DataGridCell}}}" />
        </DataTemplate>
    </DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn >
...

It's still a lot of code, but it's much less than before. I didn't finish this example for all of your properties, but I'm guessing that you'll get the idea.