2010-04-05 4 views
17

Eksperymentowałem z często wspomnianym wzorcem MVVM i w niektórych przypadkach miałem trudności z określeniem wyraźnych granic. W mojej aplikacji mam okno dialogowe, które pozwala mi utworzyć połączenie z kontrolerem. Istnieje klasa ViewModel dla okna dialogowego, która jest dość prosta. Jednak okno dialogowe zawiera również dodatkową kontrolę (wybraną przez ContentTemplateSelector), która zmienia się w zależności od konkretnego typu kontrolera, który jest podłączony. Ta kontrola ma swój własny ViewModel.MVVM: Jak radzić sobie z interakcją między zagnieżdżonymi ViewModels?

Problem, który napotykam, polega na tym, że po zamknięciu okna dialogowego przez naciśnięcie przycisku OK, muszę utworzyć żądane połączenie, które wymaga przechwycenia informacji w wewnętrznej klasie ViewModel specyficznej dla kontrolera. Kuszące jest po prostu, aby wszystkie klasy ViewModel specyficzne dla kontrolera implementowały wspólny interfejs, który tworzy połączenie, ale czy wewnętrzny ViewModel naprawdę powinien być odpowiedzialny za tę konstrukcję?

Moje ogólne pytanie brzmi: czy istnieją ogólnie przyjęte wzorce projektowe dotyczące interakcji ViewModels ze sobą nawzajem, szczególnie gdy "rodzicielska" maszyna wirtualna potrzebuje pomocy z maszyny wirtualnej "dziecko", aby wiedzieć, co robić?


EDIT:

zrobiłem pochodzić z projektu, który jest trochę czystsze niż początkowo myślał, ale ja nadal nie jestem pewien, czy jest to „prawo” sposób to zrobić. Mam niektóre usługi zaplecza, które umożliwiają ContentTemplateSelector, aby spojrzeć na instancji kontrolera i pseudo-magicznie znaleźć kontrolkę do wyświetlania dla konstruktora połączenia. To, co mnie w tym wszystkim dręczyło, to że mój ViewModel na najwyższym poziomie musiałby przyjrzeć się DataContext wygenerowanemu sterowaniu i przesłać go do odpowiedniego interfejsu, co wydaje się złym pomysłem (dlaczego Widok DataContext ma cokolwiek wspólnego z tworzeniem ? połączenie)

i skończyłem z czymś takim (upraszczając):

public interface IController 
{ 
    IControllerConnectionBuilder CreateConnectionBuilder(); 
} 

public interface IControllerConnectionBuilder 
{ 
    ControllerConnection BuildConnection(); 
} 

mam mój wewnętrzny klasy ViewModel wdrożyć IControllerConnectionBuilder i kontroler zwraca wewnętrzną ViewModel. ViewModel najwyższego poziomu wizualizuje ten IControllerConnectionBuilder (poprzez mechanizm pseudo-magiczny). Trochę mi to przeszkadza, że ​​to mój wewnętrzny ViewModel wykonujący budynek, ale przynajmniej mój najlepszy ViewModel na najwyższym poziomie nie musi wiedzieć o brudnych detalach (nie ma nawet pojęcia, że ​​wizualizowane sterowanie używa ViewModel).

Z zadowoleniem przyjmuję dodatkowe przemyślenia, jeśli istnieją sposoby, aby to jeszcze bardziej oczyścić. Wciąż nie jest dla mnie jasne, na ile odpowiedzialność jest "w porządku" dla ViewModel.

+2

Zadajemy sobie takie pytanie ciągle w mojej pracy. Bardzo dobrze sformułowałeś to pytanie, więc mam nadzieję, że otrzymasz tutaj dobre opinie. –

+2

Na szczęście jest to projekt dla zwierząt domowych, więc mam luksus eksplorowania różnych projektów. Mój sklep nie zastosował WPF lub MVVM, ponieważ na początku tego okresu obciążenie i nieporęczność nie są akceptowane w naszych obecnych harmonogramach. Mocno wierzę, że jest to technologia, która będzie wypłacać duże dywidendy w wydajności, gdy zrozumiemy, jak z niego korzystać, ale jest to taka zmiana perspektywy, że trudno jest wiedzieć, gdzie narysować linie w projekcie. –

Odpowiedz

3

Opcją, która działa dobrze w przypadku interakcji między modelami view, jest bezpośrednie powiązanie z klasami observer umieszczonymi między klasami viewmodel.

+0

