GalaSoft_MvvmLight_Command:EventToCommand on a button on a ListItem? - silverlight

What is the syntax for hooking up a mvvm-light EventToCommand on a line item in a data template? For an action on the main model, syntax like the below works fine, however if I am doing an operation on a line item in a data template, the binding isn't working, and I need to identify the specific line item to operate on.
Before attempting to hook up the Event to Command I had the line item click hooked to an event handler in the XAML codebehind; the handler extracted the line item data object out of the event args, and then passed the line item data object to a method via the DataContext to the view model and that worked fine, but I wanted to stay consistent with handling across the app.
Runtime error in output:
System.Windows.Data Error: BindingExpression path error: 'EditLineCommand' property not found on 'Model.LineItem'. BindingExpression: Path='EditLineCommand' DataItem='Model.LineItem'; target element is 'System.Windows.Controls.Button' (Name='EditRowButton'); target property is 'DependencyPropertyListener39' (type 'System.Object')..
XAML main layout:
<!-- Line Items -->
<ListBox ItemTemplate="{StaticResource LineItemTemplate}" ItemsSource="{Binding Model.LineItems}"/>
XAML data template:
<DataTemplate x:Key="LineItemTemplate">
<Button>
<Image Source="..." />
<Custom:Interaction.Triggers>
<Custom:EventTrigger EventName="Click">
<GalaSoft_MvvmLight_Command:EventToCommand Command="{Binding EditLineCommand, Mode=OneWay}" />
</Custom:EventTrigger>
</Custom:Interaction.Triggers>
</Button>
</DataTemplate>
Update - I think I'm almost there, #Claus' answer got me most of the way by solving the Command binding problem. To identify the specific line to operate on, I bind to the LineNumber of the LineItem and then pull that parameter out on the relay command:
<GalaSoft_MvvmLight_Command:EventToCommand
Command="{Binding DataContext.DeleteLineCommand, ElementName=DetailPage}"
CommandParameter="{Binding LineNumber}"
PassEventArgsToCommand="True" />
...
public RelayCommand<int> DeleteLineCommand { get; private set; }
...
DeleteLineCommand = new RelayCommand<int>((ln) => { DeleteLineItem(ln); });
This is a workable solution, but is there a way to bind to the full LineItem rather than just a member?

See Bind datagrid to one ViewModel, column / combobox to another for a discussion on how to bind to a property (or command) of a view model from a data template.

Magic trick:
<phone:PhoneApplicationPage x:Name="MyPage" ... >
...
<GalaSoft_MvvmLight_Command:EventToCommand
Command="{Binding DataContext.EditLineCommand, ElementName=MyPage}" />
...
</phone:PhoneApplicationPage>
That way, it'll use the Page's DataContext, which usually is your ViewModel.

Related

How to bind DataGrid Sorting event to Prism ViewModel DelegateCommand

