MvvmCross: How to use the "PassEventArgsToCommand" on "MvxEventToCommand" - wpf

Maybe a simple question here, I tried to set PassEventArgsToCommand=True on MvxEventToCommand. But found no benefit.
The event/command gets fired on my ViewModel for "True" and for "False".This is OK, no complains here. But how do I access the EventArgs on the ViewModel?
Also using MvxWithArgsEventToCommand (which sets PassEventArgsToCommand to true) had no effect. I looked online and in NPlus1DaysOfMvvmCross samples but found no hints. I'm using WPF. Any help is much appreciated.
XAML:
<i:Interaction.Triggers>
<i:EventTrigger EventName="SelectionChanged">
<commands:MvxEventToCommand Command="{Binding DataContext.MyMandatory_SelectionChangedCommand,
Source={x:Reference MyView}}"
CommandParameter="{Binding RelativeSource={RelativeSource AncestorType={x:Type syncfusion:ComboBoxAdv}, Mode=FindAncestor}}"
PassEventArgsToCommand="True"
/>
</i:EventTrigger>
ViewModel:
public IMvxCommand MyMandatory_SelectionChangedCommand
{
get
{
return new MvxCommand<Object>(e => MyMandatory_Changed(e));
}
}
public void MyMandatory_Changed(Object e)
{
if (e != null && e is ComboBoxAdv)
{
ComboBoxAdv cbo = (ComboBoxAdv)e;
…Further code
}
}

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.

How can I take all value in screen?

I have an employee table I filtered from Location with a combobox in WPF MVVM.
like that:
in xaml:
<ComboBox Grid.Column="2" ItemsSource="{Binding Locations}" SelectedItem="{Binding SelectedLocation}">
<i:Interaction.Triggers>
<i:EventTrigger EventName="SelectionChanged">
<i:InvokeCommandAction Command="{Binding LocationFilterCommand}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</ComboBox>
In ViewModel:
Locations = new ObservableCollection<string>()
{ "All","A","B"};
and in LocationFilterCommand
#region LocationFilterCommand
private DelegateCommand _locationFilterCommand;
public DelegateCommand LocationFilterCommand
{
get { return _locationFilterCommand ?? (_locationFilterCommand = new DelegateCommand(CanLocationFilter, LocationFilter)); }
}
private bool CanLocationFilter()
{
return true;
}
private void LocationFilter()
{
ParticularEntries = ParticularEntries.Where(p =>p.Region.Location==_selectedLocation);
}
I dont have problem with when I select A location or when I select B location they comes to screen.But I dont know how to take all values together with "All" item in combobox?
Hint:All is not a location type.All need all locations employee together come to screen.But I dont know how to this filter ?
thank you for all your helps.

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();
}

Binding with checkbox (select all)

