Im Beitrag Lose Kommunikation zwischen ViewModels habe ich eine Möglichkeit der Kommunikation unter ViewModels vorgestellt. Diese enthielt noch einen kleinen Bug, der mit der aktualisierten Variante ausgebessert wurde. Zudem wurde das Interface IMessenger um eine weitere Überladung der Methode Unregister erweitert.

interface IMessenger
{
    void Register<TNotification>(object recipient, Action<TNotification> action);
    void Register<TNotification>(object recipient, string identCode, Action<TNotification> action);

    void Send<TNotification>(TNotification notification);
    void Send<TNotification>(TNotification notification, string identCode);

    void Unregister<TNotification>(object recipient);
    void Unregister<TNotification>(object recipient, string identCode);
}

Es war zwar die Möglichkeit geboten, eine Objekt-Instanz über einen Identification Code zu registrieren, ein Entfernen genau dieser Registrierung war jedoch nicht möglich. Diese Möglichkeit ist nun gegeben.

public class Messenger : IMessenger
{
    private static Messenger instance;
    private static object lockObject = new object();

    private Dictionary<Type, List<ActionIdentifier>> references = new Dictionary<Type,List<ActionIdentifier>>();

    private Messenger() { }

    public static Messenger Instance
    {
        get
        {
            lock (lockObject)
            {
                if (instance == null)
                    instance = new Messenger();
                return instance;
            }
        }
    }

    #region IMessenger Members

    public void Register<TNotification>(object recipient, Action<TNotification> action)
    {
        Register<TNotification>(recipient, null, action);
    }

    public void Register<TNotification>(object recipient, string identCode, Action<TNotification> action)
    {
        Type messageType = typeof(TNotification);

        if (!references.ContainsKey(messageType))
            references.Add(messageType, new List<ActionIdentifier>());

        ActionIdentifier actionIdent = new ActionIdentifier();
        actionIdent.Action = new WeakReferenceAction<TNotification>(recipient, action);
        actionIdent.IdentificationCode = identCode;

        references[messageType].Add(actionIdent);
    }

    public void Send<TNotification>(TNotification notification)
    {
        Type type = typeof(TNotification);
        List<ActionIdentifier> typeActionIdentifiers = references[type];
        foreach (ActionIdentifier ai in typeActionIdentifiers)
        {
            IActionParameter actionParameter = ai.Action as IActionParameter;
            if (actionParameter != null)
                actionParameter.ExecuteWithParameter(notification);
            else
                ai.Action.Execute();
        }
    }

    public void Send<TNotification>(TNotification notification, string identCode)
    {
        Type type = typeof(TNotification);
        List<ActionIdentifier> typeActionIdentifiers = references[type];
        foreach (ActionIdentifier ai in typeActionIdentifiers)
        {
            if (ai.IdentificationCode == identCode)
            {
                IActionParameter actionParameter = ai.Action as IActionParameter;
                if (actionParameter != null)
                    actionParameter.ExecuteWithParameter(notification);
                else
                    ai.Action.Execute();
            }
        }
    }

    public void Unregister<TNotification>(object recipient)
    {
        Unregister<TNotification>(recipient, null);
    }

    public void Unregister<TNotification>(object recipient, string identCode)
    {
        bool lockTaken = false;

        try
        {
            Monitor.Enter(references, ref lockTaken);
            foreach (Type targetType in references.Keys)
            {
                foreach (ActionIdentifier wra in references[targetType])
                {
                    if (wra.Action != null && wra.Action.Target != null && wra.Action.Target.Target == recipient)
                        if (String.IsNullOrEmpty(identCode) || (!String.IsNullOrEmpty(identCode) && !String.IsNullOrEmpty(wra.IdentificationCode) && wra.IdentificationCode.Equals(identCode)))
                            wra.Action.Unload();
                }
            }
        }
        finally
        {
            if (lockTaken)
                Monitor.Exit(references);
        }
    }

    #endregion
}

Die verlinkte Beispielanwendung wurde dahingehend erweitert. Ebenfalls habe ich mein Testprojekt angefügt.

Download WPF Messaging Demo

Im nächsten Schritt wird von mir ein kleiner Last-/Performance-Test durchgeführt. Dieser sollte schnell zu erkennen geben, ob dieser Ansatz ressourcensparend ist, oder Optimierungen notwendig sind. Ein Einsatz dieser leichtgewichtigen Lösung sollte aber schon jetzt möglich sein. Weitere Anregungen etc. sind natürlich gerne gesehen.

Über den Autor

Norbert Eder

Ich bin ein leidenschaftlicher Softwareentwickler und Fotograf. Mein Wissen und meine Gedanken teile ich nicht nur hier im Blog, sondern auch in Fachartikeln und Büchern.