Reactive UI Routing and UWP - c#

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.

Related

What is the best practice to implement methods using values of properties in the View Model?

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.

Xamarin MvvmCross call command handler from another viewModel

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.

C# WPF MVVM Window OnLoad Binding

My Code behind looks like this...
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = new MainWindowViewModel();
}
}
My ViewModel looks like this...
class MainWindowViewModel : INotifyPropertyChanged
{
public MainWindowViewModel()
{
bool flag = Application.Current.MainWindow.IsInitialized;
if (flag)
{
// Do something...
}
}
I guess my question is....Does this conform to the MVVM design pattern? The only other way to do this is How to fire a Command when a window is loaded in wpf
I don't know why, but I don't want to use mvvm-light or any other boilerplate code..
Accessing UI component from ViewModel is violation of MVVM pattern.
Application.Current.MainWindow.IsInitialized is breaking that pattern.
Custom behaviours is more in accordance with MVVM. So, i would suggest to go with the approach you mentioned as link in your question.
Accessing UI component breaks the testability of your ViewModel. How would you write testcase for your ViewModel class? Application.Current will be null when you try to test it via unit test and it will throw null reference exception.
One of the main motive of MVVM was to seperate UI logic from business
logic so that business logic can be tested separately without worrying
about its consumer which is view.
There is no "pure" way to do this in MVVM without boilerplate code. In general, you shouldn't need to do work in response to the VIew within your VM - just the concept is a violation of MVVM, since your ViewModel is trying to do something in response the View, and things should always flow the other way.
The ViewModel shouldn't, in a real scenario, care about the View's state at all - it should be doing nothing but presenting data for data binding by the View.
Most of the time when people are attempting this, it's to try to avoid loading data up front. This is something that's typically handled better by pushing the data to load and starting it directly on a background thread within the ViewModel, then updating the property within the VM when it completes. C# 5's async/await language features can be used to simplify this quite a bit.
While it is generally believed that having some load/unload logic is a pattern violation, there is a set of use cases, where it's necessary. E.g. a view model may need to be subscribe to some events. If it didn't unsubscribe when unloaded, it might not be garbage collected, depending on the nature of the subscription.
What would break the pattern is accessing view state from within the view model, e.g. manipulating controls. The role of the view model is to expose data to the view and managing load/unload behaviour is part of this contract. Knowing when a view model is loaded means knowing when to expose that data.
While it is true the view model should not care about state of the view, it must know how to prepare data for presentation in the view. More importantly the view model is a layer between the model and the view that makes them separate. Yet in other words: since 'model' means logic, then 'view model' means logic of getting data to display. And it is also about knowing when to fetch it/make it available/etc.
You may want to take a look at this blog post, which provides a convenient way of making a view model aware of being loaded. It is not 100% correct in terms of MVVM purity, because it passes FrameworkElement back into the view model, but imagine we ignore this parameter.
The sample code below is based on the above blog post, but with purer signatures. You could implement IViewModel interface on your classes:
public interface IViewModel : INotifyPropertyChanged
{
void Load();
void Unload();
}
Then instruct the view to call adequate methods when loaded or unloaded by using an attached property:
ViewModelBehavior.LoadUnload="True"
Notice the last line has its place in XAML - the view is the one that enforces a certain behaviour, not vice-versa.
What you are currently doing is correct and that is how it is really done with other frameworks behind the scenes.
Since you mentioned MVVM-Light, I suggest you can take a look at caliburn micro. It has very nice framework to conform the MVVM Pattern. Caliburn micro makes it easy to hook up bindings with events on the controls. Just check out its documentation and it is still considered as MVVMy..
in particular because MVVM is mainly used to guarantee easy to maintain and testable code, you should bear in mind that Application.Current will be null if you use the MainViewModel in UnitTests. Therefore this code will end in a NullPointerException in your tests.
You should consider using the Initialized event if you want to ensure that something is initialized already. But you create the ViewModel after you called InitializeComponent - I simply would leave the check out.

Calling a Method in View's CodeBehind from ViewModel?

I have a method within the code behind of my View (this method does something to my UI).
Anyway, I'd like to trigger this method from my ViewModel. How could this be done?
My (and maybe others?) difficulty with MVVM was to understand a simple thing: View knows about ViewModel. I was using bindings and commands, but they are simple strings in xaml. Because of safe resolving at run-time (safe means you can do a typo, but software will not crash) this makes view decoupled from view-model (at compile time at least). And I was always looking for solution to keep this decoupling, to example, behaviors.
Truth is, you can get access directly to view model, which is typically a DataContext of window/user control:
var vm = (MyViewModel)this.DataContext;
Knowing that, using events probably the best way to call view method from view model, because view model don't know if there is subscriber, it just firing that event and event can be used by view or another view model.
// define in the view model
public delegate void MyEventAction(string someParameter, ...);
public event MyEventAction MyEvent;
// rise event when you need to
MyEvent?.Invoke("123", ...);
// in the view
var vm = (MyViewModel)DataContext;
vm.MyEvent += (someParameter, ...) => ... // do something
You can do it like this in View (code behind).
It casts to an interface to be implemented by the ViewModel, so that you are not constrained to one specific ViewModel type.
// CONSTRUCTOR
public SomeView()
{
InitializeComponent();
DataContextChanged += DataContextChangedHandler;
}
void DataContextChangedHandler(object sender, DependencyPropertyChangedEventArgs e)
{
var viewModel = e.NewValue as IInterfaceToBeImplementedByViewModel;
if (viewModel != null)
{
viewModel.SomeEvent += (sender, args) => { someMethod(); }
}
}
According to MVVM pattern ViewModel is not aware of View, so this is not acceptable. To interact with ViewModel View could trigger a command, also you can use bindings. Moreover, you should not move UI-specific things like BusyIndicator to ViewModel level.
Please provide more details regardign your concrete use case - when you want to call a View's method and what this method does.
Let's say you have a method within the code behind of my Login View, that updates UI by bringing Focus to the PasswordEntry if login fails, then the easiest & most universal way to trigger this method from your ViewModel is using Action delegates.
As you can see in this sample, all you need to add, where your services determine that the login has failed and you want the Password Entry to get the focus, is two lines of code in your ViewModel and an action handler in your View.
ViewModel code:
Declaration of the event: public Action<bool> OnLoginFailed { get; set; } &
Then simply, when needed, executing this OnLoginFailed?.Invoke(true);
View code:
ViewModel.OnLoginFailed = ((obj) =>
{
PasswordEntry.Focus();
});
Update: I wrote an article to explain this in a lot more detail
I saw youre reply to the answer above, you are saying that you want your ViewModel to retrieve data and then tell your view to stop the busy indicator.
I'm not sure if my solution would be the best solution, but you can give it a try, and maybe someone can correct if I'm wrong.
So from your view, you would call a method from ViewModel to start reading the dataset, am I right? In this method, you can pass a delegate (pointing to a method that exists in your view) and when your ViewModel finishes reading the dataset from the server, trigger the delegate (from your viewmodel) that is linked to your method in your view that can stop the busy indicator.
so in your view you have
void StopBusyIndicator()
{
this.BusyIndicator.IsBusy = false;
}
and when you call your ViewModel to read dataset,
call it like this:
ViewModel.ReadDataSet( ()= >StopBusyIndicator)
which will pass the StopBusyIndicator method as a delegate, which you can call at the end of your ReadDataSet.
HTH
You could write an action class that accepts a Data Transfer object. Within the DTO, add a property called "View" and assign it the current view. Call the action via the controller from within your view's codebehind, unbox the DTO and now you have full control of the view within the action class.
If you truely want to do this in your model, just create the method with a "View" type parameter in your Model and execute it, passing in the current view.

Getting lookup view model to trigger Modal Dialog

What's the right way to get my viewmodel to trigger a custom lookup control to throw up a modal dialog that essentially represents that lookup viewmodel? The custom lookup control's data context is that of the parent record view model. The lookup control also has another DependencyProperty that has it bound to a lookupviewmodel property on the parent record view model and this represents a sub lookupviewmodel.
METHOD 1) I currrently use an event on the lookupviewmodel that the custom control knows to listen for.
METHOD 2) I tried throwing a validation exception within the setter of the property on the lookupviewmodel that the lookup control's text propery is bound too. Then I hooked the ErrorEvent in the custom lookup control. But it seems that if the user "corrects" the value from within the dialog while in this event, the original value sticks. And worse, even after I call Validation.ClearInvalid, another ErrorEvent still fires that somehow adds the error back. So everything works here in the sense that all the viewmodels have the correct data, it's just that it seems like the textbox is ignoring that the bound text property has changed on the underlying data source when inside an ErrorEvent. So it seems like I can't correct an error while inside the processing of that error?
Another sub issue within method 2 is that Validation.ClearInvalid doesn't remove the red error border. I had to manually clear the ErrorTemplate too. Is that right?
I'd like to find a way to use natural error handling within the control to get it to throw up the modal dialog.
This isn't what you use events for. Events exist to facilitate decoupling: the object raising the event shouldn't know or care what the object(s) listening to it are doing. You're expecting an event to be able to change the value of a property from inside the property's setter - or worse, your event handler is calling the very property setter that's raising the event that it's handling, which means that you have to do something pretty hackish to avoid a stack overflow.
Your description isn't very clear (you're describing both the problem you're having and the non-working solutions you're trying at the same time, which is confusing), but it sounds like what you're trying to do is something more like:
if (IsValid(value))
{
_Property = value;
}
else
{
_Property = GetValueFromDialog();
}
The problem is that you don't want to have code in your view model that throws up a dialog, since that creates a view model that can't be tested outside of your WPF application.
The answer in this case is to use dependency injection. Create an interface called IDialogService:
interface IDialogService
{
object GetValueFromDialog();
}
Now add this property to your view model:
public IDialogService DialogService { get; set; }
The above code becomes:
if (IsValid(value))
{
_Property = value;
}
else
{
_Property = DialogService.GetValueFromDialog();
}
Create a dialog service for use in your WPF application that actually throws up the dialog and gets the result. When you instantiate your view model in your application, do this:
MyViewModel vm = new MyViewModel { DialogService = new WpfDialogService(); }
Thus, in your application, the property setter will put up the dialog and get the result exactly as you expect it to.
For your unit tests, create a mock dialog that looks like this:
public class MockDialogService : IDialogService
{
private object Result;
public MockDialogService(object result)
{
Result = result;
}
public object GetValueFromDialog() { return Result; }
}
You can then write a test like:
MyViewModel vm = new MyViewModel { DialogService = MockDialogService(ExpectedResult) };
vm.Property = InvalidValue;
Assert.AreEqual(ExpectedResult, vm.Property);
The above is really more a sketch of a solution than a solution - depending on how your application uses dialogs, you may need a lot more features than what are sketched out here. If you take a look at MVVM frameworks you'll find that a lot of them implement dialog services of one kind or another.
You can use a framework like MVVMLight or Prism which allow you to pass payloads between different entities in totally decoupled ways. MVVMLight is very lightweight compared to Prism. It has a concept of Messanger which acts as a system wide event bus. Similarly you have EventAggregator in Prism.

Categories

Resources