I have a (WPF) Catel DataWindow with a DataGrid, where the SelectedItem property is bound to a VM property, and has two buttons indented to launch different Catel TaskCommands on the selected data grid item.
Note the CommandParameters are bound in different ways to what seems - but isn't - the same value:
<catel:DataWindow x:Class="CatelWPFApplication1.Views.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:catel="http://schemas.catelproject.com"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:viewModels="clr-namespace:CatelWPFApplication1.ViewModels"
mc:Ignorable="d"
ShowInTaskbar="True"
ResizeMode="NoResize"
SizeToContent="WidthAndHeight"
WindowStartupLocation="Manual"
d:DataContext="{d:DesignInstance Type=viewModels:MainWindowViewModel, IsDesignTimeCreatable=True}"
>
<StackPanel Orientation="Vertical">
<StackPanel Orientation="Horizontal" Margin="0,6">
<Button Command="{Binding ViaVmCommand}"
CommandParameter="{Binding SelectedPerson, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"
Content="Binding via VM" />
<Button Command="{Binding ViaElementNameCommand}"
CommandParameter="{Binding SelectedItem, ElementName=PersonDataGrid, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"
Content="Binding via ElementName"
x:Name="ViaElementButton"/>
</StackPanel>
<DataGrid x:Name="PersonDataGrid"
ItemsSource="{Binding PersonList}"
SelectedItem="{Binding SelectedPerson, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"
AutoGenerateColumns="True" />
</StackPanel>
</catel:DataWindow>
For completeness, the VM code is given below.
When the Window is first shown, no data grid row is selected, and as expected both buttons are disabled. If the first data grid row gets selected (by a mouse click), only the one button binding to the SelectedPerson property of the VM gets enabled while to my surprise the other one stays disabled. On selecting a different item, both buttons get enabled and on Crtl-Clicking unselecting the selected line, the VM bound button gets disabled while the one binding through the ElementName mechanism doesn't.
Using Debugger breakpoints, I proved that both CanExecute functions are called on window initialization and on each item selection change. Yet the parameter for the Button binding via ElementName reference is one click behind.
If I change the VM SelectedPerson property in one of those commands, both buttons update as expected, their CanExecute handlers get the correct item value.
I see that binding to the VM property isn't bad, as it will be useful elsewhere in the business logic, yet I like learn why the two approaches behave so different.
What is going on with the 'ElementName' binding, why is it one click behind?
Finally, this is the VM
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Catel.Data;
using Catel.MVVM;
using CatelWPFApplication1.Models;
namespace CatelWPFApplication1.ViewModels
{
/// <summary>
/// MainWindow view model.
/// </summary>
public class MainWindowViewModel : ViewModelBase
{
#region Fields
#endregion
#region Constructors
public MainWindowViewModel()
{
ViaVmCommand = new TaskCommand<Person>(OnViaVmCommandExecute, OnViaVmCommandCanExecute);
ViaElementNameCommand = new TaskCommand<Person>(OnViaElementNameCommandExecute, OnViaElementNameCommandCanExecute);
PersonList = new List<Person>();
PersonList.Add(new Person(){FirstName = "Albert", LastName = "Abraham"});
PersonList.Add(new Person(){FirstName = "Betty", LastName = "Baboa"});
PersonList.Add(new Person(){FirstName = "Cherry", LastName="Cesar"});
}
#endregion
#region Properties
/// <summary>
/// Gets the title of the view model.
/// </summary>
/// <value>The title.</value>
public override string Title { get { return "View model title"; } }
public List<Person> PersonList { get; }
// classic Catel property, avoiding any magic with Fody weavers
public Person SelectedPerson
{
get { return GetValue<Person>(SelectedPersonProperty); }
set
{
SetValue(SelectedPersonProperty, value);
}
}
public static readonly PropertyData SelectedPersonProperty = RegisterProperty(nameof(SelectedPerson), typeof(Person), null);
#endregion
#region Commands
public TaskCommand<Person> ViaElementNameCommand { get; }
private bool OnViaElementNameCommandCanExecute(Person person)
{
return person is not null;
}
private async Task OnViaElementNameCommandExecute(Person person)
{
SelectedPerson = null;
}
public TaskCommand<Person> ViaVmCommand { get; }
private bool OnViaVmCommandCanExecute(Person person)
{
return person is not null;
}
private async Task OnViaVmCommandExecute(Person person)
{
SelectedPerson = PersonList.FirstOrDefault();
}
#endregion
}
}