does invokecommandaction use canexecute? - wpf

I am new to wpf and interactivity. I am trying to execute a command based on some event trigger. In the code below, when the doubleclick event is trigger, CanExecute is invoked and return false but the execute function is still called. Is this the default behavior for invokecommandaction? I would think that when can execute returns false, execute will not be called.
<UserControl x:Class="..."
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity">
<i:Interaction.Triggers>
<i:EventTrigger EventName="MouseDoubleClick">
<i:InvokeCommandAction Command="{Binding Path=DisplayReportCommand}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
...

Yes it will use the canexecute and command will not be execute if it return false. I have posted a code example.
Here is the command in your ViewModel class
RelayCommand _showMessageCommand;
public ICommand ShowMessageCommand
{
get
{
if (_showMessageCommand == null)
{
_showMessageCommand = new RelayCommand(param => this.ShowMessage(), param => this.CanShowMessage);
}
return _showMessageCommand;
}
}
public void ShowMessage()
{
MessageBox.Show("Nitesh");
}
private bool CanShowMessage
{
get
{
return false; // Set to true to execute the command
}
}
and this is how you will use it in XAML
<Button Content="Nitesh">
<i:Interaction.Triggers>
<i:EventTrigger EventName="MouseDoubleClick">
<i:InvokeCommandAction Command="{Binding ShowMessageCommand}" ></i:InvokeCommandAction>
</i:EventTrigger>
</i:Interaction.Triggers>
</Button>

Related

How to bind Click in combobox in the view model?

I would like to do something when a click in a combobox, in a MVVM pattern.
I am trying to use input bindings in this way:
<ComboBox.InputBindings>
<KeyBinding Key="Return" Command="{Binding BuscarKeyDownCommand}"/>
<MouseBinding Gesture="LeftClick" Command="{Binding TiposFacturasMouseLeftClickCommand}"/>
</ComboBox.InputBindings>
In my view model I have this code:
private RelayCommand _tiposFacturasMouseLeftClickCommand;
public RelayCommand TiposFacturasMouseLeftClickCommand
{
get { return _tiposFacturasMouseLeftClickCommand ?? (_tiposFacturasMouseLeftClickCommand = new RelayCommand(param => TiposFacturasMouseLeftClick(), param => true)); }
}
private async void TiposFacturasMouseLeftClick()
{
try
{
//search for items.
}
catch(Exception ex)
{
MessageBox.Show(ex.Message);
}
}
But the command is not fired.
However, the key return input binding works as expected.
Which is the best way to bind the click event of the combobox? Or perhaps there are another better solution to search the items on the combobox the first time that I click on it.
Thanks.
You could install the Microsoft.Xaml.Behaviors.Wpf NuGet package and use an EventTrigger to invoke a command when an event is raised, e.g.:
<ComboBox xmlns:i="http://schemas.microsoft.com/xaml/behaviors">
<i:Interaction.Triggers>
<i:EventTrigger EventName="PreviewMouseLeftButtonDown" >
<i:InvokeCommandAction Command="{Binding TiposFacturasMouseLeftClickCommand}" />
</i:EventTrigger>
</i:Interaction.Triggers>
<ComboBoxItem>1</ComboBoxItem>
<ComboBoxItem>2</ComboBoxItem>
<ComboBoxItem>3</ComboBoxItem>
</ComboBox>
Please refer to this blog for more informatio about how to handle events in MVVM.

WPF: BusyIndicator when DataGrid loading and sorting

I have a DataGrid with long time loading.
I want put a BusyIndicator for the time to wait.
I have declared a property "IsBusy" in my ViewModel, the BusyIndicator is raised on it.
If I declare the property with "true" by default, the BusyIndicator does display good => OK.
If I declare it to be "false", the BusyIndicator is hidden => OK.
But, if I set the value by triggers in XAML, the property is well set, but the BusyIndicator doesn't appear.
ViewModel:
public bool IsBusy
{
get { return _IsBusy; }
set { _IsBusy = value; RaisePropertyChanged(nameof(IsBusy)); }
}
private void SetBusy(bool obj)
{
IsBusy = obj;
}
XAML:
<xctk:BusyIndicator Name="BusyBar" IsBusy="{Binding IsBusy, NotifyOnSourceUpdated=True, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}" BusyContent="Traitement en cours. veuillez patienter..." />
<ComboBox x:Name="cbbSchEtt" ...>
<i:Interaction.Triggers>
<i:EventTrigger EventName="SelectionChanged">
<i:InvokeCommandAction Command="{Binding SetBusyCommand}" CommandParameter="True"/>
<ei:CallMethodAction MethodName="LoadTableau" TargetObject="{Binding ElementName=ucTabBord}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</ComboBox>
Same thing if I set it by code behind (m is my ViewModel):
private void LoadTableau()
{
m.IsBusy = true;//Mouse.OverrideCursor = Cursors.Wait;
.....
m.IsBusy = false;
}
For information, if I don't set it to false at the end of LoadTableau(), the BusyIndicator is displayed well.
Any idea ?

Where to put events when using MVVM?