I want to implement a checkbox which functions as a select-all / unselect-all checkbox but I am getting some problems with the binding. I am not really new to XAML and WPF so it is a mistery to me why my code is not working. I hope you can help. This is what I am doing:
First of all, I use MVVMLight and the event-to-command tags.
My view is called SetupView.xaml and my view-model is called SetupViewModel.cs
public class SetupViewModel : ViewModelBase
{
private List<FilterOptions> m_informationToShow;
private FilterOptions m_currentSelection;
public List<FilterOptions> InformationToShow
{
get { return m_informationToShow; }
set
{
m_informationToShow = value;
RaisePropertyChanged("InformationToShow");
RaisePropertyChanged("InformationToShowCount");
}
}
public FilterOptions CurrentSelection
{
get { return m_currentSelection; }
set
{
m_currentSelection = value;
RaisePropertyChanged("CurrentSelection");
}
}
}
The FilterOptions-object is my model object. Because you will need some of it it to understand the problem, here it show the part you need to understand the problem:
public class FilterOptions
{
private string m_projectName;
private BugsFilter m_bugsFilter;
private BuildsFilter m_buildsFilter;
private ChangeSetsFilter m_changeSetsFilter;
private ProgressInfoFilter m_progressInfoFilter;
private RisksFilter m_risksFilter;
private bool m_projectHealthFilter;
public bool AllFilterValues
{
get
{
if (m_bugsFilter.AtLeastOneFieldEnabled() ||
m_buildsFilter.AtLeastOneFieldEnabled() ||
m_changeSetsFilter.AtLeastOneFieldEnabled() ||
m_progressInfoFilter.AtLeastOneFieldEnabled() ||
m_risksFilter.AtLeastOneFieldEnabled() ||
m_projectHealthFilter
)
{
return true;
}
else
{
return false;
}
}
set
{
if (value == false)
{
m_bugsFilter.NoInformation();
m_buildsFilter.NoInformation();
m_changeSetsFilter.NoInformation();
m_progressInfoFilter.NoInformation();
m_risksFilter.NoInformation();
m_projectHealthFilter = false;
}
else
{
m_bugsFilter.CompleteInformation();
m_buildsFilter.CompleteInformation();
m_changeSetsFilter.CompleteInformation();
m_progressInfoFilter.CompleteInformation();
m_risksFilter.CompleteInformation();
m_projectHealthFilter = true;
}
}
}
I will proceed with my view:
<UserControl.Resources>
<viewModels:SetupViewModel x:Key="thisViewModel"></viewModels:SetupViewModel>
<DataTemplate x:Key="ProjectEntryTemplate">
<Border Margin="75,20,5,0">
<CheckBox Name="naam" Content="{Binding ProjectName}"
FontFamily="Segoe UI"
FontWeight="Light"
FontSize="24"
IsChecked="{Binding AllFilterValues}"
DataContext="{Binding}">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Checked">
<mvvm:EventToCommand Command="{Binding ProjectListItemCheckedChanged, Source={StaticResource thisViewModel}}"
PassEventArgsToCommand="True"/>
</i:EventTrigger>
<i:EventTrigger EventName="Unchecked">
<mvvm:EventToCommand Command="{Binding ProjectListItemCheckedChanged, Source={StaticResource thisViewModel}}"
PassEventArgsToCommand="True"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</CheckBox>
</Border>
</DataTemplate>
</UserControl.Resources>
As you can see, I have a datatemplate which I am using in a listbox:
<ListBox ItemsSource="{Binding InformationToShow}"
ItemTemplate="{StaticResource ProjectEntryTemplate}"
SelectedIndex="0"
BorderThickness="0">
<i:Interaction.Triggers>
<i:EventTrigger EventName="SelectionChanged">
<mvvm:EventToCommand Command="{Binding SelectionListboxChanged}"
PassEventArgsToCommand="True"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</ListBox>
Besides from that, I have in the same window, in another grid on the right side, a lot of checkboxes. They all correspond to a given filter from the FilterOptions object. This is what I am doing in my view:
<StackPanel Orientation="Vertical"
Margin="0,5,0,5">
<CheckBox Name="activeBugs" Content="Active bugs"
FontFamily="Segoe UI"
FontWeight="Light"
FontSize="22"
IsChecked="{Binding CurrentSelection.BugsFilter.ActiveBugs}">
</CheckBox>
<CheckBox Name="resolvedBugs" Content="Resolved bugs"
FontFamily="Segoe UI"
FontWeight="Light"
FontSize="22"
IsChecked="{Binding CurrentSelection.BugsFilter.ResolvedBugs}"/>
<CheckBox Name="bugTrend" Content="Bug trend"
FontFamily="Segoe UI"
FontWeight="Light"
FontSize="22"
IsChecked="{Binding CurrentSelection.BugsFilter.BugTrend}"/>
</StackPanel>
Last but not least, this are the command functions which I have in my view-model:
RelayCommand m_selectionChanged;
public ICommand SelectionListboxChanged
{
get
{
if (m_selectionChanged == null)
m_selectionChanged = new RelayCommand(param => SelectionListboxChangedExec(param), param => true);
return m_selectionChanged;
}
}
private void SelectionListboxChangedExec(object param)
{
SelectionChangedEventArgs e = (SelectionChangedEventArgs)param;
ListBox b = (ListBox)e.Source;
CurrentSelection = (FilterOptions)b.SelectedItem;
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
RelayCommand m_projectCheckedChanged;
public ICommand ProjectListItemCheckedChanged
{
get
{
if (m_projectCheckedChanged == null)
m_projectCheckedChanged = new RelayCommand(param => ProjectListItemCheckedChangedExec(param), param => true);
return m_projectCheckedChanged;
}
}
private void ProjectListItemCheckedChangedExec(object param)
{
RoutedEventArgs e = (RoutedEventArgs)param;
CheckBox checkBox = (CheckBox)e.Source;
FilterOptions dataContext = (FilterOptions)checkBox.DataContext;
if ((bool)checkBox.IsChecked)
dataContext.AllFilterValues = true;
else
{
dataContext.AllFilterValues = false;
}
//var expression = checkBox.GetBindingExpression(ToggleButton.IsCheckedProperty);
//expression.UpdateSource();
}
I really cant find the problem. Is there anyone who can help me?
Your help will be much appreciated !!
Thank you all in advance !
Why do you have to do it with a command? bind the checkbox to a BindingOption class that internally notifies the parent ViewModel, this notification really doesn't belong in the view, but to the view-model.
The ViewModel isn't supposed to know that the view uses a CheckBox.
So what u should do have the individual options have a notifying IsSelected property bound to the CheckBoxes, provide each of them with a reference to the parent ViewModel, and notify the parent when current IsSelected was changed. Doing this in the View, although you can find many way to do it, you're not supposed to, MVVM is about separating view-tasks from viewmodel-tasks.
This is way also gives you control on "Select all", "Deselect all" or "Invert selection" etc.

MVVM - WPF DataGrid - AutoGeneratingColumn Event

I'm currently taking a good look at the excellent toolkit from Laurent and I have the following question.
From Blend 4, I have added an EventTrigger for the Loaded event, in my ViewModel I have the following:
public RelayCommand rcAutoGeneratingColumn { get; private set; }
In the constructor I have:
rcAutoGeneratingColumn =
new RelayCommand(o => DataGridAutoGeneratingColumn(o));
Also in the ViewModel, I have the method which I wish to be invoked by the RelayCommand:
private void DataGridAutoGeneratingColumn(Object o)
{
DataGrid grid = (DataGrid)o;
foreach (DataGridTextColumn col in grid.Columns)
{
if (col.Header.ToString().ToLower() == "id")
{
col.Visibility = System.Windows.Visibility.Hidden;
}
}
}
My XAML contains the following (for the DataGrid):
<i:Interaction.Triggers>
<i:EventTrigger EventName="Loaded">
<GalaSoft_MvvmLight_Command:EventToCommand
Command="{Binding rcAutoGeneratingColumn, Mode=OneWay}"
CommandParameter="{Binding ElementName=dataGrid1, Mode=OneWay}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
There is NO PROBLEM here the code works just fine, but obviously the event used to hide certain columns should be the AutoGeneratingColumn event and not Loaded.
I have used to Loaded event as a getaround.
I was hoping that I could relay any event offered by the control so that, in this case, the following would work instead:
<i:Interaction.Triggers>
<i:EventTrigger EventName="AutoGeneratingColumn">
<GalaSoft_MvvmLight_Command:EventToCommand
Command="{Binding rcAutoGeneratingColumn, Mode=OneWay}"
CommandParameter="{Binding ElementName=dataGrid1, Mode=OneWay}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
I am unable to get the AutoGeneratingColumn event to trigger, and I'm hoping that I've overlooked something and appreciate any advice given!
This behaviour is the same with the GridControl from DevExpress, in that the Loaded event is triggered whereas the ColumnsPopulated event (this being the equivalent of the AutoGeneratingColumn event) is not.
DevExpress offered the following information with regard to my question:
"We have reviewed this question, and come to an interesting conclusion. It looks like the visual tree is not being built at the moment when the Interaction.Triggers are being processed"
If this is true, and there is no other way in which to invoke the events within the ViewModel, then one would have to go ahead and - by using trial and error - note which of the DataGrid events (of which there are over 100) can be invoked in this way and which cannot!
One would like to think that every event which is available in the code-behind, can also be reached when applying the MVVM pattern.
I have searched for an answer but I cannot rule out that I have overlooked something, so if this is to be the case, then please accept my apologies!
You don't have to use evil code behind ;-) You can do this using an attached behaviour...
public class AutoGeneratingColumnEventToCommandBehaviour
{
public static readonly DependencyProperty CommandProperty =
DependencyProperty.RegisterAttached(
"Command",
typeof(ICommand),
typeof(AutoGeneratingColumnEventToCommandBehaviour),
new PropertyMetadata(
null,
CommandPropertyChanged));
public static void SetCommand(DependencyObject o, ICommand value)
{
o.SetValue(CommandProperty, value);
}
public static ICommand GetCommand(DependencyObject o)
{
return o.GetValue(CommandProperty) as ICommand;
}
private static void CommandPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var dataGrid = d as DataGrid;
if (dataGrid != null)
{
if (e.OldValue != null)
{
dataGrid.AutoGeneratingColumn -= OnAutoGeneratingColumn;
}
if (e.NewValue != null)
{
dataGrid.AutoGeneratingColumn += OnAutoGeneratingColumn;
}
}
}
private static void OnAutoGeneratingColumn(object sender, DataGridAutoGeneratingColumnEventArgs e)
{
var dependencyObject = sender as DependencyObject;
if (dependencyObject != null)
{
var command = dependencyObject.GetValue(CommandProperty) as ICommand;
if (command != null && command.CanExecute(e))
{
command.Execute(e);
}
}
}
}
Then use it in XAML like this...
<DataGrid ItemsSource="{Binding MyGridSource}"
AttachedCommand:AutoGeneratingColumnEventToCommandBehaviour.Command="{Binding CreateColumnsCommand}">
</DataGrid>
Just set EventTrigger.SourceObject property.
<DataGrid
x:Name="DataGrid"
AutoGenerateColumns="True"
IsReadOnly="True"
ItemsSource="{Binding Data}">
<i:Interaction.Triggers>
<i:EventTrigger EventName="AutoGeneratingColumn" SourceObject="{Binding ElementName=DataGrid}">
<local:EventToCommand
Command="{Binding ColumnGeneratingCommand}"
PassEventArgsToCommand="True" />
</i:EventTrigger>
</i:Interaction.Triggers>
</DataGrid>
As MVVMLight from Galasoft is deprecated now, we can use CommunityToolkit.Mvvm package and use it like this:
<DataGrid AutoGenerateColumns="True"
Name="DataGrid"
ItemsSource="{Binding Items}">
<i:Interaction.Triggers>
<i:EventTrigger EventName="AutoGeneratingColumn" SourceObject="{Binding ElementName=DataGrid}">
<i:InvokeCommandAction Command="{Binding AutoGeneratingColumnCommand}" PassEventArgsToCommand="True"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</DataGrid>
Note that Items property is a simple List, It could be an ObservableCollection or whatever.
The trick to get the fired event is to load your data after the window is loaded, or raise OnpropertyChanged on Items property after loaded.
<Window ...>
<i:Interaction.Triggers>
<i:EventTrigger EventName="Loaded">
<i:InvokeCommandAction Command="{Binding LoadedCommand}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</Window>
In your View Model:
private RelayCommand<DataGridAutoGeneratingColumnEventArgs> myAutoGeneratingColumnCommand;
public RelayCommand<DataGridAutoGeneratingColumnEventArgs> AutoGeneratingColumnCommand
{
get
{
if (myAutoGeneratingColumnCommand == null)
myAutoGeneratingColumnCommand = new RelayCommand<DataGridAutoGeneratingColumnEventArgs>(AutoGeneratingColumnCommandAction);
return myAutoGeneratingColumnCommand;
}
}
private void AutoGeneratingColumnCommandAction(DataGridAutoGeneratingColumnEventArgs e)
{
if (e.PropertyName == "Id")
{
e.Column.Width = 60;
}
else if (e.PropertyName == "Name")
{
e.Column.Header = "myName";
e.Column.Width = new DataGridLength(1, DataGridLengthUnitType.Star);
}
else
e.Cancel = true; // ignore all other properties and remove their column
}
RelayCommand myLoadedCommand;
public RelayCommand LoadedCommand
{
get
{
if (myLoadedCommand == null)
myLoadedCommand = new RelayCommand(LoadedCommandAction);
return myLoadedCommand;
}
}
private void LoadedCommandAction()
{
Load(); // Populate the Items List
}
During the course of developing a project with MVVM you're going to have circumstances where you must handle events in your view's code-behind and EventToCommand just plain doesn't work. You especially find this with Silverlight, but I assume from your question that you're using WPF. It's okay to do some event handling in your view's code-behind, just don't put any business logic there. You can even leave the command in your view model, just call it directly from your event handler.
((YourViewModel)this.DataContext).rcAutoGeneratingColumn.Execute(sender);

Resources