I am looking for the correct way to specify the bindings for a DataTemplate.
For the correct initialisation of a Command it is relevant in what order CommandParamter and Commands bindings happen.
Here is a xaml and some sourcecode to illustrate the point. The example contains 4 Commands, 2 are expected to not initialize correctly. 2 are supposed to initialize correctly. The two trivial cases behave as expected. The two DataTemplate cases do not behave as I expect.
xaml:
<Window x:Class="WPFCommand.MainWindow"
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:WPFCommand"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<StackPanel>
<Button
Command="{Binding CMD1}"
CommandParameter="{Binding Parameter1}"
>CMD->PAR</Button>
<Button
CommandParameter="{Binding Parameter2}"
Command="{Binding CMD2}"
>PAR->CMD</Button>
<DataGrid
ItemsSource="{Binding DataGridItemsSource}"
AutoGenerateColumns="False"
HeadersVisibility="None"
>
<DataGrid.Columns >
<DataGridTemplateColumn Width="1*">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Button
Command="{Binding CMD4}"
CommandParameter="{Binding Parameter4}"
>Grid CMD->PAR</Button>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn Width="1*">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Button
CommandParameter="{Binding Parameter3}"
Command="{Binding CMD3}"
>Grid PAR->CMD</Button>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
</StackPanel>
</Window>
Codebehind
namespace WPFCommand
{
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Windows;
using System.Windows.Input;
public class DemoCommand : ICommand
{
bool? oldCanEx;
private string myname;
public DemoCommand(string myname)
{
this.myname = myname;
}
public event EventHandler CanExecuteChanged = delegate { };
public bool CanExecute(object parameter)
{
bool newCanEx;
if (parameter == null)
{
Debug.WriteLine($"{myname} CanExecute called with null");
newCanEx = false;
}
else
{
Debug.WriteLine($"{myname} CanExecute called with {parameter}");
newCanEx = true;
}
if (oldCanEx != newCanEx)
{
Debug.WriteLine($"{myname} CanExecute changed");
oldCanEx = newCanEx;
CanExecuteChanged(this, EventArgs.Empty);
}
return newCanEx;
}
public void Execute(object parameter)
{
Debug.WriteLine($"{myname} Execute {parameter}");
}
}
public class Item
{
internal Item() { }
public DemoCommand CMD3 { get; private set; } = new DemoCommand("CMD3");
public DemoCommand CMD4 { get; private set; } = new DemoCommand("CMD4");
public int Parameter3 { get; private set; } = 7;
public int Parameter4 { get; private set; } = 13;
}
public class MainWindowViewModel
{
public IEnumerable<Item> DataGridItemsSource { get; private set; } = new List<Item> { new Item() };
public DemoCommand CMD1 { get; private set; } = new DemoCommand("CMD1");
public DemoCommand CMD2 { get; private set; } = new DemoCommand("CMD2");
public int Parameter1 { get; private set; } = 42;
public int Parameter2 { get; private set; } = 5;
}
public partial class MainWindow : Window
{
public MainWindow()
{
this.DataContext = new MainWindowViewModel();
InitializeComponent();
}
}
}
CMD1 and CMD2 are outside of the DataTemplate. CMD3 and CMD4 are Inside. Both show the same difference in the ordering of the bindings.
Here is the Debug output:
CMD1 CanExecute called with null
CMD1 CanExecute changed
CMD1 CanExecute called with null
CMD2 CanExecute called with 5
CMD2 CanExecute changed
CMD2 CanExecute called with 5
CMD4 CanExecute called with null
CMD4 CanExecute changed
CMD4 CanExecute called with null
CMD3 CanExecute called with null
CMD3 CanExecute changed
CMD3 CanExecute called with null
CMD1 Fails to initialize corretly as expected. CMD2 Succeeds as expected. CMD4 Fails as expeted. CMD3 Fails and I did not expect that.
Why did the parameter of CMD3 not bind before the Command?
What is the correct way to write the xaml for a DataTemplated Command/CommandParameter binding (ideally without touching the code behind)?
This Issue can be resolved by putting the DataTemplate Contents into their own view.
Use
<DataTemplate>
<local:CommandView />
</DataTemplate>
With CommandView beeing a custom UserControl.
<UserControl x:Class="WPFCommand.CommandView"
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:local="clr-namespace:WPFCommand"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<Button
CommandParameter="{Binding Parameter3}"
Command="{Binding CMD3}"
>Grid PAR->CMD</Button>
</UserControl>