I have just started using MvvmCross, but i didn't find any info about to how i can execute UI code from a ViewModel.
On Caliburn there are coroutine so i can access the view and keep the ui code separated from the viewmodel code.
on my first case i need to open a dialow from a command inside a ViewModel, what is the correct way?
Right now i'm developing a WinRT app.
Thanks
There isn't any hard/fast rule on this within MvvmCross.
Generally, when I need to do this I use the Messenger plugin.
This answer assumes you are using the latest Alpha v3
code. For older vNext code you'll have to do some translation - see notes below.
To use this approach:
I reference Cirrious.MvvmCross.Plugins.Messenger.dll
from both Core and UI projects.
Then I add a line somewhere in Setup.cs (e.g. in InitializeLastChance
) to:
Cirrious.MvvmCross.Plugins.Messenger.PluginLoader.Instance.EnsureLoaded();
Then in the Core project I add a message:
public class InputIsNeededMessage : MvxMessage
{
public InputIsNeededMessage(object sender) : base(sender) {}
}
In the ViewModel I can get the Messenger by constructor injection or by:
var messenger = Mvx.Resolve<IMvxMessenger>();
and I can send messages by calling:
messenger.Publish(new InputIsNeededMessage(this));
In the View I can again get to the messenger and subscribe to messages using:
var messenger = Mvx.Resolve<IMvxMessenger>();
_token = messenger.SubscribeOnMainThread<InputIsNeededMessage>(OnInputIsNeeded);
where _token
must be a member variable - if it isn't then the subscription won't persist - the subscription itself is weak by default (so you never have to unsubscribe)
and where OnInputIsNeeded
is something like:
private void OnInputIsNeeded(InputIsNeededMessage message)
{
if (message.Sender != ViewModel)
return;
// do stuff here - you are already on the UI thread
}
The above sequence is what I normally do for 'proper code'
To start with using a Messenger/EventAggregator can feel uncomfortable - it certainly took me a while to get used to it - but after I did get used to it, then I now use it everywhere - the pub/sub Message decoupling is very flexible for testing and for future maintenance of code (IMO)
As alternatives to this approach above I do sometimes take shortcuts:
- sometimes I fire normal C# events from the ViewModel and have the View respond to these
- sometimes I have special marker properties and fire the UI code from them
Sorry for using v3
syntax - but the changeover is coming and it's what I'm now coding in...
To switch back to vNext
I think you might need to:
- use
IMessenger
instead of IMvxMessenger
- use
BaseMessage
instead of the MvxMessage
- use
Subscribe
instead of SubscribeOnMainThread
- but then you will need to marshall the message onto the UI thread yourself.
There exists an easier way. Here is the method I use for executing any action on the main
thread:
protected void RunOnUIThread(Action action) {
var dispatcher = Mvx.Resolve<IMvxMainThreadDispatcher>();
dispatcher.RequestMainThreadAction(action);
}
Hope it helps. Cheers.