It's possible to convert event triggers to viewmodel using converters? - c#

I'm writing WPF app in MVVM using MVVM Light. I have an event trigger in DataGrid to detecting the cell editing ends.
In viewmodel I have command which needs a DataGrid binding item as param. I did it using casting DataGridCellEditEndingEventArgs.EditingElement.DataContext to my model. It's work as I want but it's hard to VM testing.
Here's View's trigger
// xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
<DataGrid x:Name="PeopleDataGrid" ItemsSource="{Binding People}" >
<i:Interaction.Triggers>
<i:EventTrigger EventName="CellEditEnding">
<cmd:EventToCommand PassEventArgsToCommand="True" Command="{Binding EditPersonRowCommand}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
And in VM here's the command
public RelayCommand<DataGridCellEditEndingEventArgs> EditPersonRowCommand
{
get
{
return editPersonRowCommand ??
(editPersonRowCommand =
new RelayCommand<DataGridCellEditEndingEventArgs>(param => this.EditPersonRow(param.EditingElement.DataContext as PersonForListDto), this.editPersonRowCommandCanExecute));
}
}
It's possible to using IValueConverter or something to have model right way without control casting?

The PassEventArgsToCommand dependency property pass the event argument to command. Instead of using PassEventArgsToCommand, you can define the binding for CommandParameter to pass the DataContext. With this, at VM, the RelayCommand can define with actual type. The code at View and ViewModel will be as follows:
<i:Interaction.Triggers>
<i:EventTrigger EventName="CellEditEnding">
<cmd:EventToCommand Command="{Binding EditPersonRowCommand}" CommandParameter="{Binding //Since you have not given the full code so not sure how Binding is cascading so if you require to use ReleativeSource to bind to DataContext then use that.}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
And
public RelayCommand<PersonForListDto> EditPersonRowCommand
{
get
{
return editPersonRowCommand ??
(editPersonRowCommand =
new RelayCommand<PersonForListDto>(param => this.EditPersonRow(param), this.editPersonRowCommandCanExecute));
}
}
With above, your VM would be cleaner and can easily be unit tested.

Related

Handling PreviewMouseDown and PreviewMouseUp events in Prism

I have a Prism application and I'm attempting to bind PreviewMouseDown and PreviewMouseUp button events in my view to commands in my view model. When I run the code I see the following exception:
As a workaround, I'm currently binding to methods in the view and use a reference to the data context of the view model to execute the command. This works but doesn't seem correct because the view now has knowledge of the view model.
What is the proper way to handle something like this?
You cannot bind events to commands like, for example, the Command property of a button.
Luckily, you don't need to, because you have the Command property. It even disables the button if the command returns false from CanExecute.
If you have something other than a button or something other than MouseDown, you can use InvokeCommandAction (from Prism or from Interactivity)...
xmlns:i="http://schemas.microsoft.com/xaml/behaviors"
<i:Interaction.Triggers>
<i:EventTrigger EventName="MouseDown">
<prism:InvokeCommandAction Command="{Binding MyCommand}"/>
<!-- or -->
<i:InvokeCommandAction Command="{Binding MyCommand}"/>
</i:EventTrigger>
</i:Interaction.Triggers>

Binding to Selected in WPF combobox

I have a WPF, C# Application and acombobx like:
<ComboBox SelectedItem="{Binding MySelectedItem.MyString, ValidatesOnDataErrors=True}" ItemsSource="{Binding MyCollection}" />
Is there a way to bind to Selected, or let a command fire if a item is selected.
Background:
I want to start loading data based on the selected Item, the start triggger should be something like IsSelected or LostFocus
Any help or a different/better way of archive this would be very helpfully.
Thank you very much
There are couple of ways
1 - define your binded property as a full property and do the manipulations when the value changes:
private string myString;
public string MyString
{
get { return myString; }
set
{
myString = value;
// do your stuff here...
}
}
2 - Use interactivity and convert event to command:
<ComboBox ItemsSource="{Binding MyCollection}">
<i:Interaction.Triggers>
<i:EventTrigger EventName="SelectionChanged">
<i:InvokeCommandAction Command="{Binding SelectedItemChangedCommand}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</ComboBox>
Don't fordet to add the needed namespace:
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
And implement the SelectedItemChangedCommand in your ViewModel

Binding custom events of custom elements in MVVM Pattern

