How to bind to a method on ViewModel with data from View - wpf

I currently have one view with 3 fairly simplistic view models. For the sake of this discussion, we will focus on 2 of the three view models.
The View is a User Management user control. It contains a DataGrid that has its ItemsSource binding set to a UserListViewModel. This view model simply displays user information in the data grid.
The User Management View also contains some other controls, such as buttons for adding new users and removing users. Those buttons are currently bound to a second view model called UserManagementViewModel. For example, the Remove button will successfully call the RemoveUser method on the UserManagementViewModel.
My question is, via XAML (as I hate code-behind), how can I pass the SelectedItem property of the DataGrid (bound to UserListViewModel) into the RemoveUser method call on the UserManagementViewModel? I realize that, in the MVVM design pattern, my view model can't look into the view to retrieve the information necessary, so there must be a way via binding to pass that information into the method.
XAML code examples (or links that show how) to perform similar functionality would be appreciated. Thanks for any help!

you can simply use a commandparameter
<Button Command="{Binding RemoveCommand} CommandParameter="{Binding Elementname=gridUser, Path=SelectedItem}" />
or your UserManagementViewModel have access to the UserListViewModel then you need a command without commandparameter and simply use the SelectedUser property of your UserListViewModel instance
public void ExecuteRemove()
{
var userToRemove = this._myUserListViewModelinstance.SelectedUser;
...
}

I believe what you seek is commanding with a command target bound to the datagrid's selecteditem where one can route such information from the datagrid; say when a button is pressed.
See Commanding Overview on MSDN

Related

How Bind Child Control To A View Object (Like PageCollectionView) In Master\Detail scenario With MVVM In SILVERLIGHT

I have a question about “Master\Detail” data binding using MVVM and I hope you can help me with it:
Let say my Master ModelView is “ProductModelView” (for Product Entity) and the Product Entity has a property named ProductInventories of type Entity Collection. I defined a “DomainCollectionView” in my master ProductModelView with name: “ProductViewDCV”
I do following items to fill my master and details Datagrids:
1-Set the DataContext of my page to my ModelView : ProductModelView
2-Bind master Datagrid (name: masterDG) to ProductViewDCV.
3-Bind detail Datagrid (name: detailDG) to ProductInventories property of my master ProductViewDCV :
<sdk:DataGrid x:Name=" detailDG "
ItemsSource="{Binding Path= ProductViewDCV. ProductInventories }"/>
The Program works well ,As I change the row in master DataGrid , The current item in the child DataGrid also changes.
But my question and Problem :
When I bind my detail (child) DataGrid as mentioned in step 3 , the child DataGrid is bound to “EntityCollection” not a View Object Like “PagedCollectionView” or “DomainCollectionView” ,So I can’t use benefits like Sorting ,Grouping and Paging on my child (detail) DataGrid. I want to know :How I can bind my child DataGrid to a View Object in MVVM pattern to use Paging option.(Namely I want to have Paging and Grouping Options in my child DataGrid.)
Look forward to receiving your reply.
Hello.
I really appreciate your kind and quick answer. I did as you said ,but I encounter a problem and I hope you help me:
Method:
In my “ProductModelView” (for Product Entity) I defined following objects:
1-private DomainCollectionView _ProductViewDCV; (As Master(Parent) View)
2-private PagedCollectionView _ProductViewPCV; (As Detail(Child) View)
3-In Constructor of “ProductModelView” class, I assigned an EVentHandler to “_ ProductViewDCV”(my DomainCollectionView as Master view):
this._ProductViewDCV.CurrentChanged+=_ProductViewDCV_CurrentChanged;
this._ProductViewDCV.Refresh();
4-In the EventHandler , I Create a new “PagedCollectionView” (my Detail/Child view) and assign it to my _ProductViewDCV :
if (_ProductViewDCV.CurrentItem != null)
{
_ProductViewPCV = new PagedCollectionView((_ProductViewDCV.CurrentItem as BA1.Web.Product).ProductInventories);
//ProductViewPCV.Refersh();
}
5-In my View File (XAML file), I have two Datagrids which are configured as follow :
Page DataContext is set to an object of my ModelView : ProductViewModel
sdk:DataGrid x:Name="dgParent" Grid.Row="1" ItemsSource="{Binding Path=ProductViewDCV}"//my Master/Parent DataGrid
sdk:DataGrid Grid.Row="2" x:Name="dgDetails" ItemsSource="{Binding Path=ProductViewPCV}//My Detail/Child DataGrid
Problem :
When I change the current row in my Master/Parent DataGrid , nothing happens in Detail/Child DataGrid and testing the Detail/Child DataGrid’s Itemssource property , returns “Null” ,not an object of “PagedCollectionview”.
Where’s my problem and why doesn’t the Detail/Child DataGrid get updated in response to Master/Parent current item changing? (Testing shows that ProductViewPCV itself changes when it’s parent collection changes.)
Again let me thank you for giving me advices on this problem.
Off the top of my head, there's a few problems I can see. You should instantiate the _ProductViewPCV PagedCollectionView property in the constructor, and not create new instances of it in the _ProductViewDCV_CurrentChanged event handler. If you did want to do this, then you need to notify the view that the value of this _ProductViewPCV property has changed by raising a PropertyChanged event. However, you shouldn't do this as it's likely to result in a memory leak.
You'd be best off wrapping an observable collection in the PagedCollectionView, and simply updating the contents of the observable collection in the _ProductViewDCV_CurrentChanged event handler I think. The view should update automatically I think. Sorry, I'm a little rusty on this, but I think that should work for you.

