Thursday, 12 April 2012

MVVM Light - Passing Params to Target ViewModel Before Navigating - Part II

After a lot more thinking about this, I've come up with a really neat solution, needs more testing, but here it is in current form. There's a MessageSender and MessageReceiver class which between them handle sending InitMessageBase type objects and allow buffering and requesting of a message if target is not constructed.


Example code here


Part 1 here


MessageSender


    public class MessageSender<T> where T : InitMessageBase
    {
        private T _message = default(T);

        public MessageSender()
        {
            // Send back item when a constructor asks for it
            Messenger.Default.Register<T>(this, message =>
            {
                if (this._message != null && this._message != message && !this._message.Received)
                {                     
                    // Send back original message
                    Messenger.Default.Send(this._message);
                }
            });
        }

        public void SendMessage(T message)
        {
            // Store value
            this._message = message;

            // Try and send message
            Messenger.Default.Send(message);
        }
    }

MessageReceiver

    public class MessageReceiver<T> where T : InitMessageBase
    {
        private Action<T> _handler = null;
        private T _message = null;

        public MessageReceiver(Action<T> handler)
        {
            this._handler = handler;

            // Wait for messages
            Messenger.Default.Register<T>(this, message =>
            {
                if (this._message != null && this._message != message && !message.Received)
                {
                    message.Received = true;

                    this.OnMessageReceived(message);
                }
            });
        }

        /// <summary>
        /// Send empty message on instantiation
        /// </summary>
        /// <param name="message"></param>
        public MessageReceiver(Action<T> handler, bool sendInitMessage)
            : this(handler)
        {
            if (sendInitMessage)
            {
                this._message = Activator.CreateInstance(typeof(T)) as T;

                // Send empty message
                Messenger.Default.Send<T>(this._message);
            }
        }

        private void OnMessageReceived(T message)
        {
            if (this._handler != null)
                this._handler( message );
        }
    }

InitMessageBase

    public abstract class InitMessageBase : MessageBase
    {
        // Used for a send
        public bool Received { get; set; }

        public InitMessageBase()
        {

        }
    }

An Implementation of InitMessageBase

    public class ArticleInitMessage : InitMessageBase
    {
        public  Article Value { get; set; }

        public ExampleInitMessage()
        {

        }

        public ExampleInitMessage( Article value)
        {
          this.Value = value;
        }
    }

Sending a Message
Simply create a global instance of the MessageSender and send a message or value:

if (this._articleSender == null)
this._articleSender = new MessageSender<ArticleInitMessage>();

this._articleSender.SendMessage(new ArticleInitMessage(article));

this._navService.NavigateTo(ViewModelLocator.ArticleUri);

Receiving a Message
Simply create a global instance of MessageReceiver at VM construct time and pass it an action to do:

this._articleReceiver = new MessageReceiver<ArticleInitMessage>((message) =>
{
this.SelectedItem = message.Value;
}, true);






No comments:

Post a Comment