I have two view models: ListItemViewModel and ListViewModel. My ListViewModel contains many ListItemViewModel objects, when I try to delete one item, I'm binding DeleteCommand in ListItemViewModel like this:
public IMvxCommand DeleteCommand => new MvxCommand(DeleteCommandHandler);
The problem is that DeleteCommandHandler is in ListViewModel... How can I call handler from another view model?
UPDATE 1
Got the question why do I need this. The reason of my question is that in handler I will need to make an API call which requires dependency injection, but my ListItemViewModel must have default empty constructor because of automapper, so all the business logic I want to move to "parent view model" which is ListViewModel
Try using messaging services to communicate b/w different viewmodels. In your case, the DeleteCommandHandler will trigger in ListItemViewModel and then will pass a message to ListViewModel sending details of item deleted.
Refer to the link on how to implement Messaging.
Related
I am currently developing a UWP application, but I think this question applies to any project type with a UI. I have built a View Model for my UI using the new Microsoft Toolkit MVVM library. It has properties such as:
private bool _isLoginAvailable = true;
public bool IsLoginAvailable
{
get => _isLoginAvailable;
set => SetProperty(ref _isLoginAvailable, value);
}
Furthermore, I have a few business methods that as parameters require up to 5-6 of these properties.
Reading on forums, I saw that it is unadvised to business logic within the view model, therefore, I came up with the following options:
Create a new class for the methods, and use the view model as a parameter: SampleMethod(SampleViewModel vm). Then, if I create an object of this class in the view model, I could use SampleMethod(this). At this point, I don't really see a difference between this option, and including the method within the view model class.
Second option I see is to add each required parameter to the method, and return each parameter as well in a tuple: SampleMethod(var1, var2, var3...) { return (var1, var2, var3...)} This to me seems very cumbersome.
The third option I figured is to use the MVVM Toolkit's messaging feature. In this case, I can set up the constructor of the view model to listen to messages with Messenger.Register<SampleViewModel, Var1Message>(this, (r, m) => r.var1 = m.Value);. Then, the method in a differenct class can send the value in message using Messenger.Send(new Var1Message(message). While this seems to be the best option, as it can be easily implemented together with dependency injection, it quickly becomes very convoluted, as for each property a new sealed class is required, that describes the message.
Is any of these options the best practice, or is there an option that I am not aware of?
If business methods require multiple properties in your VM, then maybe the properties should be in the business object? Are the properties intrinsic to the business rules, or do they only exist in the context of the view?
VM properties can just pass through to the business properties, or quite often you can just directly expose the business object itself in your VM.
Why do you think that its inadvisable to use methods within a ViewModel?
A ViewModel must act as an intermediary between the View, Model and business logic classes or libraries.
Consider using a RelayCommand (implement the property as ICommand type) in your ViewModel, this can be bound to a button click and is used to call a method within your ViewModel that takes the property values and passes them to your business logic.
A typical scenario for a ViewModel may be user input on a form, with your ViewModel properties being bound by the View. The user then clicks a button to submit this data.
I am struggling with ReactiveUI routing for my UWP Navigation View. Since Navigation View Item does not implement command I use ItemInvoked event and execute my command in my view model. Unfortunately, I am unable to show another page in the view. I was using the official tutorial and also Reactive UI UWP Example. When using breakpoint I can see that my command is executed but nothing happens. I have no clue how to debug this more. Did anyone implement Navigation View wit ReactiveUI Routing?
My code: My repo
#Edit
POCOObservableForProperty: The class InwentarzRzeczowy.UWP.Views.MainView property ViewModel is a POCO type and won't send change notifications, WhenAny will only return a single value!
POCOObservableForProperty: The class InwentarzRzeczowy.UWP.Views.MainView property RoutedViewHost is a POCO type and won't send change notifications, WhenAny will only return a single value!
I believe you need a .Subscribe() after the AddPage.Execute() command in your event handler. I'm doing this from memory though and I remember something like that tripping me up.
Based on the conversation we had in Slack, posting the solution here as well. With ReactiveUI routing, you have to either register the views into Splat.Locator.CurrentMutable before using the router (see View Location) or write a custom view locator that matches your view models and returns the views (see Routing). The latter option expects you to implement the IViewLocator interface and assign it to RoutedViewHost.ViewLocator property. So, in C# code we generally have this:
public class YourViewLocator : IViewLocator
{
public IViewFor ResolveView<T>(T viewModel, string contract = null) => viewModel switch
{
NewEntryViewModel _ => new NewEntryView(),
// Also match other routable view models...
// The RoutedViewHost will initialize the ViewModel
// properties of your views automatically.
_ => throw new System.NotImplementedException()
};
}
And in XAML markup we have this:
<reactiveUi:RoutedViewHost Router="{Binding Router}">
<reactiveUi:RoutedViewHost.ViewLocator>
<yourAssembly:YourViewLocator />
</reactiveUi:RoutedViewHost.ViewLocator>
</reactiveUi:RoutedViewHost>
Also, it is important to initialize the IViewFor.ViewModel property for the root view. This could be done in the constructor of your view right after a call to this.InitializeComponent(), or in the composition root of your application.
I'm designing an MVVM framework and I need to know if my understanding of MVVM pattern is correct or not. My question is simple. How should I pass the ObservableCollection object between the VieModels? or shouldn't I?
I have a CustomerViewModel which has an ObservableCollection to hold a list of customers. I also have an InsertCustomerViewModel which is responsible for insertng new customer models in to that ObservableCollection. in the InsertCustomerViewModel I have a method called Insert() which is called everytime the user clicks on the Insert button.
What I'm doing so far is passing the ObsertvableCollection from CustomerViewModel to the constructor of the InsertCustmerViewModel and then in the Insert method I have Items.Add(newCustomer).
Is my implementation correct? or is there any better way to do the job?
I would pass the CustomerViewModel to the InsertCustomerViewModel and expose a property for the collection. That way you can use and modify that collection from InsertCustomerViewModel directly.
From my point of view InsertCustomerViewModel does not make any sense here. When user insert a customer it should only add in CustomerCollection Class which should be a Model for multiple ViewModels.
I think the idea should be share the same CustomerCollection model among the two ViewModels via some common instance.
I have a StockModule and a PurchaseModule. When I receive a new purchase, I add that to the Stock Model in the database.
My problem is that I have an ObservableCollection stockList in the ViewModel in my StockModule and the controller that adds the stock is in the PurchaseModule. I don't know how I'm supposed to tell the ViewVodel in the StockModule that I have updated the stockList already.
I am using databinding in wpf and prism with unity. Updating the view within a module is easy enough but I haven't found a way to do it between two modules.
Should I pass a reference to the StockController when I initialize my PurchaseModule? I don't want to do that because that seems too tightly coupled.
You should use something like Marlon's Mediator, or EventAggregator from Prism to achieve communication between ViewModels or components (they can be in a single module, or across multiple modules).
In your case, your ViewModel in PurchaseModule would publish a message (say, StockAdded) via Mediator/EventAggregator, and then your ViewModel from StockModule will listen for this message and updates its data and in-effect the view as well.
More reading on Mediator: http://marlongrech.wordpress.com/2009/04/16/mediator-v2-for-mvvm-wpf-and-silverlight-applications/
Prism EventAggregator: http://msdn.microsoft.com/en-us/library/ff921122.aspx
I came accross the following situation:
I have 2 view models and a single view which contains 2 user controls on which the view models will be bound to. The first VM is a Search functionality which returns a list of Persons, and the second VM is a more detailed description of each person.
I want to do the following:
public CompositeVM
{
public SearchVM SearchViewModel{get;set;}
public DescriptionVM DescriptionViewModel{get;set;}
}
As I have said, the search view model also incorporates a list of found persons, so I wish that when I select a person the DescriptionVM to be updated accordingly.
How can I achieve this type of communication between VMs? Should I set a SelectedPerson property on the SearchVM and pass it to the DescriptionVM when the selected list item changes (pretty high coupling to me)? Is there a more simple approach to this matter?
It's possible for CompositeVM to subscribe to SearchViewModel's PropertyChanged event and set DescriptionViewModel.SetSelectedPerson(SearchViewModel.SelectedPerson).
There is no coupling here between SearchVM and DescriptionVM, since they're not aware of each other. CompositeVM knows them both, and is also who's in charge of their interaction.
Alternatively you can use the Mediator-Observer pattern, such as the Messenger class in MVVM Light:
http://blog.galasoft.ch/archive/2009/09/27/mvvm-light-toolkit-messenger-v2-beta.aspx