WPF DataBinding with MVVM and User Controls

Let's say I have a Customer Window showing information about a customer, like Name, address and phone number. On the bottom there is a DataGrid of their orders. Of course the Customer has an Orders property, so if you're using MVVM, you would just set:
ItemsSource = "{Binding Customer.Orders}"
However, now let's say that the data grid is now part of a user control, which also includes controls for editing/adding/removing Orders. I want to use this same set of controls in multiple places, and I would like all the logic for editing/adding/removing Order objects to be encapsulated in the user control. And because I want to use commands, rather than event handlers, I would like the user control to have its own view model.
So now the question is: how do I pass the orders from the Customer view model to the Orders user control's view model? Because the Orders user control will be bound to a view model, I can't say:
<local:OrdersUserControl DataContext="{Binding Customer.Orders}" />
because the user control has it's own view model. It would expect to see Customer.Orders there, and of course it's not.
I guess this is kind of a chicken or the egg situation.
Your help is always appreciated.
Aaron
And now for my weekly "don't do that" answer...
I can't say ... because the user control has it's own view model.
To which I say
Creating a ViewModel for your UserControl is a code smell.
You're experiencing this issue because of that smell, and it should be an indication that you're doing something wrong.
The solution is to ditch the VM built for the UserControl. If it contains business logic, it should be moved to an appropriate location in another ViewModel.
You should think of a UserControl as nothing more than a more complex control. Does the TextBox have its own ViewModel? No. You bind your VM's property to the Text property of the control, and the control shows your text in its UI.
MVVM doesn't mean no codebehind. Put your UI logic for your user control in the codebehind. If it is so complex that you need business logic inside the user control, that suggests it is too encompassing. Break it down into two or more.
Think of UserControls in MVVM like this--For each model, you have a UserControl, and it is designed to present the data in that model to the user. You can use it anywhere you want to show the user that model. Does it need a button? Expose an ICommand property on your UserControl and let your business logic bind to it. Does your business logic need to know something going on inside? Add a routed event.
Normally, in WPF, if you find yourself asking why it hurts to do something, it's because you shouldn't do it.
There are many ways to skin a cat. The way I've been doing this is by having my CustomerViewModel have a property of type OrdersViewModel. Then in the constructor (or wherever you are setting the customer) have it set the "OrdersContext" with a new OrdersViewModel passing in the customer.orders.
XAML:
<local:OrdersUserControl DataContext="{Binding OrdersContext}" />
ViewModel:
public CustomerViewModel(Customer customer)
{
Customer = customer;
OrdersContext = new OrdersViewModel(customer.Orders);
}

Silverlight 4 MVVM DataGrid AND Child Window Pass Data back to Parent Window

