I have set a bool property and have bound it to the IsEnabled in the xaml but the ICommand CanExecute method overrides the IsEnabled in xaml, so my bool property is ineffect
You can always do your conditional statement within the CanExecute function of your custom command, no need for you to bind IsEnabled property with your button that is bound to a command. Here's a sample implementation, hope this helps.
Custom Command:
public class CustomCommand<T> : ICommand
{
private readonly Action<T> _action;
private readonly Predicate<T> _canExecute;
public CustomCommand(Action<T> action, Predicate<T> canExecute)
{
_action = action;
_canExecute = canExecute;
}
public bool CanExecute(object parameter)
{
return _canExecute((T)parameter);
}
public void Execute(object parameter)
{
_action((T)parameter);
}
public event EventHandler CanExecuteChanged;
}
As you can see here, I created an object that implements the ICommand interface, this custom command accepts a generic type parameter which is used to evaluate a condition (CanExecute: this tells whether to enable or disable a command (in UI, the button), normally use to check for permissions, and other certain conditions) this parameter is also used to execute the action (Execute: the actual logic/action to be performed), The command contructor accepts delegate parameters that contain signatures for these 2 methods, the caller may choose lambda or standard methods to fillup these parameters.
Sample ViewModel:
public class ViewModel1: INotifyPropertyChanged
{
public ViewModel1()
{
// Test Data.
Items = new ObservableCollection<ItemViewModel>
{
new ItemViewModel{ Code = "001", Description = "Paint" },
new ItemViewModel{ Code = "002", Description = "Brush" },
new ItemViewModel{ Code = "003", Description = "" }
};
EditCommand = new CustomCommand<ItemViewModel>(Edit, CanEdit);
}
public CustomCommand<ItemViewModel> EditCommand { get; }
private bool CanEdit(ItemViewModel item)
{
return item?.Description != string.Empty;
}
private void Edit(ItemViewModel item)
{
Debug.WriteLine("Selected Item: {0} - {1}", item.Code, item.Description);
}
private ObservableCollection<ItemViewModel> _items { get; set; }
public ObservableCollection<ItemViewModel> Items
{
get => _items;
set
{
_items = value;
OnPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
XAML:
<Page x:Name="root"
x:Class="App1.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:vms="using:App1.ViewModels"
mc:Ignorable="d"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"
d:DesignHeight="450" d:DesignWidth="800">
<Page.DataContext>
<vms:ViewModel1 x:Name="Model"/>
</Page.DataContext>
<Grid>
<ItemsControl ItemsSource="{Binding Items}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel Margin="0 0 0 15">
<TextBlock Text="{Binding Code}" />
<TextBlock Text="{Binding Description}" />
<Button Content="Edit" Command="{Binding DataContext.EditCommand, ElementName=root}" CommandParameter="{Binding}" />
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
</Page>
I think you can pick a lot of code from the RelayCommand of MVVMLight. Try to change your event to
public event EventHandler CanExecuteChanged
{
add
{
if (canExecute != null)
{
CommandManager.RequerySuggested += value;
}
}
remove
{
if (canExecute != null)
{
CommandManager.RequerySuggested -= value;
}
}
}
and add also a function
public void RaiseCanExecuteChanged()
{
CommandManager.InvalidateRequerySuggested();
}
Then, whatever you put as your Predicate on the command, at the Predicate's boolean setter do:
SomeCustomCommand.RaiseCanExecuteChanged()
Hope I helped.