0

i'm working in wpf mvvm for first time, and i'm having some problems in ''problems of real life''

I have a ViewModel, with its view, from where I must open a second view (with its ViewModel, which will have a list of objects) and here, I must choose one object, to return it to the first ViewModel, where i will work with it.

I'm using a simple approach to open the second window, I know I broke the mvvm pattern a bit

public object ShowDialog<TView, TViewModel>(TViewModel dataContext) where TView : IWindow, new() where TViewModel : BaseViewModel<TViewModel>
{
    //Instancio la vista.
    TView view = new TView();    
    WindowService wnd = new WindowService(view);    
    dataContext?.SetWindowService(wnd);    
    view.DataContext = dataContext;         
    view.ShowDialog();
    return dataContext;
}

And in the ViewModel i'm opening the second page like this

WindowService.ShowDialog<ChooseOneCat, ChooseOneCatViewModel>(null);

Then in the ChooseOneCat ViewModel, I let the user choose the category that I must return later

And here is my issue, how can i give that object to the first viewmodel?

Pd: i dont use any extra framework.

Thanks!

Juan Salvador Portugal
  • 1,233
  • 4
  • 20
  • 38
  • First of all, you are not breaking MVVM, WindowServices are a perfect valid solution that is enforced even by MVVM. Regarding your issue, you can use a Messenger for ViewModel communication if you want to enforce loose coupling. Otherwise you can set the second viewmodel as a field or child of the first one. More info on Messengers [here](http://dotnetpattern.com/mvvm-light-messenger) – taquion May 16 '19 at 15:58
  • You can either use one of several already Messenger implementations that are available out there or implement yours – taquion May 16 '19 at 15:59
  • @taquion Thanks for your time, i will try! – Juan Salvador Portugal May 16 '19 at 16:48
  • The first viewmodel creates the second viewmodel and passes it to ShowDialog. When ShowDialog returns, the first viewmodel does whatever it likes with the second. Don't add multiple layers of additional complexity unless they're solving a problem that you actually have, and which can only be solved via that additional complexity. IOC can easily become something akin to a Turing Tarpit. It can be a very expensive thing to impose on your code, if you make a religion of it. Be sure the cost is justified by actual identifiable benefits. – 15ee8f99-57ff-4f92-890c-b56153 May 16 '19 at 17:24
  • 1
    If it's not clear, the dialog results should be in the second viewmodel. Either the dialog edits the second viewmodel's properties in whatever way, or maybe the second viewmodel has a Result property or something. Unless the result is boolean or `bool?` -- ShowDialog should actually return `bool?` btw. A "parent" viewmodel always creates and manages its "children"; the "children" preferably don't know their parent exists. If necessary, the children may have a parent viewmodel reference, but that's a dependency you try to avoid if you can. – 15ee8f99-57ff-4f92-890c-b56153 May 16 '19 at 17:32

1 Answers1

0

I managed to solve the problem using a messenger, specifically, an implementation that I found in the Dalstroem response

Source code:

using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;

namespace Application.Messaging
{
    public class Messenger
    {
        private static readonly object CreationLock = new object();
        private static readonly ConcurrentDictionary<MessengerKey, object> Dictionary = new ConcurrentDictionary<MessengerKey, object>();

        #region Default property

        private static Messenger _instance;

        /// <summary>
        /// Gets the single instance of the Messenger.
        /// </summary>
        public static Messenger Default
        {
            get
            {
                if (_instance == null)
                {
                    lock (CreationLock)
                    {
                        if (_instance == null)
                        {
                            _instance = new Messenger();
                        }
                    }
                }

                return _instance;
            }
        }

        #endregion

        /// <summary>
        /// Initializes a new instance of the Messenger class.
        /// </summary>
        private Messenger()
        {
        }

        /// <summary>
        /// Registers a recipient for a type of message T. The action parameter will be executed
        /// when a corresponding message is sent.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="recipient"></param>
        /// <param name="action"></param>
        public void Register<T>(object recipient, Action<T> action)
        {
            Register(recipient, action, null);
        }

        /// <summary>
        /// Registers a recipient for a type of message T and a matching context. The action parameter will be executed
        /// when a corresponding message is sent.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="recipient"></param>
        /// <param name="action"></param>
        /// <param name="context"></param>
        public void Register<T>(object recipient, Action<T> action, object context)
        {
            var key = new MessengerKey(recipient, context);
            Dictionary.TryAdd(key, action);
        }

        /// <summary>
        /// Unregisters a messenger recipient completely. After this method is executed, the recipient will
        /// no longer receive any messages.
        /// </summary>
        /// <param name="recipient"></param>
        public void Unregister(object recipient)
        {
            Unregister(recipient, null);
        }

        /// <summary>
        /// Unregisters a messenger recipient with a matching context completely. After this method is executed, the recipient will
        /// no longer receive any messages.
        /// </summary>
        /// <param name="recipient"></param>
        /// <param name="context"></param>
        public void Unregister(object recipient, object context)
        {
            object action;
            var key = new MessengerKey(recipient, context);
            Dictionary.TryRemove(key, out action);
        }

        /// <summary>
        /// Sends a message to registered recipients. The message will reach all recipients that are
        /// registered for this message type.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="message"></param>
        public void Send<T>(T message)
        {
            Send(message, null);
        }

        /// <summary>
        /// Sends a message to registered recipients. The message will reach all recipients that are
        /// registered for this message type and matching context.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="message"></param>
        /// <param name="context"></param>
        public void Send<T>(T message, object context)
        {
            IEnumerable<KeyValuePair<MessengerKey, object>> result;

            if (context == null)
            {
                // Get all recipients where the context is null.
                result = from r in Dictionary where r.Key.Context == null select r;
            }
            else
            {
                // Get all recipients where the context is matching.
                result = from r in Dictionary where r.Key.Context != null && r.Key.Context.Equals(context) select r;
            }

            foreach (var action in result.Select(x => x.Value).OfType<Action<T>>())
            {
                // Send the message to all recipients.
                action(message);
            }
        }

        protected class MessengerKey
        {
            public object Recipient { get; private set; }
            public object Context { get; private set; }

            /// <summary>
            /// Initializes a new instance of the MessengerKey class.
            /// </summary>
            /// <param name="recipient"></param>
            /// <param name="context"></param>
            public MessengerKey(object recipient, object context)
            {
                Recipient = recipient;
                Context = context;
            }

            /// <summary>
            /// Determines whether the specified MessengerKey is equal to the current MessengerKey.
            /// </summary>
            /// <param name="other"></param>
            /// <returns></returns>
            protected bool Equals(MessengerKey other)
            {
                return Equals(Recipient, other.Recipient) && Equals(Context, other.Context);
            }

            /// <summary>
            /// Determines whether the specified MessengerKey is equal to the current MessengerKey.
            /// </summary>
            /// <param name="obj"></param>
            /// <returns></returns>
            public override bool Equals(object obj)
            {
                if (ReferenceEquals(null, obj)) return false;
                if (ReferenceEquals(this, obj)) return true;
                if (obj.GetType() != GetType()) return false;

                return Equals((MessengerKey)obj);
            }

            /// <summary>
            /// Serves as a hash function for a particular type. 
            /// </summary>
            /// <returns></returns>
            public override int GetHashCode()
            {
                unchecked
                {
                    return ((Recipient != null ? Recipient.GetHashCode() : 0) * >397) ^ (Context != null ?
                    Context.GetHashCode() : 0);
                }
            }
        }
    }
}
Juan Salvador Portugal
  • 1,233
  • 4
  • 20
  • 38