Bir MVVM uygulamasında navigasyonu kim kontrol etmelidir?


33

Örnek 1: MVVM uygulamamda görüntülenen bir görüşüm var (tartışma için Silverlight'ı kullanalım) ve beni yeni bir sayfaya götürmesi gereken bir düğmeyi tıklıyorum.

Örnek 2: Aynı görünümde, tıklandığında alt pencerede bir ayrıntı görünümü açması gereken başka bir düğme bulunur (iletişim kutusu).

ViewModel'imizin maruz bıraktığı komut nesnelerinin, kullanıcının tıklamasına yanıt veren yöntemlerle düğmelere bağlı olacağını biliyoruz. Ama, o zaman ne? İşlemi nasıl tamamlarız? NavigationService denilen bir şey kullansak bile, ne söylüyoruz?

Daha açık olmak gerekirse, geleneksel bir View-first modelinde (web'de veya SL'de yerleşik gezinme çerçevesinde olduğu gibi URL tabanlı gezinme şemaları gibi), Command nesnelerinin daha sonra hangi görünümün görüntüleneceğini bilmesi gerekir. Bu, model tarafından desteklenen endişelerin ayrılması söz konusu olduğunda çizgiyi geçiyor gibi görünüyor.

Öte yandan, düğme bir Command nesnesine bağlı değilse ve köprü gibi davrandıysa, gezinme kuralları işaretlemede tanımlanabilir. Ancak, Görünümlerin uygulama akışını kontrol etmesini istiyoruz ve navigasyon sadece başka bir iş mantığı türü değil mi? (Bazı durumlarda evet, bazılarında hayır diyebilirim.)

Bana göre, MVVM modelinin ütopik uygulaması (ve başkalarının bunu söylediğini duydum), ViewModel'in, uygulamanın başsız çalışabileceği şekilde (örn. Görüntüleme Yok) çalışmasını sağlayacak şekilde kablolanmış olması olacaktır. Bu, kod tabanlı test için en fazla yüzey alanını sağlar ve Görünümleri uygulamada gerçek bir görünüm oluşturur. Ve ViewModel'im ana pencerede, hareketli bir panelde veya bir alt pencerede görüntülenip görüntülenmemesine aldırış etmemeli, değil mi?

Bu değerlendirmeye göre, çalışma zamanında her ViewModel için hangi View'in gösterilmesi gerektiğini 'bağlamak' mümkün. Ancak, bir Görünümü birden fazla ViewModels ile veya tam tersi olarak paylaşmak istiyorsak ne olur?

Böylece View-ViewModel ilişkisini yönetme ihtiyacı duyulduğundan, pencereler / diyaloglar gösterme dahil görünümler arasında gezinme ihtiyacı ile birlikte ne göstereceğimizi biliyoruz, bunu MVVM modelinde nasıl başarıyoruz?

Yanıtlar:


21

Navigasyon daima ViewModel'de kullanılmalıdır.

MVVM tasarım modelinin mükemmel bir şekilde uygulanmasının, uygulamanızı tamamen Görünümler olmadan çalıştırabileceğiniz anlamına geldiğini ve Görünümleriniz Navigasyonunuzu kontrol ederse yapamayacağınız anlamına geldiğini düşünerek doğru yoldasınız.

Genelde başvurumun genel durumunu idare eden bir ApplicationViewModelveya ShellViewModelvardır. Bu CurrentPage(bir ViewModel olan) ve işlem kodunu içerir ChangePageEvents. (Genellikle, CurrentUser veya ErrorMessages gibi uygulama genelinde diğer nesneler için de kullanılır)

Bu nedenle, herhangi bir ViewModel, herhangi bir yerde, a yayınlarsa ChangePageEvent(new SomePageViewModel), ShellViewModelo mesajı alacak ve mesajda CurrentPagebelirtilen herhangi bir sayfaya geçecektir .

Aslında ilgileniyorsanız MVVM ile Navigasyon hakkında bir blog yazısı yazdım.


2
İlginç yaklaşım Dört yorum: 1) Silverlight, DataTemplate üzerindeki DataType özelliğini desteklemediğinden, DataTemplate'i ViewModel ile eşleştirmek SL'de mümkün değildir. 2) Bu, Views ve ViewModels arasındaki çoktan çoğa olasılıkları ele almıyor. 3) Alt pencereleri işlemez (ya da en azından nasıl olduğunu bilmiyorum). 4) Uygulamanız / Shell ViewModel'iniz ve çocukları (torunlar, vb.) Arasında sıkı bir bağlantı kurulmasını gerektirir.
SonOfPirate 16:11

Bu, kesinlikle dikkate alınması gereken bir şey dedi.
SonOfPirate 16:11

@SonOfPirate 1) Silverlight, örtük DataTemplate eşlemesini (henüz) desteklemez, ancak DataTemplateSelectorgenellikle Silverlight uygulamaları için kullandığım a özelliğini de destekler . 2) Bunu daha önce çok-çok durumlarda kullandım. Örneğin, bir ViewModel birden fazla View'a sahip olabilir veya bir View birden fazla ViewModels ile ilişkilendirilebilir. Bir önceki örnek için bkz: rachel53461.wordpress.com/2011/05/28/… . Daha sonra, yalnızca DataTemplateSelector öğenizde aynı View ile aynı ViewModels eşlemesini belirtmeniz gerekir.
Rachel