Dzięki za link; Używałem podobnego schematu do innej koordynacji, udostępniając usługę IMainViewModel, która jest zaimplementowana przez mój MainViewModel. Sądzę, że sensowne może być dalsze jego refaktoryzowanie, aby funkcja "współużytkowana" nie była związana w modelu dla głównego okna i zamiast tego była serwerem MainObserver. –

+1

To było pomocne podejście. Mój projekt jest wciąż trochę schizofreniczny, ale zaczynam widzieć, jak maszyny wirtualne mogą się komunikować za pomocą usług wspólnych. Czuję się nieco odwrócony, ponieważ jestem przyzwyczajony do "rodzica", który wie wszystko o swoich "dzieciach". Teraz jest to bardziej kwestią mojej klasy, mówiąc "muszę to zrobić" i częścią aplikacji, o której nie wiem prawie nic, aby wzmocnić i zająć się tym dla mnie. –

3

Myślę, że chcesz, aby Twój najwyższy poziom ViewModel był świadomy istnienia NestedViewModel, ma to sens z hierarchicznego punktu widzenia, widok główny zawiera widok potomny.

Moim zdaniem, twój instynkt ma rację, nie jest właściwe, aby zagnieżdżony ViewModel ujawniał zachowania zainicjowane przez akcje użytkownika na najwyższym poziomie. Zamiast tego najwyższy poziom ViewModel powinien zapewniać zachowania dla widoku, z którym jest powiązany.

Ale rozważyłbym przeniesienie odpowiedzialności za konstrukcję połączeń na ICommand i udostępnienie tego polecenia za pomocą najwyższego poziomu ViewModel. Przycisk OK w głównym oknie dialogowym zostanie powiązany z tym poleceniem, a polecenie przeniesie się do najwyższego poziomu ViewModel, na przykład wywołaj ViewModel.CreateConnection(), gdy zostanie wykonane.

Odpowiedzialność za zagnieżdżoną kontrolę polega wyłącznie na zbieraniu i udostępnianiu danych do numeru NestedViewModel, do wykorzystania przez plik zawierający ViewModel, i teoretycznie jest bardziej przydatny do ponownego wykorzystania w różnych kontekstach, które wymagają wprowadzenia tych samych informacji (jeśli dowolne) - załóżmy, że chcesz go ponownie użyć do edycji już utworzonych połączeń.

Jedyne zmarszczenie byłoby, gdyby różne typy NestedViewModel wystawiały radykalnie inny zestaw danych.

Na przykład, jedna naraża HostName i Port jako właściwości, a drugi naraża nazwa_użytkownika i Hasło.

W takim przypadku może zajść potrzeba wykonania pewnych prac infrastrukturalnych, aby Twój najwyższy poziom ochrony nadal działał w czysty sposób. Chociaż, jeśli masz małą liczbę zagnieżdżonych typów kontrolnych, może to nie być warte wysiłku, a prosty test typu i odlewania NestedViewModel może wystarczyć.

Czy to brzmi dobrze?

+0

Wyzwanie polega na tym, że zewnętrzny ViewModel nie wie o typie swojego InnerViewModel podczas kompilacji. Kontrolery są wprowadzane jako rozszerzenia aplikacji w środowisku wykonawczym, z pewnym rodzajem wykrywania pod maską, aby połączyć specyficzne zagnieżdżone sterowanie za pomocą niestandardowego DataTemplateSelector. Wiem, że DataContext będzie domyślnie specyficznym zagnieżdżonym ViewModel, ale czuję się hackowitym dla mnie, aby sprawdzić DataContext i spróbować rzucić go do wspólnego interfejsu. –

0

Niedawno eksperymentowałem z Unity (biblioteka Microsoft Enterprise), aby użyć wtrysku zależności. Może to być droga, którą należy przejść, używając interfejsów, które całkowicie definiują to, czego obie wizje potrzebują od siebie. MEF byłby inną opcją wstrzyknięcia zależności, o której wiem.

HTH

+0

Dzięki, faktycznie używam MEF w tej aplikacji i pomogło całkiem sporo w umożliwieniu bardzo bogatej rozszerzalności interfejsu użytkownika. Ta elastyczność pozwala tworzyć wyzwania projektowe, ponieważ interfejs użytkownika zawiera elementy sterujące, o których praktycznie nic nie wie.Tak naprawdę wymyśliłem bardziej przejrzysty sposób, aby to zrobić, o czym więcej będę mówić, kiedy wrócę dzisiaj do domu. –

Powiązane problemy