I'm trying to bind the "DataClick" event of LiveChart's Cartesian Chart element using MVVM pattern.
I have my Charts.xml like this:
<ContentControl Grid.Row="0">
<lvc:CartesianChart x:Name="ContrastChart" Series="{Binding ContrastSeriesCollection}">
<i:Interaction.Triggers>
<i:EventTrigger EventName="DataClick">
<i:InvokeCommandAction Command="{Binding ChartDataClick}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</lvc:CartesianChart>
</ContentControl>
This is my ICommand ChartDataClick on my ViewModel:
public ICommand ChartDataClick {
get
{
if(_dataClickCommand == null)
{
_dataClickCommand = new DelegateCommand(
() =>
{
MessageBox.Show("Data Clicked!");
}
);
}
return _dataClickCommand;
}
}
If I switch e.g "DataClick" for "MouseEnter" I get my command fired.
So I'm assuming that the problem is that the DataClick is a custom event.
Anybody knows a workaround for this?
I really tried everything I could find on Google that could help, but nothing so far...
LiveCharts Events: Events Documentation
The EventTrigger doesn't discriminate.
We can check this by implementing MyButtonSimple which has a custom Routed Event Tap.
We can go from handler in code behind
<custom:MyButtonSimple
x:Name="mybtnsimple" Tap="mybtnsimple_Tap"
Content="Click to see Tap custom event work">
</custom:MyButtonSimple>
To a ViewModel ICommand
<custom:MyButtonSimple
x:Name="mybtnsimple"
Content="Click to see Tap custom event work">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Tap">
<i:InvokeCommandAction Command="{Binding Command}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</custom:MyButtonSimple>
And everything works as expected
The shortcoming of these triggers is that they have to be placed on the UIElement raising the event.
In other words, they ignore Bubbling or Tunneling events. That's why there is no Interaction.Triggers alternative for:
<Grid custom:MyButtonSimple.Tap="mybtnsimple_Tap">
<custom:MyButtonSimple
x:Name="mybtnsimple"
Content="Click to see Tap custom event work">
</custom:MyButtonSimple>
</Grid>
To sum it up, the DataClick event isn't raised on the CartesianChart (but further down the Logical Tree) and therefore you can't handle it this way.

WPF Command: parameter from user control

I'm in my MainWindowView.xaml. It includes a usercontrol.
I'm trying to set a command with a parameter. This parameter is the selected row of a gridControl (devexpress item).
I have tried two binding, both wrong (they don't find the parameter):
<Button Command="{Binding DeleteCommand}" CommandParameter="{Binding Path=lst1, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type uc:ucImpianti}}}" Style="{DynamicResource BtnToolBar}"/>
and
<Button Command="{Binding DeleteCommand}" CommandParameter="{Binding ElementName=lst1, Path=FocusedRow}" Style="{DynamicResource BtnToolBar}"/>
How have I to write the binding to pass the selected row of a gridControl in a UC?
My command defition is:
public ICommand DeleteCommand { get; private set; }
private void DeleteRecord(object parameter)
{
Debug.WriteLine(parameter);
}
[...]
DeleteCommand = new DelegateCommand<object>(DeleteRecord, CanAlways);
It is customary in WPF to data bind a collection of a certain type to the ItemsSource property and a property of the type of object in the collection to the SelectedItem property (it makes no difference that this example uses a ListBox):
<ListBox ItemsSource="{Binding YourCollection}"
SelectedItem="{Binding YourSelectedItem}" ... />
With this set up, you can data bind directly to the YourSelectedItem property from the CommandParameter property:
<Button Command="{Binding DeleteCommand}" CommandParameter="{Binding YourSelectedItem}"
Style="{DynamicResource BtnToolBar}" />
a more general answer would be:
if you wanna access an object/property from a usercontrol, then the UserControl should expose the object/property with a Dependency Property and you can bind to this DP.
another way would be to check if the DataContext of the Usercontrol expose the property then bind to the Datacontext.Property of the usercontrol.
so for your case i would need some more information of your Viewmodel and View bindings

Use a Command with TabItem

I would like to call a Command when a TabItem of my TabControl is selected.
Is there a way to do it without breaking the MVVM pattern ?
Use an AttachedCommand Behavior, which will let you bind a Command to WPF events
<TabControl ...
local:CommandBehavior.Event="SelectionChanged"
local:CommandBehavior.Command="{Binding TabChangedCommand}" />
Of course, if you're using the MVVM design pattern and binding SelectedItem or SelectedIndex, you could also run the command in the PropertyChanged event
void MyViewModel_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == "SelectedIndex")
RunTabChangedLogic();
}
It can be done using the following classes together:
EventTrigger class from the System.Windows.Interactivity namespace (System.Windows.Interactivity assembly).
EventToCommand class from the GalaSoft.MvvmLight.Command namespace (MVVM Light Toolkit assembly, for example, GalaSoft.MvvmLight.Extras.WPF4):
XAML:
<Window ...
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
xmlns:cmd="clr-namespace:GalaSoft.MvvmLight.Command
...>
...
<TabControl>
<i:Interaction.Triggers>
<i:EventTrigger EventName="SelectionChanged">
<cmd:EventToCommand Command="{Binding TabSelectionChangedCommand}"
PassEventArgsToCommand="True" />
</i:EventTrigger>
</i:Interaction.Triggers>
<TabItem>...</TabItem>
<TabItem>...</TabItem>
</TabControl>
...
</Window>
Create an instance of the command in the ViewModel constructor:
TabSelectionChangedCommand = new RelayCommand<SelectionChangedEventArgs>(args =>
{
// Command action.
});

Categories