How do I bind the sorting command of my datagrid to view model?
Below is my XAML Code
<DataGrid ItemsSource="{Binding ViewModels}"
CanUserSortColumns="True"
Sorting="{Binding ViewModel_SortingCommand}">
</DataGrid>
Below is the ViewModel implemented that cause the error in binding
ViewModel_SortingCommand = new DelegateCommand<DataGridSortingEventArgs>(ViewModel_Sorting;
public void ViewModel_Sorting(DataGridSortingEventArgs args)
{
// Error on binding
}
Since Sorting is an event, you cannot bind it directly, but you can use an EventTrigger.
<DataGrid ItemsSource="{Binding ViewModels}"
CanUserSortColumns="True">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Sorting">
<i:InvokeCommandAction Command="{Binding ViewModel_SortingCommand}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</DataGrid>
If you use the legacy blend behaviors shipped with Blend, use this namespace:
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
If you use the new Microsoft.Xaml.Behaviors.Wpf Nuget package, use this namespace:
xmlns:i="http://schemas.microsoft.com/xaml/behaviors"
If you need to process the event args in your command, set PassEventArgsToCommand to True:
<b:InvokeCommandAction Command="{Binding FavContextMenuEditCmd}" PassEventArgsToCommand="True"/>
Also note, that there is an EventArgsParameterPath property to specify a property in the event args that should be passed to the command and an EventArgsConverter property to use a converter. These are useful to avoid passing UI related types like event args to your view model.
Passing a DataGridSortingEventArgs to a view model breaks the MVVM pattern.
You should either perform the sorting in the view/control or, if you are really interested in the sort order in the context of the view model, sort the actual source collection that the view binds to. In general, the view model isn't interested in or aware of how the user sorts, groups or filter the data in the view.
Either way, the view model should not depend on a DataGridSortingEventArgs or anything else that is related to the DataGrid control in the view.

WPF: why my ComboBox Selected Value Command return wrong value [duplicate]

I'm using MVVM and custom ICommand objects are provided by ViewModel layer. One ViewModel object at the same time can be attached via DataContext property to many View objects (windows, pages, etc). In ICommand.CanExecute() I want to check absence of validation errors for some controls in View (which attached to ViewModel props, significant for a particular VM command). One ViewModel can provide many commands, each of them has own set of controls for errors validation verification. So, pseudo-XAML is:
<Button.CommandParameter>
<x:Array Type="sys_win:DependencyObject">
<sys_win:DependencyObject>
<reference_to_textbox_or_other_control/>
</sys_win:DependencyObject>
<sys_win:DependencyObject>
<reference_to_textbox_or_other_control/>
</sys_win:DependencyObject>
</x:Array>
</Button.CommandParameter>
The second problem is that the particular command may be invoked by control, which itself is the part of the DataTemplate for collection item (in my case - part of ListBoxItem data template). My templated listbox item has two text boxes (binded to two props of corresponding ViewModel) and button, which invoke the ViewModel command. So, in command CanExecute() I need to check for validation errors for some window controls & two text boxes, which belongs to this listitem, not other items. The code below works fine if I want to pass ListBoxItem.IsSelected property as CommandParameter:
<Button DataContext="{Binding}"
Command="{Binding Path=SwitchCommand}"
CommandParameter="{Binding Path=IsSelected, RelativeSource={
RelativeSource
Mode=FindAncestor,
AncestorType={x:Type ListBoxItem}}}"/>
But how can I pass whole (DependencyObject)ListBoxItem as the CommandParameter? And how this ListBoxItem, passed via {Binding RelativeSource} can be mixed with other current window controls in the first code example?
I'm very sorry, but how can I add the references to controls in xaml?
<Button.CommandParameter>
<x:Array Type="sys_win:DependencyObject">
<sys_win:DependencyObject>
<reference_to_textbox_or_other_control/>
</sys_win:DependencyObject>
<sys_win:DependencyObject>
<reference_to_textbox_or_other_control/>
</sys_win:DependencyObject>
</x:Array>
</Button.CommandParameter>
Just use a binding with no Path :
<Button DataContext="{Binding}"
Command="{Binding Path=SwitchCommand}"
CommandParameter="{Binding RelativeSource=
{RelativeSource
Mode=FindAncestor,
AncestorType={x:Type ListBoxItem}}}"/>
I'm not sure if I'm reading your example correctly, but it seems to violate a bit of the MVVM principle. (My apologies if I read it incorrectly).
The idea behind MVVM is to decouple the viewmodel from any dependency on a XAML / View entity. You're breaking that by having the CommandParameter dependent on the usercontrol. What I would do is create state properties in the ViewModel and bind the usercontrol validations to those states, then in CanExecute you can test the values of those properties rather than trying to bind to a usercontrol.

WPF: How to bind MouseEnter event from an ItemsControl.ItemTemplate to elements outside this ItemsControl