I need Some help. I have a Silverlight application the parent form has a search button that when clicked loads a ModalDialog that has 3 text boxes, two button(for searchin and resetting) and a DataGrid (telerik gridview, but I can change it to any grid so not a problem). I enter a search criteria on one of the buttons, say the last name, and all the records with that particular lastname get loaded on the grid. I need to be able to select a row on the grid and having done so, the details of the selected row should be updated on the controls on the parent window (there is no grid on the parent window, its just like a data entry form). I am using MVVM. How do I acheive this while keeping true to the MVVM pattern? I have just seen alot of stuff on passing data from parent to child none on child to parent/calling window.
Any help, and some simple code example, will be highly appreciated. Been on it for a whole 3days and aint figuring it out as yet.
Francis.
I've done this in the Mvvm light toolkit using messaging. It uses something called "PubSub", which means "Publish a message, Subscribe for a message"
I use a command in the GridView of my Modal window. Here's the XAML for that:
<i:Interaction.Triggers>
<i:EventTrigger EventName="SelectionChanged">
<cmd:EventToCommand Command={Binding ItemSelectedCommand} />
</i:EventTrigger>
</i:Interaction.Triggers>
Depending on what flavor (if any) of MVVM toolkit you're using, the syntax might be different. In my case, as I said, MVVM light. Ultimately I'm using the iCommand interface to pass this command to my view model. In my case, the cmd reference is pointing to Galasoft.MvvmLight.Command:
xmlns:cmd="clr-namespace:GalaSoft.MvvmLight.Command;Assembly=GalaSoft.MvvmLight.Extras.SL4"
In the ViewModel for my child window, I register for the command, and when it's triggered, I call this line:
Messenger.Default.Send<TSMVVM.Model.WorkOrders>(SelectedWorkOrder);
This line Publishes (sends) a message of type WorkOrders (which is a model class in my app), passing through the SelectedWorkOrder. This line of code sits inside of a Command, which is fired off on the SelectionChanged event of my gridview. Example code for this is:
_ItemSelectedCommand = new RelayCommand(WorkOrderSelected);
private void WorkOrderSelected()
{
Messenger.Default.Send<TSMVVM.Model.WorkOrders>(SelectedWorkOrder);
}
In my parent window, in the constructor, I have this:
Messenger.Default.Register<TSMVVM.Model.WorkOrders>(this, SetWorkOrder);
This line registers (or Subscribes) for a message of type WorkOrders. Any time a message is sent through my app, of type WorkOrder, it calls a function: SetWorkOrder, which is below:
private void SetWorkOrder(TSMVVM.Model.WorkOrders wo)
{
this.SelectedWO = wo;
}
My DataForm has an ItemsSource bound to WorkOrders, and a CurrentItem bound to SelectedWO, set with TwoWay binding. So when I update the SelectedWO (implementing INotifyPropertyChanged, via Mvvm light toolkit's viewmodelbase), the DataForm updates accordingly.
If you need more info, let me know!
Scott

"Nested" MVVM Question

I have a WPF window that has a datagrid and a user control for a form for the fields in that datagrid. The user control and the WPF window have view models.
The user control's DataContext is bound to one of the window's view model's member field, whose value changes during the data grid's Selection Changed event.
I'm not sure if this is the right way to do it because I am unable to create references from the inner view model to the outer view model for some reason. Constructor injection won't work because I'm required to use default constructor only, and I can't seem to put a property injector in the right place (always getting null reference when I try to use it).
I am also unable to get my property change notification to work properly in the inner view model.
Is there a better way to wire my view models so that they automatically change the values in the user control when a new row is selected in the datagrid? I have a feeling that binding to the control's DataContext is not the way to go.
This doesn't seems a complex/nested scenario. Looks like pretty much a normal master details scenario. Suppose you want to edit Customer data, I would have an ObservableCollection instance bind to the DataGrid, and there will be a SelectedCustomer property in the VM also. In the DataGrid you can set SelectedItem twoway bind to the SelectedCustomer property which makes SelectedCustomer always updated with your selection. Since the usercontrol has the same instance of customer as in the DataGrid row, whenever you change anything in the UC those data will get reflected in the grid. Ofcourse all those properties should fire NotifypropertyChanged.

List<> Binding and button click using mvvm light

I am trying to use MVVM light to achieve something like this. I have the following scenario:
In my Model--I have set the properties like ActivityName,Image and there is a class constructor whose is accepting 2 parameters like name and image.
Im my DataAccess--I have set the database connection and implement the required method who will fetch the data from DB and I am storing in the List and returning the list to ViewModel.
In my ViewModel--I have created the list property who will return the list by calling the GetActivities() method that I have defined in the DataAccess.
Now my problem is I am not getting how to bind it in the View so that by clicking on a button it will display the list of activities with image. By clicking on some button a new window should open with the desired result. How to bind the above list and implement the button functionality using MVVM light.
Kindly help?
Thanks
First of all, use an ObservableCollection instead of a List as it would notify the view when property or collection changes.
Then set the DataContext of your view to the viewmodel. If you use MVVMLight View Class, then the DataContext would be automatically set. You've to just give the ViewModel Name there.
Then set the ItemsSource of the DataGrid like this <dg:DataGrid ItemsSource="{Binding YourListInViewModel}"/>
For handling click event you can use the Event-To-Command behaviour and write your logics in the Button's corresponding Command handler.
Bind to DataContext of the control

Resources