3) Bu sadece temel bir örnek. Uygulamanızın birden fazla pencere olacağını bilseydiniz, ShellViewModel’i birden fazla ele alması için açıkça değiştirirdiniz CurrentPages. Gerçekte, PageViewModels'imin hepsi bazı temel sınıflara dayanıyor, bu yüzden ShellViewModel'im sadece gibi temel sınıf veya arayüzlerle çalışıyor IPageViewModel. Gerçekten en büyük dağınık haritalama parçası, 40 ViewModels ile 40 Views'u haritalandırması gereken DataTemplateSelector.
Rachel,

@SonOfPirate Umarım bazı sorularınızı cevaplamışsınızdır. Başkaları varsa beni Sohbet etmekten çekinmeyin :)
Rachel

6

Kapatma uğruna, nihayet bu sorunu çözmek için seçtiğim yönü işaretleyeceğimi düşündüm.

İlk karar, kullanıma hazır Silverlight Sayfa Gezinme çerçevesinden yararlanmaktı. Bu karar, bu tür bir gezinti türünün Microsoft tarafından Windows 8 Metro uygulamalarına taşındığı ve Phone 7 uygulamalarındaki gezinti ile tutarlı olduğu bilgisi dahil olmak üzere birkaç faktöre dayanıyordu.

Çalışmasını sağlamak için daha sonra ASP.NET MVC'nin kongre tabanlı navigasyonla yaptığı işe baktım. Kare kontrolü, görüntülenecek 'sayfayı' bulmak için URI'ları kullanır. Benzerlik, Silverlight uygulamasında benzer bir kongre temelli yaklaşım kullanma fırsatı sağladı. İşin püf noktası, her şeyi birlikte MVVM tarzında çalıştırıyordu.

Çözüm, NavigationService'dir. Bu hizmet, ViewModels’in sayfa değişikliği başlatmak için kullanabileceği NavigateTo ve Back gibi birkaç yöntem sunar. Yeni bir sayfa istendiğinde, NavigationService, MVVMLight Messenger özelliğini kullanarak bir CurrentPageChangedMessage gönderir.

Çerçeve denetimini içeren görünümün, bu mesajı dinleyen DataContext olarak ayarlanmış kendi ViewModel'i vardır. Alındığında, yeni görünümün adı, kural kurallarımızı uygulayan ve CurrentPage özelliğine ayarlanan bir eşleme işlevinden geçirilir. Frame denetiminin Source özelliği, CurrentPage özelliğine bağlıdır. Sonuç olarak, özelliği ayarlamak Kaynak'ı günceller ve navigasyonu tetikler.

NavigationService'e geri dönüyoruz. NavigateTo yöntemi, hedef sayfanın adını kabul eder. ViewModel'in UI endişesi olmadığından emin olmak için kullanılan ad, görüntülenecek ViewModel adıdır. Aslında her gezinilebilir ViewModel için bir yardımcı olarak bir alan olan ve tüm uygulamadaki sihirli dizeleri yok eden bir numaralandırma oluşturdum. Yukarıda bahsettiğim haritalama fonksiyonu ismin "ViewModel" sonekini soracak, isme "Sayfa" ekleyecektir ve tam ismi "Views {Name} Page.xaml" olarak ayarlayacaktır.

Örneğin, müşteri ayrıntıları görünümüne gitmek için arayabilirim:

NavigationService.NavigateTo(ViewModels.CustomerDetails);

CustomerDetails değeri, "Views \ CustomerDetailsPage.xaml" ile eşlenen "CustomerDetailsViewModel" dir.

Bu yaklaşımın güzelliği, kullanıcı arayüzünün ViewModels'ten tamamen ayrılmasıdır, ancak tam navigasyon desteğine sahibiz. Ancak şimdi uygulamamı yeniden uygulayabilirim ve ne zaman herhangi bir kod değişikliği olmadan uygun olduğumu görebiliyorum.

Açıklama yardımcı olur umarım.


2

Rachel'ın söylediğine benzer şekilde, çoğunlukla MVVM uygulamamın Presenterpencereler veya sayfalar arasındaki anahtarları kullanması gerekir. Rachel Bu çağırır ApplicationViewModel, ama benim deneyim, genellikle daha adil başka yapacak olması bağlayıcı bir hedef (örneğin alıcı mesajlar gibi vb Windows'u oluşturmak) daha geleneksel bir benzeri teknik yüzden zorundadır Presenterya Controller.

Uygulamamda, Presenterbir ile başlar CurrentViewModel. PresenterArasındaki tüm iletişimi durdurur Viewve ViewModel. Şeylerden biri ViewModelbir etkileşim sırasında yapabileceği yeni bir dönüş olduğunu ViewModelyeni bir sayfa anlamına veya yeni olan Windowgörüntülenmesi gerekir. PresenterOluşturarak veya üzerine yazma ilgilenir Viewyeni için ViewModelve ayar DataContext.

Bir eylemin sonucu ayrıca ViewModel"tamamlandı" olabilir, bu durumda bunu Presenteralgılar ve pencereyi kapatır veya bunu ViewModelVM yığından çıkar ve önceki sayfayı görüntülemeye geri döner.


Sunucu hangi görünümün görüntüleneceğini nereden biliyor?
SonOfPirate 17:11

1
@SonOfPirate - Bu normalde WPF mekanizmaları ile yapılır. PresenterSadece sopa döndü ViewModelgörsel ağacında ve WPF uygun düzgün kapar View, kanca DataContexto görsel ağacında yerine, ve koyar. Bunu, DataTemplatene ViewModeltür işlediklerini bildiren s kullanarak veya özel bir veri şablonu seçicisi oluşturarak yapabilirsiniz. Bu hala WPF özellikleri alanında.
Scott Whitlock
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.