I'm new to WPF but I manage to advance slowly towards writing my first serious project. I'm not sure I use the correct terms so please bear with me.
I implemented a set of ItemsControl (User Controls). The item source is a collection of items. Each item holds much data including it's own ID. I would like the User Control to change a property when the mouse hovers over another control outside this set of ItemsControl.
To complicate things the other control is also an element of another set of ItemsControl.
I already managed to store the ID of the hovered-over control in a top-level property (In the top DataContext) but I can't find a way to bind to it from within the element buried inside the DataTemplate.
Here's a screenshot:
In this example the user hovers over channel 14 - as a result, bottom axes X and Z should highlight (I chose them arbitrarily - according to data stored in the database).
I'd be grateful for any idea. Examples will be most welcome!
Assuming that:
1) You are using the MVVM design pattern.
2) That there is an underlying ViewModel for each of the items in your ItemsControl.
Then what you need to do is handle the MouseEnter and MouseLeave events in your view and have them trigger Commands in your view model. Then have those ViewModels update properties in your other ViewModels to have them highlight the appropriate items in the other ItemsControl.
<UserControl x:Class="ClassName"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
xmlns:cmd="clr-namespace:GalaSoft.MvvmLight.Command;assembly=GalaSoft.MvvmLight.Extras.WPF4">
<i:Interaction.Triggers>
<!-- this will call the associated commands on your viewmodel when the mouse enters/leaves the corresponding view in your itemscontrol, from there you can create the viewmodel functionality you'd like-->
<i:EventTrigger EventName="MouseEnter">
<i:InvokeCommandAction Command="{Binding MouseEnterCommand}"/>
</i:EventTrigger>
<i:EventTrigger EventName="MouseLeave">
<i:InvokeCommandAction Command="{Binding MouseLeaveCommand}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
<Grid Height="10" Width="10">
<!--the content of your usercontrol-->
</Grid>
</UserControl>
Once you get the correct commands notifying your viewmodel that the mouse is hovering over it (or left it). You can manage the state of your viewmodels to create the affects you are looking for.
I really like the Blend SDK for stuff like this.
Since you mentioned you have the hovered-over ID setting in a property, you could start with a PropertyChangedTrigger at the root level. Next, you will probably want to invoke a command (rather than just a method), since your action includes a parameter (the ID). Use InvokeCommandAction for this. You can trigger a command either on the view or the view-model. If you want to trigger it on the view, then you'll probably want to use ElementName as the binding.
Here's an example.
<UserControl>
<i:Interaction.Triggers>
<!-- When "SelectedID" changes, invoke the "SelectedIDChangedCommand" on the
element "AxesAndButtons". Pass the value of "SelectedID" as the
command parameter -->
<ei:PropertyChangedTrigger Binding="{Binding SelectedID}">
<i:InvokeCommandAction CommandParameter="{Binding SelectedID}"
Command="{Binding ElementName=AxesAndButtons,Path=SelectedIDChangedCommand}" />
</ei:PropertyChangedTrigger>
<i:Interaction.Triggers>
<my:AxesAndButtonsControl x:Name="AxesAndButtons">
</my:AxesAndButtonsControl>
</UserControl>
I have assumed that ID property that gets changed is called "SelectedID" (and is a property of the root data context). Also, that your target user control has a defined ICommand "SelectedIDChangedCommand" dependency property that performs the update. That is, something like this:
public class AxesAndButtonsControl : UserControl
{
public static readonly DependencyProperty SelectedIDChangedCommand = DependencyProperty.Register("SelectedIDChangedCommand",
typeof(ICommand),
typeof(AxesAndButtonsControl),
new PropertyMetadata(null));
public AxesAndButtonsControl()
{
SelectedIDChangedCommand = new DelegateCommand(id => {
// highlight "X" and "Z" or whatever
});
}
}
Edit I just noticed that maybe you haven't bound the MouseOver event to update the SelectedID property yet. If that's the case, then you should be able to use an EventTrigger along with a ChangePropertyAction (in a FindAncestor or ElementName binding) to update it. Something like this:
<DataTemplate>
<Grid>
<i:Interaction.Triggers>
<!-- When "MouseEnter" gets triggered, set the property "SelectedID" on the
data context of the closest "UserControl" parent to the value of "ItemID"
in the current data context -->
<i:EventTrigger EventName="MouseEnter">
<ei:ChangePropertyAction Value="{Binding ItemID}"
TargetObject="{Binding RelativeSource={RelativeSource AncestorType=UserControl},Path=DataContext}"
PropertyName="SelectedID" />
</i:EventTrigger>
</i:Interaction.Triggers>
</Grid>
</DataTemplate>

How to trigger ViewModel command for a specific button events

How can a command on a ViewModel be invoked by a specific event of a button, such as MouseDoubleClick?
You can use the EventTrigger in the System.Windows.Interactivity namespace, which is part of the so-called Prism framework. If you're just getting started with MVVM, don't care too much for Prism by now, but keep it in mind for later. Anyway, you can steel the EventTrigger
It works like this:
Reference the assembly System.Windows.Interactivity.dll
In XAML, reference the namespace:
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
Then in your Button or any other control, add a EventTrigger like this:
<Button Content="Button">
<i:Interaction.Triggers>
<i:EventTrigger EventName="MouseDoubleClick">
<i:InvokeCommandAction Command="{Binding CommandToBindTo}"
CommandParameter="{Binding CommandParameterToBindTo}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</Button>
This way, you bind your event to a Command on your DataContext.
Remark
To clarify the usage, here's a kind of real life example including the ViewModel. The fictional requirement is to allow the user to select an item in a list and then perform a command which takes the selected item as a parameter:
<ListBox x:Name="ItemsList" ItemsSource="{Binding Items}" />
<Button Content="Do something with selected item">
<i:Interaction.Triggers>
<i:EventTrigger EventName="MouseDoubleClick">
<i:InvokeCommandAction Command="{Binding DoSomethingCommand}"
CommandParameter="{Binding SelectedItem,
ElementName=ItemsList}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</Button>
And that would be the ViewModel. Note how the parameter to the command is used, in the example with a generic version of a DelegateCommand object as you get it in every MVVM framework (sometimes RelayCommand). This class takes the type of the required parameter as a generic parameter (here ItemViewModel) and requires a method which takes an according parameter (here ExecuteDoSomethingWithItem(ItemViewModel ...)). The rest is WPF magic: The oject to which the CommandParameter property is bound in your XAML will be passed through as the parameter in your Execute(...) function.
public class ViewModel
{
ObservableCollection<ItemViewModel> Items { get; set; }
public ICommand DoSomethingCommand
{
get
{
return _doSomethingCommand ??
(_doSomethingCommand = new DelegateCommand<ItemViewModel>(ExecuteDoSomethingWithItem));
}
}
private DelegateCommand<ItemViewModel> _doSomethingCommand;
private void ExecuteDoSomethingWithItem(ItemViewModel itemToDoSomethingWith)
{
// Do something
}
public ViewModel()
{
Items = new ObservableCollection<ItemViewModel>();
// Fill the collection
}
}
Have fun with learning MVVM, it's worth it.
you can use attached command behaviors
=> http://geekswithblogs.net/HouseOfBilz/archive/2009/08/21/adventures-in-mvvm-ndash-generalized-command-behavior-attachments.aspx
You need to do a lot of pluming yourself if you going to use Command and Event Binding from out of the box WPF. You can gain a lot of just using existing framework such as MVVM Light Toolkit, or Cliburn Micro that already provide command and even binding.

