We wzorcu MVVM dla WPF obsługa okien dialogowych jest jedną z bardziej złożonych operacji. Ponieważ model widoku nie wie nic o widoku, komunikacja w dialogu może być interesująca. Mogę ujawnić, ICommand
że gdy widok go wywołuje, może pojawić się okno dialogowe.
Czy ktoś zna dobry sposób obsługi wyników z okien dialogowych? Mówię o oknach dialogowych systemu Windows, takich jak MessageBox
.
Jednym ze sposobów, w jaki to zrobiliśmy, było zdarzenie w viewmodel, które widok subskrybowałby, gdy wymagane byłoby okno dialogowe.
public event EventHandler<MyDeleteArgs> RequiresDeleteDialog;
To jest OK, ale oznacza, że widok wymaga kodu, od którego chciałbym się trzymać.
Odpowiedzi:
Proponuję zrezygnować z dialogów modalnych z lat 90. i zamiast tego wdrożyć formant jako nakładkę (płótno + pozycjonowanie bezwzględne) z widocznością powiązaną z wartością logiczną z powrotem na maszynie wirtualnej. Bliżej kontrolki typu ajax.
Jest to bardzo przydatne:
jak w:
Oto jak mam jeden zaimplementowany jako kontrola użytkownika. Kliknięcie „x” zamyka kontrolkę w wierszu kodu w kodzie kontrolki użytkownika z tyłu. (Ponieważ mam moje widoki w pliku .exe i ViewModels w bibliotece dll, nie czuję się źle z powodu kodu, który manipuluje interfejsem użytkownika.)
źródło
W tym celu powinieneś użyć mediatora. Mediator to wspólny wzorzec projektowy, znany również jako Messenger w niektórych jego implementacjach. Jest to paradygmat typu Zarejestruj / Powiadom i umożliwia Twojemu ViewModel i Widokom komunikowanie się za pośrednictwem nisko sprzężonego mechanizmu przesyłania wiadomości.
Powinieneś sprawdzić grupę Google WPF Disciples i po prostu poszukać Mediatora. Będziesz bardzo zadowolony z odpowiedzi ...
Możesz jednak zacząć od tego:
http://joshsmithonwpf.wordpress.com/2009/04/06/a-mediator-prototype-for-wpf-apps/
Cieszyć się !
Edycja: odpowiedź na ten problem z zestawem narzędzi MVVM Light Toolkit można znaleźć tutaj:
http://mvvmlight.codeplex.com/Thread/View.aspx?ThreadId=209338
źródło
Dobre okno dialogowe MVVM powinno:
Niestety WPF nie zapewnia tych funkcji. Wyświetlenie okna dialogowego wymaga wywołania kodowego
ShowDialog()
. Klasa Window, która obsługuje okna dialogowe, nie może być zadeklarowana w XAML, więc nie może być łatwo powiązana z danymiDataContext
.Aby rozwiązać ten problem, napisałem kontrolkę kodu pośredniczącego XAML, która znajduje się w drzewie logicznym i przekazuje wiązanie danych do a
Window
oraz uchwyty pokazujące i ukrywające okno dialogowe. Można go znaleźć tutaj: http://www.codeproject.com/KB/WPF/XAMLDialog.aspxJest naprawdę prosty w użyciu i nie wymaga żadnych dziwnych zmian w ViewModel i nie wymaga zdarzeń ani wiadomości. Podstawowe połączenie wygląda następująco:
Prawdopodobnie chcesz dodać styl, który określa
Showing
. Wyjaśniam to w moim artykule. Mam nadzieję, że to Ci pomoże.źródło
"Showing a dialog requires a code-behind"
mmm, możesz to nazwać w ViewModelUżywam tego podejścia do dialogów z MVVM.
Teraz muszę tylko wywołać następujące z mojego modelu widoku.
źródło
Moje obecne rozwiązanie rozwiązuje większość problemów, o których wspomniałeś, ale jest całkowicie oderwane od rzeczy specyficznych dla platformy i można je ponownie wykorzystać. Również nie użyłem żadnego wiązania tylko kodem z DelegateCommands, które implementują ICommand. Okno dialogowe to w zasadzie widok - oddzielna kontrolka, która ma swój własny ViewModel i jest pokazywana z ViewModel ekranu głównego, ale wyzwalana z interfejsu użytkownika poprzez powiązanie DelagateCommand.
Zobacz pełne rozwiązanie Silverlight 4 Modalne okna dialogowe z MVVM i Silverlight 4
źródło
Naprawdę zmagałem się z tą koncepcją przez jakiś czas, kiedy uczyłem się (wciąż się uczę) MVVM. To, co zdecydowałem i myślę, że inni już zdecydowali, ale nie było dla mnie jasne, to:
Moją pierwotną myślą było to, że ViewModel nie powinien mieć możliwości bezpośredniego wywoływania okna dialogowego, ponieważ nie ma biznesu decydującego o sposobie wyświetlania okna dialogowego. Z tego powodu zacząłem myśleć o tym, jak mogę przekazywać wiadomości tak, jak w MVP (tj. View.ShowSaveFileDialog ()). Myślę jednak, że to niewłaściwe podejście.
Jest OK, aby ViewModel bezpośrednio wywoływał okno dialogowe. Jednak podczas testowania ViewModel oznacza to, że okno dialogowe albo wyskoczy podczas testu, albo zakończy się niepowodzeniem (nigdy tak naprawdę nie próbowałem tego).
Tak więc to, co musi się zdarzyć, polega na tym, aby podczas testowania użyć „testowej” wersji okna dialogowego. Oznacza to, że w każdym oknie dialogowym, które masz, musisz utworzyć interfejs i albo wykpić odpowiedź okna dialogowego, albo stworzyć próbny test, który będzie miał domyślne zachowanie.
Powinieneś już używać jakiegoś Lokalizatora usług lub IoC, który możesz skonfigurować, aby zapewnić poprawną wersję w zależności od kontekstu.
Korzystając z tego podejścia, Twój ViewModel jest nadal testowalny i zależnie od tego, jak wykpisz swoje okna dialogowe, możesz kontrolować zachowanie.
Mam nadzieję że to pomoże.
źródło
Są dwa dobre sposoby, aby to zrobić: 1) usługa dialogu (łatwa, czysta) i 2) pomoc w wyświetlaniu. Wspomaganie widoku zapewnia kilka ciekawych funkcji, ale zwykle nie jest tego warte.
USŁUGA DIALOGOWA
a) interfejs usługi okna dialogowego, taki jak przez konstruktora lub jakiś kontener zależności:
interface IDialogService { Task ShowDialogAsync(DialogViewModel dlgVm); }
b) Wdrożenie IDialogService powinno otworzyć okno (lub wstrzyknąć trochę kontroli do aktywnego okna), stworzyć widok odpowiadający nazwie danego typu dlgVm (użyj rejestracji lub konwencji kontenera lub ContentPresenter z typem DataTemplates). ShowDialogAsync powinien utworzyć TaskCompletionSource i zwrócić jego właściwość .Task. Sama klasa DialogViewModel potrzebuje zdarzenia, które można wywołać w klasie pochodnej, gdy chce się zamknąć, i oglądać w oknie dialogowym, aby faktycznie zamknąć / ukryć okno dialogowe i ukończyć TaskCompletionSource.
b) Aby użyć, po prostu wywołaj polecenie wyczekuj this.DialogService.ShowDialog (myDlgVm) w twojej instancji klasy wywodzącej się z DialogViewModel. Po oczekiwaniu na powrót sprawdź właściwości dodane do maszyny Wirtualnej okna dialogowego, aby ustalić, co się stało; nawet nie potrzebujesz oddzwonienia.
WIDOK Z POMOCĄ
Dzięki temu twój widok nasłuchuje zdarzenia na viewmodel. Można to wszystko zawrzeć w zachowaniu mieszania, aby uniknąć opóźnień w korzystaniu z kodu i zasobów, jeśli masz takie skłonności (FMI, podklasę klasy „Zachowanie”, aby zobaczyć coś w rodzaju mieszanej właściwości dołączanej na sterydach). Na razie zrobimy to ręcznie dla każdego widoku:
a) Utwórz OpenXXXXXDialogEvent z niestandardowym ładunkiem (klasa pochodna DialogViewModel).
b) Poproś widok, aby zasubskrybował to wydarzenie w swoim zdarzeniu OnDataContextChanged. Pamiętaj, aby ukryć i anulować subskrypcję, jeśli stara wartość! = Null oraz w zdarzeniu okna rozładowanego.
c) Gdy zdarzenie się uruchomi, otwórz widok, który może znajdować się w zasobie na Twojej stronie, lub możesz go zlokalizować zgodnie z konwencją w innym miejscu (np. w podejściu do usługi dialogu).
To podejście jest bardziej elastyczne, ale wymaga więcej pracy. Nie używam tego dużo. Jedną miłą zaletą jest na przykład fizyczne umieszczenie widoku wewnątrz karty. Użyłem algorytmu, aby umieścić go w granicach bieżącego formantu użytkownika, a jeśli nie jest wystarczająco duży, przejdź w górę drzewa wizualnego, aż znajdzie się wystarczająco duży kontener.
Dzięki temu okna dialogowe mogą znajdować się w pobliżu miejsca, w którym faktycznie są używane, przyciemnić tylko część aplikacji związaną z bieżącą aktywnością i pozwolić użytkownikowi poruszać się po aplikacji bez konieczności ręcznego odsuwania okien dialogowych, a nawet mieć wiele quasi- modalne okna dialogowe otwierają się na różnych kartach lub widokach podrzędnych.
źródło
Użyj polecenia zamrażania
źródło
Uważam, że obsługa okna dialogowego powinna odpowiadać za widok, a widok musi mieć kod do obsługi tego widoku.
Jeśli zmienisz interakcję ViewModel - View w celu obsługi okien dialogowych, wówczas ViewModel jest zależny od tej implementacji. Najprostszym sposobem rozwiązania tego problemu jest uczynienie Widoku odpowiedzialnym za wykonanie zadania. Jeśli oznacza to wyświetlenie okna dialogowego, to dobrze, ale może to być również komunikat o stanie na pasku stanu itp.
Chodzi mi o to, że cały punkt wzorca MVVM polega na oddzieleniu logiki biznesowej od GUI, więc nie powinieneś mieszać logiki GUI (aby wyświetlić okno dialogowe) w warstwie biznesowej (ViewModel).
źródło
Ciekawą alternatywą jest użycie kontrolerów odpowiedzialnych za wyświetlanie widoków (okien dialogowych).
Jak to działa, pokazuje WPF Application Framework (WAF) .
źródło
Dlaczego po prostu nie zgłosić zdarzenia na maszynie wirtualnej i zasubskrybować to wydarzenie w widoku? Pozwoliłoby to zachować logikę aplikacji i widok osobno i nadal pozwalało na korzystanie z okna potomnego do okien dialogowych.
źródło
Zaimplementowałem zachowanie, które nasłuchuje komunikatu z ViewModel. Opiera się na rozwiązaniu Laurent Bugnion, ale ponieważ nie wykorzystuje kodu z tyłu i jest bardziej wielokrotnego użytku, myślę, że jest bardziej elegancki.
Jak sprawić, aby WPF zachowywał się tak, jakby MVVM był obsługiwany po wyjęciu z pudełka
źródło
Myślę, że widok może mieć kod do obsługi zdarzenia z modelu widoku.
W zależności od zdarzenia / scenariusza może również mieć wyzwalacz zdarzenia, który subskrybuje, aby wyświetlić zdarzenia modelowe, i jedną lub więcej akcji do wywołania w odpowiedzi.
źródło
Miałem tę samą sytuację i zapakowałem MessageBox w niewidzialną kontrolkę projektanta. Szczegóły są na moim blogu
http://geekswithblogs.net/mukapu/archive/2010/03/12/user-prompts-messagebox-with-mvvm.aspx
To samo można rozszerzyć na dowolne modalne okna dialogowe, kontrolę przeglądania plików itp.
źródło
Uruchomiłem własny moduł ładujący okna opisany w odpowiedzi na to pytanie:
Zarządzanie wieloma widokami WPF w aplikacji
źródło
Karl Shifflett stworzył przykładową aplikację do wyświetlania okien dialogowych przy użyciu podejścia serwisowego i podejścia Prism InteractionRequest.
Podoba mi się podejście do usługi - jest mniej elastyczne, więc użytkownicy są mniej podatni na uszkodzenie :) Jest to również zgodne z częścią mojej aplikacji WinForm (MessageBox.Show) Ale jeśli planujesz wyświetlać wiele różnych okien dialogowych, to InteractionRequest jest lepszy sposób.
http://karlshifflett.wordpress.com/2010/11/07/in-the-box-ndash-mvvm-training/
źródło
Wiem, że to stare pytanie, ale kiedy przeprowadziłem to wyszukiwanie, znalazłem wiele powiązanych pytań, ale nie znalazłem naprawdę jasnej odpowiedzi. Tworzę więc własną implementację okna dialogowego / skrzynki wiadomości / popin i udostępniam to!
Myślę, że jest to „dowód MVVM” i staram się, aby było to proste i właściwe, ale jestem nowy w WPF, więc nie krępuj się komentować, a nawet wysyłać żądania ściągania.
https://github.com/Plasma-Paris/Plasma.WpfUtils
Możesz użyć tego w następujący sposób:
Lub jeśli chcesz bardziej wyrafinowanego popina:
I pokazuje takie rzeczy:
źródło
Standardowe podejście
Po latach spędzonych na rozwiązywaniu tego problemu w WPF, w końcu wymyśliłem standardowy sposób wdrażania dialogów w WPF. Oto zalety tego podejścia:
Więc jaki jest klucz. Jest to DI + IoC .
Oto jak to działa. Używam MVVM Light, ale to podejście może zostać rozszerzone również na inne frameworki:
Dodaj interfejs IDialogService do projektu VM:
Ujawnij publiczną właściwość statyczną
IDialogService
typu w swoimViewModelLocator
, ale pozostaw część rejestracyjną do wykonania dla warstwy Widok. To jest klucz :Dodaj implementację tego interfejsu w projekcie aplikacji.
ShowMessage
,AskBooleanQuestion
etc.), inne są specyficzne dla tego projektu i wykorzystanie niestandardowychWindow
s. Możesz dodać więcej niestandardowych okien w ten sam sposób. Kluczem jest utrzymanie elementów specyficznych dla interfejsu użytkownika w warstwie widoku i po prostu odsłonięcie zwróconych danych za pomocą POCO w warstwie maszyny wirtualnej .Wykonaj rejestrację IoC swojego interfejsu w warstwie widoku za pomocą tej klasy. Możesz to zrobić w konstruktorze głównego widoku (po
InitializeComponent()
wywołaniu):Proszę bardzo. Masz teraz dostęp do wszystkich funkcji okna dialogowego na warstwach VM i View. Twoja warstwa maszyny wirtualnej może wywoływać następujące funkcje:
Inne bezpłatne profity
IDialogService
w swoim projekcie testowym i zarejestrować tę klasę w IoC w konstruktorze swojej klasy testowej.Microsoft.Win32
dostęp do okien dialogowych Otwórz i Zapisz. Zostawiłem je, ponieważ dostępna jest również wersja tych okien dialogowych WinForm, a także ktoś może chcieć stworzyć własną wersję. Zauważ też, że niektóre z używanych identyfikatorówDialogPresenter
to nazwy własnych okien (npSettingsWindow
.). Musisz usunąć je zarówno z interfejsu, jak i z implementacji, lub podać własne okna.DispatcherHelper.Initialize()
wczesnym etapie cyklu życia aplikacji.Z wyjątkiem sytuacji, w
DialogPresenter
której wstrzykiwany jest do warstwy View, inne ViewModals powinny zostać zarejestrowane,ViewModelLocator
a następnie publiczna właściwość statyczna tego typu powinna zostać ujawniona, aby warstwa View mogła zużyć. Coś takiego:W przeważającej części twoje okna dialogowe nie powinny mieć żadnych opóźnień w kodowaniu dla takich rzeczy jak wiązanie lub ustawianie DataContext itp. Nie powinieneś nawet przekazywać rzeczy jako parametrów konstruktora. XAML może zrobić to wszystko za Ciebie:
DataContext
ten sposób zapewnia wszystkie korzyści w czasie projektowania, takie jak Intellisense i automatyczne uzupełnianie.Mam nadzieję, że pomaga wszystkim.
źródło
Zastanawiałem się nad podobnym problemem, pytając, jak powinien wyglądać model widoku dla zadania lub okna dialogowego .
Moje obecne rozwiązanie wygląda następująco:
Gdy model widoku decyduje, że dane wejściowe użytkownika są wymagane, wyświetla instancję
SelectionTaskModel
z możliwymi opcjami dla użytkownika. Infrastruktura zajmuje się wyświetlaniem odpowiedniego widoku, który w odpowiednim czasie wywołaChoose()
funkcję z wyborem użytkownika.źródło
Walczyłem z tym samym problemem. Wymyśliłem sposób na komunikację między View a ViewModel. Możesz zainicjować wysłanie wiadomości z ViewModel do View, aby kazać mu wyświetlić okno komunikatu, a on zgłosi się z wynikiem. Następnie ViewModel może odpowiedzieć na wynik zwrócony z widoku.
Wykazuję to na moim blogu :
źródło
Napisałem dość obszerny artykuł na ten temat, a także opracowałem pop-in bibliotekę dla MVVM Dialogs. Ścisłe przestrzeganie MVVM jest nie tylko możliwe, ale bardzo czyste, jeśli jest odpowiednio zaimplementowane, i można je łatwo rozszerzyć na biblioteki innych firm, które nie przestrzegają go same:
https://www.codeproject.com/Articles/820324/Implementing-Dialog-Boxes-in-MVVM
źródło
Przepraszam, ale muszę się włączyć. Przed znalezieniem przestrzeni nazw Prism.Wpf.Interactivity w projekcie Prism zapoznałem się z kilkoma sugerowanymi rozwiązaniami. Możesz użyć żądań interakcji i akcji wyskakującego okienka, aby rzucić okno niestandardowe lub dla prostszych potrzeb wbudowane są wyskakujące okienka powiadomień i potwierdzeń. Tworzą one prawdziwe okna i jako takie są zarządzane. możesz przekazać obiekt kontekstowy z dowolnymi zależnościami potrzebnymi w oknie dialogowym. Korzystamy z tego rozwiązania w mojej pracy, odkąd go znalazłem. Mamy tutaj wielu starszych deweloperów i nikt nie wymyślił nic lepszego. Naszym poprzednim rozwiązaniem była usługa dialogu w nakładce i użycie klasy prezentera, aby to się stało, ale musieliście mieć fabryki dla wszystkich modeli okien dialogowych itp.
To nie jest trywialne, ale też nie jest bardzo skomplikowane. I jest wbudowany w Pryzmat i dlatego jest najlepszą (lub lepszą) praktyką IMHO.
Moje 2 centy!
źródło
EDYCJA: tak Zgadzam się, że to nie jest poprawne podejście MVVM i teraz używam czegoś podobnego do tego, co sugerują blindmeis.
Jednym ze sposobów na to jest
W głównym widoku modelu (w którym otwierasz modal):
A w twoim oknie modalnym View / ViewModel:
XAML:
ViewModel:
lub podobny do zamieszczonego tutaj WPF MVVM: Jak zamknąć okno
źródło