<p>Framework MVVM Light udostępnia funkcjonalność pozwalającą na łatwą wymianę informacji między obiektami,szczególnie w przypadku modułowej budowy aplikacji. Jest za to odpowiedzalna klasa Messenger (ewentualnie nasza własna klasa, implementująca interfejs IMessenger). Głownym, i chyba najbardziej istotnym, efektem jej użycia, jest zmniejszenie sztywnych powiązań między widokami modelu. Messenger jest implementacją wzorca projektowego Mediator. Ogólnie wygląda to tak, że Messenger pozwala na wysyłanie wiadomości do swoich odbiorców, w taki sposób, że odbiorca nie wie kto wysłał wiadomość, a sam Messenger nie wie kiedy odbiorca ją otrzymał.</p>
<h2>Podstawowy schemat użycia Messenger’a:</h2>
<p><b>1.</b> Implementujemy pożądany typ wiadomości, lub wybieramy którąś z wiadomości wbudowanych.</p>
<p><b>2.</b> Odbiorca rejestruje wiadomość – to znaczy mówi, że chce otrzymywać taki a taki typ wiadomości.</p>
<p><b>3.</b> Nadawca (np.. obiekt w innym module programu) wysyła wiadomość przy pomocy Messengera.</p>
<p><b>4.</b> Gdy odbiorca „nie chce” już otrzymywać wiadomości, powinien ją wyrejestrować (aczkolwiek w przypadku WPF’a garbage collector radzi sobie z wszelkimi problemami z pozostawionymi, a niepotrzebnymi już subskrypcjami.)</p>
<h2>Więc po kolei:</h2>
<p><b>Implementacja wiadomośći:</b></p>
<p>Wiadomością może być jakikolwiek typ, zarówno wbudowany jak i własny. Jest też dostępnych kilka wiadomości zdefiniowanych przez twórców framework’a. Nadawca tworzy obiekt wiadomości, natomiast odbiorca posiada go jako parametr metody obsługującej otrzymaną wiadomość. Bardzo podobne do… Do czego? 😉</p>
<pre class="language-" tabindex="0"><code class="language-"> public class OpenViewMessage
{
public OpenViewMessage(TypesOfViews typeOfView, short profileId = 0)
{
this.TypeOfViewToOpen = typeOfView;
this.ProfileId = profileId;
}
public TypesOfViews TypeOfViewToOpen { get; private set; }
public short ProfileId { get; private set; }
}
</code></pre>
<p><b>Rejestrowanie wiadomości:</b></p>
<p>Klasa, która ma mieć możliwość odbioru wiadomości, musi ją zarejestrować, i wskazać jaka będzie jej reakcja. Rejestrowanie ma miejsce zwykle w konstruktorze.</p>
<pre class="language-" tabindex="0"><code class="language-">public ViewsSwitcher()
{
Messenger.Default.Register<OpenViewMessage>(this, this.ViewTransition);
}
</code></pre>
<p>Pierwszy parametr metody <i>Register</i>, wskazuję odbiorcę wiadomości, natomiast drugi to delegat <i>Action<TypWiadomości></i>, wskazujący co ma się stać gdy odbierzemy wiadomość. W tym wypadku wskazujemy, że ma zostać wywołana metoda <i>ViewTransition</i>, która może wyglądać choćby tak:</p>
<pre class="language-" tabindex="0"><code class="language-">private void ViewTransition(OpenViewMessage msg)
{
switch (msg.TypeOfViewToOpen)
{
case TypesOfViews.CommonViewModule:
ServiceLocator.Current.GetInstance<ISimpleNavigationService>().NavigateTo(new CommonWindow(msg.ProfileId));
break;
case TypesOfViews.UserData:
ServiceLocator.Current.GetInstance<ISimpleNavigationService>().NavigateTo(new UserDataView(msg.ProfileId));
break;
case TypesOfViews.UserDataSetter:
ServiceLocator.Current.GetInstance<ISimpleNavigationService>().NavigateTo(new UserDataSetterView(msg.ProfileId));
break;
}
}
</code></pre>
<p><b>Wysyłanie wiadomości:</b></p>
<p>W wybranym przez nas momencie, jakaś klasa może wysłać wiadomość. Co istotne to wysyłający musi być oczywiśćie świadomy istnienia wiadomości, ale już kompletnie nie interesuje go kim jest odbiorca.</p>
<pre class="language-" tabindex="0"><code class="language-">private void OpenDataSetter()
{
MessengerInstance.Send(new OpenViewMessage(TypesOfViews.UserDataSetter, this.IdOfProfile));
}
</code></pre>
<p><b>Wyrejestrowanie wiadomości:</b></p>
<p>Trzeba pamiętać, że gdy odbiorca rejestruje wiadomość to Messenger będzie cały czas przechowywał referencjędo metody obsługującej tę wiadomość. I tutaj pojawia się pierwszy zgrzyt. Co prawda zazwyczaj wspomniana referencja jest tzw. weak reference (zmienna jest tworzona w specjalny sposób, poprzez klasę WeakReference. Pozwala to na posiadanie referencji do obiektu, nawet gdy wszystkie silne referencje są usunięte). Nie zawsze jest to jednak możliwe. O ile dla WPF’a nie ma z tym żadnego problemu, to w przypadku Silverlight’a (i win phone), jeżeli metoda obsługująca wiadomość jest oznaczona specyfikatorem dostępu: internal lub private( tyczy się to też wyrażeń lambda), to zamiast weak reference tworzona jest strong reference. Wówczas, pozostawienie wiadomości nie wyrejestrowanej , może powodować wyciek pamięci. Także użytkownicy Silverlight’a – strzeżcie się! :).</p>
<p>Jak widać używanie Messengera, jest dziecinnie proste (siekiery też, a łapę uciąć można ;)), należy jednak pamiętać także o problemach, które mogą być powodowane przez jego nadużywanie. Loose coupling, samoistnie nie chroni przed spaghetti code’m. To jednak temat na inną okazję 🙂</p>