CoreDispatcher.HasThreadAccess "breaking change" - c#

I am taking over an application developed with MvvmCross.vNext.
While trying to update it with MvvmCross.V3, I found the following breaking change: in the constructor of the MainViewModel, we show the LoginViewModel (ShowViewModel()).
It worked fine in vNext.
But with V3, the LoginView doesn't show.
After a long search, I found out that the following code, added in MvxStoreMainThreadDispatcher.RequestMainThreadAction :
if (_uiDispatcher.HasThreadAccess)
{
action();
return true;
}
was responsible for my troubles.
If I comment it out, my application works as previously, but I guess this code is there for some reasons...
Do you have any suggestions ?
Can I force the previous behavior without changing MvvmCross source code?
Should I refactor the code to handle the LoginView differently ?
Thanks in advance for your comments.
Philippe

While trying to update it with MvvmCross.V3, I found the following breaking change: in the constructor of the MainViewModel, we show the LoginViewModel (ShowViewModel()). It worked fine in vNext.
I think your constructor navigation would have broken on several platforms in any MvvmCross version. To be honest, I think you were lucky it worked before.
The problem is that ViewModels are constructed (or located) during View events such as ViewDidLoad, OnNavigatedTo, and OnCreate - and these events are normally
called during 'page transitions'
To work around this you will need to move your login navigation out of the constructor.
How you do this depends on your app
if you do need the Home->Login backstack, then you can trigger off some async or time delay or you can trigger off some other View event such as something like ViewDidAppear
if you don't need that backstack then the way I normally implement this sort of thing is to use a custom IMvxAppStart - something like:
public class AppStart
: MvxNavigatingObject
, IMvxAppStart
{
public void Start(object hint = null)
{
var authService = Mvx.Resolve<IMySerice>();
if (authService.IsLoggedIn)
{
ShowViewModel<HomeViewModel>();
}
else
{
ShowViewModel<LoginViewModel>();
}
}
}
(you can see another example in https://github.com/slodge/MvvmCross/blob/v3/Sample%20-%20CirriousConference/Cirrious.Conference.Core/ApplicationObjects/AppStart.cs)
This can be registered in App.cs startup using:
RegisterAppStart(new AppStart());

Related

How can I use DI to create an instance of the page that I navigate to with my route?

Here is the situation that I have:
I have set up a route like this:
Routing.RegisterRoute("Cars/CarMgmtPage", typeof(CarMgmtPage));
When a button is clicked it calls the following:
await Shell.Current.GoToAsync("Cars/CarMgmtPage", true);
However CarMgmtPage takes constructors in the argument like this;
public CarMgmtPage(IAddCar addCar1,
IAddCar addCar2)
What I need to do is to use the Microsoft.Extensions.DependencyInjection that is used in my application to create this page something like this:
var abc = Startup.ServiceProvider.GetRequiredService<CarMgmtPage>();
But I have no idea how to go about this as it seems the Xamarin Shell handles the creation of the page and I don't see how I can fit DI into that.
I hope someone has some ideas on how to do this.
It would seem that at this time this is not possible. There are a couple of issues in the Xamarin Github talking about this:
[Enhancement] Dependency Injection support for creating ContentPage in Shell. #8359
Current status of this is:
Under consideration-High Interest in Enhancements on Jun 2, 2020
Also
[WiP] .NET MAUI Shell Improvements #10804
So the answer to this is that it we can hopefully expect this in the near future but right now it's still at the planning stage.

How to present a modal Login screen using ViewModel First navigation in Xamarin

So I am using the FreshMVVM Xamarin.Forms library so I can do ViewModel-first navigation in xamarin. My research tells me that this is a highly recommended practice, yet there seem to be not a whole lot of tutorials for this.
What I am trying to achieve is a login page that pops up as a modal once the user opens the app if they are not signed in.
I have modified the demo project slightly by adding the modal display button and associated Command from the ViewModel to the first page, however, I am having trouble invoking this programmatically.
Here is what I am trying to so along with the relevant Command code that I intend to tweak to work with a Login Page:
public NormalOneViewModel()
{
if (/*user is not already logged in*/) {
//display the login page as a modal to get the users credentials and log them in
//see below
}
}
...
ICommand _navToChild;
public ICommand NavigatePopup
{
get
{
if (_navToChild == null)
{
_navToChild = new Command(async () =>
{
await _navService.PushModalAsync<NormalModalViewModel>();
});
}
return _navToChild;
}
}
This Command is invoked via <Button Text="Show Modal" Command="{Binding NavigatePopup}"/> in the view associated with the viewModel above.
I have:
tried to call the command programmatically using NavigatePopup.Execute(null);. This fails with an error explaining that the key cannot be found.
tried to call the command programmatically using NavigatePopup.Execute();. This yields the following error:
tried using the custom NavigationService that I have set up for this app by calling the await NavigationService.Instance.PushModalAsync<NormalModalViewModel>(); line from the constructor of the detail page's ViewModel. This tells me that I need to make the method async. Additionally, don't really understand xamarin async too well since I am fairly new to Xamarin.Forms and C#
Navigation.PushModalAsync(new LoginPage()); seems to work, but only in the contractor of the code-behind of the MasterDetailPage. this is also using the Navigation Class which is apparently built-into xamarin. but this method only accepts a Page and doesn't really conform to my ViewModel-First navigation strategy.
What I am primarily wondering is how do I programmatically invoke a modal (for a login page) when using ViewModel-first patterns?
EDIT 1: I am using Visual Studio for Mac
Apologies for the really confusing setup.
Okay, so first off, apologies for the confusion regarding the way I presented this and why I was using PushModalAsync<NormalModalViewModel> in I wanted to display a Login page. The reason for this is I was trying to modify the sample code in the repositories linked in the question in order to see how I would do this before transplanting it into the project I am working on.
My Solution
After tinkering with this for quite a while, I realized that the reason .Execute(); was failing when I was calling it from the constructor with the key not found error was because I was calling it from the constructor. Even after separating the navigation command into a separate method instead of using a lambda (see below) and calling that from the constructor wouldn't work because, from my understanding, the page hadn't been displayed yet and my NavigationService therefore hadn't registered it or something.
ICommand _loginNav;
public ICommand NavigatePopup
{
get
{
if (_loginNav == null)
{
_loginNav = new Command(async () => await NavigateModalLogin());
}
return _loginNav;
}
}
async Task NavigateModalLogin()
{
await _navService.PushModalAsync<NormalModalViewModel>();
}
while using Navigation.PushModalAsync(new LoginPage()); did work for me when calling it from the constructor, it wasn't using my custom navigation and I didn't really like that. So I continued looking for a better way.
Considering my realization from earlier that doing this check in the constructor wasn't going to work, I had the idea to use the OnAppearing() and OnDisappearing() methods provided by the Page class in Xamarin.Forms.
However, since I am using ViewModel-first navigation, the processing of the login would need to be done in the ViewModel. So I wrote this in the code-behind of the page to effectively pass the execution to the ViewModel so I could use it:
protected async override Task OnAppearing()
{
await viewmodel.OnAppearing();
base.OnAppearing();
System.Diagnostics.Debug.WriteLine("*****Here*****");
}
Then in the ViewModel I had the OnAppearing() method call the NavigateModalLogin() function from earlier (yes I know this can be optimized to avoid the extra method call but whatever).
The good part about this is that the OnAppearing() method is an Event Handler in Xamarin. Which means it supports async. Which means I didn't have to figure out how to start off an async/await chain or however that works.
Tl;dr
Use the event handlers that Xamarin provides to call the same method that the command does (the event Handlers can be either synchronous or asynchronous).
Code:
In the code-behind for your page:
protected async override Task OnAppearing()
{
await viewmodel.OnAppearing();
base.OnAppearing();
System.Diagnostics.Debug.WriteLine("*****Here*****");
}
in the ViewModel for your page:
internal async Task OnAppearing()
{
await NavigateModalLogin();
}
ICommand _loginNav;
public ICommand NavigatePopup
{
get
{
if (_loginNav == null)
{
_loginNav = new Command(async () => await NavigateModalLogin());
}
return _loginNav;
}
}
async Task NavigateModalLogin()
{
await _navService.PushModalAsync<NormalModalViewModel>();
}

Matching ViewModel and View in MVVMCross

I have an MVVM Cross application, and I'm trying to figure out why the initial screen isn't showing (this is on WinRT).
The following code looks like it loads the ViewModel MainViewModel:
var setup = new Setup(rootFrame);
setup.Initialize();
var start = Cirrious.CrossCore.Mvx.Resolve<Cirrious.MvvmCross.ViewModels.IMvxAppStart>();
start.Start();
However, the Start method of MainViewModel never gets called. The reason appears to be this error in the output:
Error seen during navigation request to MainViewModel - error KeyNotFoundException: Could not find view for MyApp.PCL.ViewModels.MainViewModel
So, here's my guess as to the problem: it's looking for MyApp.PCL.ViewModels.MainView, Obviously, this doesn't exist. The view is in: MyApp.Views.MainView. Is my assumption correct and, if so, how can I persuade MVVMCross to look in the correct place?
EDIT:
The Setup method is called from the MyApp.Windows WinRT app.
protected override IMvxApplication CreateApp()
{
return new MyApp.PCL.App();
}
MyApp.PCL is a portable class library that is referenced from MyApp.Windows.
MyApp.PCL contains the ViewModels, and MyApp.Windows contains the views.
I have tried to override Start():
public class MainViewModel
: MvxViewModel
{
public override void Start()
{
System.Diagnostics.Debugger.Break();
base.Start();
}
but this never gets hit.
Massive Doh! here, but I'll post the solution; my view was not using the MvxStoreView, but the standard StoreView.
The solution was to change the view as follows:
...
xmlns:views="using:Cirrious.MvvmCross.WindowsStore.Views"
...
<views:MvxStorePage

Standard Approach of Launching Dialogs/Child Windows from a WPF Application using MVVM

All, I would like to know the recognised best approach/industry standard of launching [child] dialogs/windows from an WPF using the MVVM pattern. I have come across the following articles:
A. CodeProject - Showing Dialogs When Using the MVVM Pattern
This approach seems good but excessive to me. The is some degree of code replication and I am not convinced this is the right way to go.
B. WPF MVVM and Showing Dialogs
This briefly runs through three options with various links which are all fairly/very poor at explaining the methodology or are of-topic.
Can someone please provide an explanation of the industry standard method/approach of launching dialogs from a WPF application using MVVM and preferably some links to further reading material? If you can provide an example yourself I would of course be most appreciative!
Thanks for your time.
First of all, i don't know of any "industry-standard" way for showing dialogs using MVVM because there is no such thing.
Secondly, Welcome to MVVM, you have just touched on of the areas that MVVM don't have a standard for.
To tell you the truth, MVVM has many pain points and this is the reason why there are tons of MVVM frameworks out there, just to mention a few MVVM Light, PRISM, Caliburn.Micro, Cinch, Catel, WAF, Baboon, shell i stop or you want more.
Now to answer your question and after dealing with most of those frameworks, i noticed one commonality, they all use a DI/IoC container and then provide you with an interface, something like IDialogManager and an implementation of their own, and then they ask you to accept this interface in your view model and use it to show dialogs. So to sum this up, i would use dependency injection, have an interface for showing dialogs, and then provide and implementation of that, and register it with the di container and then consume it from my view model or views.
Edit: So you have picked PRISM (which in my opinion) is hardest between them all in showing dialogs. Now that's aside, there is the hard way which is by using Interaction Requests (check the middle of the article), or you can use this Answer as a quicker way.
Recently, I've implemented my own Navigation Service for WPF, which uses Caliburn.Micro's WindowManager (but you could replace it by something else).
Example (how to use):
_navigationService.GetWindow<ClientDetailsViewModel>()
.WithParam(vm => vm.IsEditing, true)
.WithParam(vm => vm.Client, SelectedClient)
.DoIfSuccess(() => RefreshSelectedClient())
.ShowWindowModal();
Implementation:
namespace ClientApplication.Utilities
{
public class NavigationService : INavigationService
{
SimpleContainer _container;
IWindowManager _windowManager;
public NavigationService(SimpleContainer container, IWindowManager windowManager)
{
_container = container;
_windowManager = windowManager;
}
public INavigationService<TViewModel> GetWindow<TViewModel>()
{
return new NavigationService<TViewModel>(_windowManager, (TViewModel)_container.GetInstance(typeof(TViewModel), null));
}
}
public class NavigationService<TVM> : INavigationService<TVM>
{
IWindowManager _windowManager;
TVM _viewModel;
System.Action _action;
public NavigationService(IWindowManager windowManager, TVM viewModel)
{
_windowManager = windowManager;
_viewModel = viewModel;
}
public INavigationService<TVM> WithParam<TProperty>(Expression<Func<TVM, TProperty>> property, TProperty value)
{
var prop = (PropertyInfo)((MemberExpression)property.Body).Member;
prop.SetValue(_viewModel, value, null);
return this;
}
public INavigationService<TVM> DoBeforeShow(Action<TVM> action)
{
action(_viewModel);
return this;
}
public INavigationService<TVM> DoIfSuccess(System.Action action)
{
_action = action;
return this;
}
public void ShowWindow(IDictionary<string, object> settings = null)
{
_windowManager.ShowWindow(_viewModel, null, settings);
}
public bool ShowWindowModal(IDictionary<string, object> settings = null)
{
bool result = _windowManager.ShowDialog(_viewModel, null, settings) ?? false;
if (result && _action != null)
_action();
return result;
}
}
}
Interfaces:
namespace Common
{
public interface INavigationService<TVM>
{
INavigationService<TVM> WithParam<TProperty>(Expression<Func<TVM, TProperty>> property, TProperty value);
INavigationService<TVM> DoIfSuccess(System.Action action);
INavigationService<TVM> DoBeforeShow(Action<TVM> action);
void ShowWindow(IDictionary<string, object> settings = null);
bool ShowWindowModal(IDictionary<string, object> settings = null);
}
public interface INavigationService
{
INavigationService<TViewModel> GetWindow<TViewModel>();
}
}
The most recent release of Prism (download here) contains a so-called "Reference Implementation" of an MVVM application called "Stock Trader". My rationale was that if the Prism team is calling it a "Reference Implementation", that's the most "standard" from their point of view (if anything in MVVM is standard), and the logical choice to press on with.
The source contains an infrastructure library for raising modal dialogs, and it's pretty good. So I adopted that library and have deployed it successfully (I uploaded such an app to Codeplex).
I needed to tweak the code to 1 add the parent's icon to the title bar because the library didn't provide for it; and [2] add some meaningful text to the title bar because the library left it blank and [3] add a delegate to invoke when the dialog closes. It's abstracted to the extent that a VM can raise a dialog by passing two strings (i.e., the Unity Registration Names) to a mediator. Those changes are available on Codeplex.
So among all the other 'standards' the "Reference Implementation" should minimally participate as a viable choice. The more oblique answer to your question is that if your View Models are sufficiently isolated and work entirely through POCO interfaces, then in THEORY, it shouldn't matter because switching to another 'standard' should be a trivial exercise.
i simply use a dialogservice, see here
within your viewmodel you just have to do:
var result = this.uiDialogService.ShowDialog("Dialogwindow title goes here", dialogwindowVM);
... do anything with the dialog result...
Creating a dialog service has worked out well for me, and is also suggested in both your links.
Later I saw the same solution at the dev days in an MVVM presentation by Gill Cleeren. Check the link for working code samples (though written for Metro)
Only thing that nags me a bit about the dialog service is that it is in some way UI technology (rich client) dependent.
A simple request-response web frontend View can be built on top of the same ViewModel and Model code that the WPF XAML binds to. Until the ViewModel starts popping up dialogs through the dialog service. I would not know how to implement the dialog service for a web view. Implementing dialogs there would require pushing a bit more logic to the view.
Purpose of using interface to implement dialogs is to make code testable. In this case, "A" is widely used, but it is still hard to say "Standard". If you do not have test on your ViewModel or you can test your ViewModel avoid touching dialogs, such as using Extract-Override, you can definitely do not follow the instruction.

Set xmlrpc.net service url at runtime

I like to interact with some of my wordpress blogs through xmlrpc interface. During my research I found xml-rpc.net-Library (www.xml-rpc.net) which works really fine except for one thing.
I build my xmlrpc.net instance like this:
[XmlRpcUrlAttribute("http://my-example-blog.com/xmlrpc.php")]
public class WP : XmlRpcClientProtocol
{
public WP()
{
}
...
}
But I want to set the xmlrpc-Url dynamically during runtime. So I like to add more blogs during the UI at runtime and don't want to implement each blog "hard-coded".
Does anybody had a same problem and can help me? Is there another possibility within xmlrpc.net-library or do you know another good xmlrpc library?
Greets,
Raffi
You can specify the URL at runtime:
proxy.Url = "http://my-example-blog.com/xmlrpc.php";

Categories