Should I put all events in views code behind or there is a more proper way, like place commands in ViewModel?
For example, I want to open Tab on double click on the datagrid row, where should I handle this event?
No you should not put events in code behind. In MVVM (Model-View-ViewModel) design pattern, the view model is the component that is responsible for handling the application's presentation logic and state. This means that your view's code-behind file should contain no code to handle events that are raised from any user interface (UI) element.
for eg if you have button in your xaml
<Button Content="OK" Click="btn_Click"/>
protected void btn_Click(object sender, RoutedEventArgs e)
{
/* This is not MVVM! */
}
Instead you can use WPF Command.All you have to do is bind to its Execute and CanExecute delegates and invoke your command.
So your code will now be
public class ViewModel
{
private readonly DelegateCommand<string> _clickCommand;
public ViewModel()
{
_clickCommand = new DelegateCommand(
(s) => { /* perform some action */ }, //Execute
null
} //CanExecute );
public DelegateCommand ButtonClickCommand
{
get { return _clickCommand; }
}
}
<Button Content="COOL" Command="ButtonClickCommand"/>
Kyle is correct in that your handlers should appear in the view model. If a command property doesn't exist then you can use an interaction trigger instead:
<DataGrid>
<i:Interaction.Triggers>
<i:EventTrigger EventName="MouseDoubleClick">
<i:InvokeCommandAction Command="{Binding Mode=OneWay, Path=OpenClientCommand}" CommandParameter="{Binding ElementName=searchResults, Path=SelectedItems}" />
</i:EventTrigger>
</i:Interaction.Triggers>
... other stuff goes here ...
</DataGrid>
Or you can use MVVM Lite's EventToCommand, which also allows you to pass in the message parameters:
<i:Interaction.Triggers>
<i:EventTrigger EventName="Closing">
<cmd:EventToCommand Command="{Binding ClosingCommand}" PassEventArgsToCommand="True" />
</i:EventTrigger>
</i:Interaction.Triggers>
Which is used in in this case to cancel the window close event in response to the "Are you sure you want to quit?" dialog:
public ICommand ClosingCommand { get { return new RelayCommand<CancelEventArgs>(OnClosing); } }
private void OnClosing(CancelEventArgs args)
{
if (UserCancelsClose())
args.Cancel = true;
}
Relevant namespaces are as follows:
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
xmlns:cmd ="http://www.galasoft.ch/mvvmlight"

Does caliburn micro support IsEnabledChanged?

I want to have a method fire when a button's enabled state changes, but it does not work. The method void EnableStartScan(bool isEnabled) in the view model never gets called.
<telerik:RadRibbonGroup Header="{x:Static res:StringTable.MachineCtrl}">
<telerik:RadRibbonButton x:Name="btnStart"
Text="{x:Static res:StringTable.Start}"
Size="Large"
LargeImage="/MCSP;component/Resources/Images/Button-Start.png">
<i:Interaction.Triggers>
<i:EventTrigger EventName="IsEnabledChanged">
<cal:ActionMessage MethodName="EnableStartScan">
<cal:Parameter Value="{Binding ElementName=btnStart, Path=IsEnabled}"/>
</cal:ActionMessage>
</i:EventTrigger>
</i:Interaction.Triggers>
</telerik:RadRibbonButton>
</<telerik:RadRibbonGroup>
Wy not control the state of your button in your ViewModel in the first place? Simply add a gate method in your viewModel and add trigger your other action within your viewModel
public bool CanSayHello(string name)
{
if(EvalIfEnable())
{
YourOtherMethod();
return true;
}
return false;
}
public void SayHello(string name)
{
ExecuteYourAction();
}

MVVM Relay Command triggers before Silverlight NumericUpDown changes value

If you have a Silverlight Toolkit NumericUpDown control binded to a MVVM property and a RelayCommand trigger set (any event), the command is called before NumericUpDown changes MVVM property value. This means, you can not use the new (changed) value with you method/action/command...
XAML:
<inputToolkit:NumericUpDown x:Name="testNum" Value="{Binding RegisterForm, Mode=TwoWay}">
<i:Interaction.Triggers>
<i:EventTrigger EventName="ValueChanged">
<GalaSoft_MvvmLight_Command:EventToCommand Command="{Binding DoSomethingCommand}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</inputToolkit:NumericUpDown>
MVVM (C#):
DoSomethingCommand = new RelayCommand(() =>
{
OtherRegisterForm = RegisterForm;
});
In this case, if you have v value 0 and you input a new value 123 in NumericUpDown control it triggers the "DoSomethingCommand" before "RaisePropertyChange" event on MVVM property.
"OtherRegisterForm" would be 0 and not 123.
Is there a way to make this work?
oh boy, wasn't easy but here u are :
xaml part :
<toolkit:NumericUpDown Value="{Binding SomeNumber}">
<i:Interaction.Triggers>
<i:EventTrigger EventName="ValueChanged">
<GalaSoft_MvvmLight_Command:EventToCommand Command="{Binding MyCommand}" PassEventArgsToCommand="True" />
</i:EventTrigger>
</i:Interaction.Triggers>
</toolkit:NumericUpDown>
and cs code :
public class MainViewModel : ViewModelBase
{
public double SomeNumber { get; set; }
public MainViewModel()
{
SomeNumber = 10;
MyCommand = new RelayCommand<RoutedPropertyChangedEventArgs<double>>(myActionMethod);
}
public RelayCommand<RoutedPropertyChangedEventArgs<double>> MyCommand { get; set; }
public void myActionMethod(RoutedPropertyChangedEventArgs<double> arg)
{
MessageBox.Show(arg.NewValue.ToString());
}
}
hope that helps, Arek

Resources