Executing UI Code from ViewModel on MVVMCross
Asked Answered
I

2

5

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

Ioyal answered 23/3, 2013 at 18:54 Comment(1)
As well as providing an answer on what you can do today, I've also added github.com/slodge/MvvmCross/issues/197 - if you've got any requests please do add them in - always happy to consider if there's anything we can add across all platforms :)Straka
S
15

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.
Straka answered 23/3, 2013 at 21:44 Comment(4)
Wow, thanks for the detailed explanation, i will check that branch tomorrow. I was already used to use EventAggregator on Caliburn for other purposes, so i think i will at home :) sorry i didn't notice that plugin. Thanks for your helpIoyal
hi i am also using the same messaging pattern in iOS application, but the problem is the messages are displayed many times. this happens when you move back and forth in view. ex. i have a login view and feedback view which comes after login. so once i login and move to feedback view,and fill up field of that page, it's working fine. but if i now navigate back to login and again come back to feedback view and filled up fields and when tap on "Done" button i get many messages. can you guide me what will be the resolution of this situation?Shalandashale
Can i use it for message passing from view to viewmodel ?Lindsey
@Straka - I have a MvxListView with multiselection mode. I want to toggle its clicked item color. If it is a listview then I can do it by code on view. But how can I do same task in MVVMCross ? Because MvxListView Itemclick is bind on View Model and here we not get clicked item object not get. Please helpMuckrake
A
0

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.

Augite answered 25/6, 2018 at 10:28 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.