Button Command not binding to ViewModel in DatGrid

I have ViewModel, Which has a ObservableCollection[Employee] EmpCol , now that ViewModel is bind with my View and and EmpCol is set as with ItemSource of custom control. That custom control generates stackpanels with grids , if there are 4 objects in EmpCol then there will be 4 stackpanels with Grids inside them. Now in those grids, I have a column of Buttons.
Now my problem is I can't bind Command of that Button in datagrid to RelayCommand in Employee class.
I am using MVVM light toolkit, and I found a way around which actually kind of give full path to binding command like this
<Button x:Name="myButton" Width="20" Height="15" HorizontalAlignment="Center" VerticalAlignment="Center" Margin="5" >
<i:Interaction.Triggers>
<i:EventTrigger EventName="Click">
<cmd:EventToCommand Command="{Binding Source={StaticResource VMLocator}, Path=MyVM.Employee.MyButtonCommand}" PassEventArgsToCommand="False"/> </i:EventTrigger>
</i:Interaction.Triggers>
</Button>
Now this above approach works , but in my case I dont have Employee , but collection of Employee, so when I give binding path like this
Path=MyVM.EmpCol.MyButtonCommand
It does't work. I have searched a lot , but could't find any solution.
So any help will be much appreciated to solve my problem.
Thanks,
Maverick
The command binding happens inside a row of a DataGrid.
The tsolution depends on the structure of your view, if you have a master detail view you should create a sub-structure that expects an employee ViewModel as a DataContext and bind that to the currently selected employee. With that you get the correct ViewModel for your view.
If you navigate to a new page the easiest approach might be to to pass the View the id of the employee you want to show and then in code behind call a method on you view model - which you can get from the DataContext propery - to load the correct employee from the database or service.
Alternatively, you can hold the current employee in a application wide acessible variable. E.g. a static variable on your application class, a singleton or static class stored in you resources, or on your ViewLocator (in all cases you may have to provide an empty employee ViewModel when no employee is selected or prevent the opening of the View).
Edit:
If a collection of ViewModels is bound to a DataGrid and binding of the command is a problem, this might be a possible solution:
Put the command on a ViewModel that contains the Collection (i.e.the propety that is bound to the ItemsSource of the DataGrid). Then implement the command on this high level ViewModel. You now can use a RelativeSurce binding to access this higher level ViewModel via the DataGrid's parent DataContext property. In your button you can now bind the command and pass the current row's DataContext as the command parameter - the command obviously has to expect this parameter.
Edit 2:
Forgot Silverlight does not support all RelativeSource modes, however, it supports binding to a named element, so the following should work ...
Assumptions:
The higher level ViewModel is called "EmployeeListViewModel"
It has a property called Employees containing the list of EmployeeViewModels
The DataContext of the element containing the DataGrid is set to EmployeeListViewModel
Code:
<DataGrid x:Name="dgEmployees" ItemsSource="{Binding Empoyees}" ...>
...
<Button x:Name="myButton" Width="20" Height="15" HorizontalAlignment="Center"
VerticalAlignment="Center" Margin="5" >
<i:Interaction.Triggers>
<i:EventTrigger EventName="Click">
<cmd:EventToCommand
Command="{Binding ElementName=dgEmployees, Path=DataContext.MyButtonCommand}"
CommandParameter="{Binding}"
PassEventArgsToCommand="False"/>
</i:Interaction.Triggers>
</Button>
...
</DataGrid>
The easiest way is using the Relay command for example
public RelayCommand SearchCommand
{
get
{
return new RelayCommand(GetData);//GetDatais a method for your action on click event
}
}
for Detail you can see at following link
http://codenicely.blogspot.com/2012/01/handling-button-click-event-in.html

Resources