Obsługa okien dialogowych w WPF za pomocą MVVM

We wzorze MVVM dla WPF obsługa okien dialogowych jest jedną z bardziej złożonych operacji. Ponieważ twój model widoku nie wie nic o widoku, komunikacja dialogowa może być interesująca. Mogę wystawić polecenie ICommand, że gdy Widok go wywołuje, może pojawić się okno dialogowe.

Czy ktoś zna dobry sposób na obsługę wyników z okien dialogowych? Mówię o oknach dialogowych, takich jak MessageBox.

Jednym ze sposobów, w jaki to zrobiliśmy, było zdarzenie na viewmodel, które sprawiłoby, że widok Subskrybuj, gdy wymagane było okno dialogowe.

public event EventHandler<MyDeleteArgs> RequiresDeleteDialog;

To jest OK, ale oznacza to, że widok wymaga kodu, od którego chciałbym się trzymać z daleka.

Author: BoltClock, 2009-01-18

23 answers

Proponuję zrezygnować z modalnych okien dialogowych z Lat 90. i zamiast tego zaimplementować kontrolkę jako nakładkę (płótno+absolutne pozycjonowanie) z widocznością powiązaną z logiką w maszynie wirtualnej. Bliżej kontroli typu ajax.

To jest bardzo przydatne:

<BooleanToVisibilityConverter x:Key="booltoVis" />

Jak w:

<my:ErrorControl Visibility="{Binding Path=ThereWasAnError, Mode=TwoWay, Converter={StaticResource booltoVis}, UpdateSourceTrigger=PropertyChanged}"/>

Oto Jak mam jeden zaimplementowany jako kontrola użytkownika. Kliknięcie na ' x ' zamyka kontrolkę w linii kodu w kodzie kontrolera usercontrol za. (Ponieważ mam swoje poglądy wexe i ViewModels w dll, ja nie szkoda kodu, który manipuluje interfejsem użytkownika.)

Okno Wpf

 132
Author: Jeff K.,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-02-27 04:30:38

Powinieneś użyć mediatora. Mediator jest popularnym wzorcem projektowym znanym również jako Messenger w niektórych jego implementacjach. Jest to paradygmat typu Register/Notify i umożliwia komunikację ViewModel i Views za pomocą komunikatora o niskim sprzężeniu.

Powinieneś sprawdzić grupę Google WPF Disciples i po prostu poszukać mediatora. Będziesz bardzo zadowolony z odpowiedzi...

Można jednak zacząć od to:

Http://joshsmithonwpf.wordpress.com/2009/04/06/a-mediator-prototype-for-wpf-apps/

Enjoy !

Edit: możesz zobaczyć odpowiedź na ten problem z MVVM Light Toolkit tutaj:

Http://mvvmlight.codeplex.com/Thread/View.aspx?ThreadId=209338

 51
Author: Roubachof,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-06-14 14:49:47

Dobre okno dialogowe MVVM powinno:

  1. należy zadeklarować tylko za pomocą XAML.
  2. Get all of it ' s behavior from databinding.

Niestety, WPF nie zapewnia tych funkcji. Wyświetlanie okna dialogowego wymaga wywołania ShowDialog () za kodem. Klasa Window, która obsługuje okna dialogowe, nie może być zadeklarowana w XAML, więc nie może być łatwo przypisana do DataContext.

Aby to rozwiązać, napisałem kontrolkę stub XAML, która siedzi w drzewie logicznym i przekazuje bazę danych do Okno i uchwyty pokazujące i ukrywające okno dialogowe. Znajdziesz go tutaj: http://www.codeproject.com/KB/WPF/XAMLDialog.aspx

To naprawdę po prostu używać i nie wymaga żadnych dziwnych zmian do ViewModel i nie wymaga zdarzeń lub wiadomości. Podstawowe wywołanie wygląda tak:

<dialog:Dialog Content="{Binding Path=DialogViewModel}" Showing="True" />

Prawdopodobnie chcesz dodać styl, który ustawia wyświetlanie. Wyjaśnię to w moim artykule. Mam nadzieję, że to ci pomoże.

 28
Author: user92541,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-08-28 17:33:28

Używam tego podejścia do okien dialogowych z MVVM.

Wszystko, co muszę teraz zrobić, to wywołać następujący z mojego modelu widoku.

var result = this.uiDialogService.ShowDialog("Dialogwindow title goes here", dialogwindowVM);
 23
Author: blindmeis,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:10:36

Moje obecne rozwiązanie rozwiązuje większość problemów, o których wspomniałeś, ale jest całkowicie wyodrębnione z konkretnych rzeczy platformy i może być ponownie wykorzystane. Nie używałem też żadnego kodu - tylko wiązania z poleceniami Delegatecomands, które implementują ICommand. Okno dialogowe jest w zasadzie widokiem-osobną kontrolką, która ma swój własny ViewModel i jest wyświetlana z ViewModel głównego ekranu, ale wyzwalana z interfejsu użytkownika za pomocą powiązania DelagateCommand.

Zobacz pełne rozwiązanie Silverlight 4 tutaj modalne okna dialogowe z MVVM i Silverlight 4

 16
Author: Roboblob,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-01-21 21:14:17

Naprawdę przez jakiś czas zmagałem się z tą koncepcją, ucząc się (wciąż ucząc się) MVVM. To co postanowiłem, i to co chyba inni już zdecydowali, ale co nie było dla mnie jasne to:

Moja pierwotna myśl była taka, że ViewModel nie powinien mieć prawa do bezpośredniego wywoływania okna dialogowego, ponieważ nie ma prawa decydować o tym, jak powinno się pojawić okno dialogowe. W związku z tym zacząłem myśleć o tym, jak Mogę przekazywać wiadomości tak, jak miałbym w MVP (tj. View.ShowSaveFileDialog()). Jednak Ja myślę, że to złe podejście.

ViewModel może bezpośrednio wywoływać okno dialogowe. Jednak podczas testowania modelu widoku oznacza to, że okno dialogowe pojawi się podczas testu lub zawiedzie razem (nigdy tego nie próbowałem).

Tak więc podczas testowania należy użyć wersji "testowej" okna dialogowego. Oznacza to, że zawsze masz okno dialogowe, trzeba utworzyć interfejs i albo makieta odpowiedź okna dialogowego lub utworzyć makietę testową, która będzie miało domyślne zachowanie.

Powinieneś już używać jakiegoś lokalizatora usług lub IoC, który możesz skonfigurować, aby zapewnić Ci poprawną wersję w zależności od kontekstu.

Korzystając z tego podejścia, Twój model ViewModel jest nadal testowalny i w zależności od tego, jak wyśmiewasz okna dialogowe, możesz kontrolować jego zachowanie.

Mam nadzieję, że to pomoże.

 5
Author: Mike Rowley,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-03-04 00:28:28

Istnieją dwa dobre sposoby, aby to zrobić, 1) usługa dialogowa (łatwa, czysta) i 2) widok wspomagany. Widok wspomagany zapewnia kilka ciekawych funkcji, ale zazwyczaj nie jest tego wart.

USŁUGA DIALOGOWA

A) interfejs usługi dialogowej jak via constructor lub jakiś kontener zależności:

interface IDialogService { Task ShowDialogAsync(DialogViewModel dlgVm); }

B) twoja implementacja IDialogService powinna otworzyć okno (lub wprowadzić jakąś kontrolkę do aktywnego okna), utworzyć widok odpowiadający nazwie danego typu dlgVm (użyj container registration or convention or a ContentPresenter with type associated Datatematplates). ShowDialogAsync powinien utworzyć TaskCompletionSource i zwrócić jego ./ Align = "left" / Sama klasa DialogViewModel wymaga zdarzenia, które można wywołać w klasie pochodnej, gdy chcesz zamknąć, i oglądać w widoku dialogowym, aby faktycznie zamknąć / ukryć okno dialogowe i zakończyć TaskCompletionSource.

B) aby użyć, po prostu zadzwoń.DialogService.ShowDialog (myDlgVm) na Twojej instancji jakiegoś Klasa pochodna DialogViewModel. Po zakończeniu oczekiwania na zwroty spójrz na właściwości dodane na maszynie wirtualnej dialogowej, aby określić, co się stało; nie potrzebujesz nawet połączenia zwrotnego.

WIDOK WSPOMAGANY

To ma twój widok słuchania zdarzenia na viewmodel. To wszystko może być zawinięte w zachowanie mieszania, aby uniknąć kodu za i wykorzystania zasobów, jeśli jesteś tak skłonny (FMI, podklasa Klasa "zachowanie", aby zobaczyć rodzaj mieszanej właściwości dołączonej na sterydach). Na razie zrobimy to. ręcznie na każdym widoku:

A) Utwórz Openxxxxdialogevent z niestandardowym ładunkiem (Klasa pochodna DialogViewModel).

B) niech widok subskrybuje Zdarzenie w jego zdarzeniu OnDataContextChanged. Pamiętaj, aby ukryć i wypisać się, jeśli Stara wartość != null oraz w zdarzeniu Nieobładowanym okna.

C) gdy zdarzenie zostanie wywołane, Otwórz widok, który może znajdować się w zasobie na twojej stronie lub zlokalizuj go według konwencji w innym miejscu (np. w usłudze dialogowej podejście).

To podejście jest bardziej elastyczne, ale wymaga więcej pracy. Nie używam go zbyt często. Jedną z miłych zalet jest możliwość fizycznego umieszczenia widoku wewnątrz karty, na przykład. Użyłem algorytmu, aby umieścić go w granicach bieżącej kontroli użytkownika, lub jeśli nie jest wystarczająco duży, przemierzaj drzewo wizualne, aż znajdzie się wystarczająco duży kontener.

Dzięki temu okna dialogowe mogą być blisko miejsca, w którym są faktycznie używane, tylko przyciemnia część aplikacji związaną z bieżąca aktywność i pozwól użytkownikowi poruszać się w aplikacji bez konieczności ręcznego wypychania okien dialogowych, nawet mieć wiele quasi-modalnych okien dialogowych otwartych na różnych kartach lub pod-widokach.

 5
Author: Chris Bordeman,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-03-21 09:38:01

Użyj polecenia freezable

<Grid>
        <Grid.DataContext>
            <WpfApplication1:ViewModel />
        </Grid.DataContext>


        <Button Content="Text">
            <Button.Command>
                <WpfApplication1:MessageBoxCommand YesCommand="{Binding MyViewModelCommand}" />
            </Button.Command>
        </Button>

</Grid>
public class MessageBoxCommand : Freezable, ICommand
{
    public static readonly DependencyProperty YesCommandProperty = DependencyProperty.Register(
        "YesCommand",
        typeof (ICommand),
        typeof (MessageBoxCommand),
        new FrameworkPropertyMetadata(null)
        );


    public static readonly DependencyProperty OKCommandProperty = DependencyProperty.Register(
        "OKCommand",
        typeof (ICommand),
        typeof (MessageBoxCommand),
        new FrameworkPropertyMetadata(null)
        );


    public static readonly DependencyProperty CancelCommandProperty = DependencyProperty.Register(
        "CancelCommand",
        typeof (ICommand),
        typeof (MessageBoxCommand),
        new FrameworkPropertyMetadata(null)
        );


    public static readonly DependencyProperty NoCommandProperty = DependencyProperty.Register(
        "NoCommand",
        typeof (ICommand),
        typeof (MessageBoxCommand),
        new FrameworkPropertyMetadata(null)
        );


    public static readonly DependencyProperty MessageProperty = DependencyProperty.Register(
        "Message",
        typeof (string),
        typeof (MessageBoxCommand),
        new FrameworkPropertyMetadata("")
        );

    public static readonly DependencyProperty MessageBoxButtonsProperty = DependencyProperty.Register(
        "MessageBoxButtons",
        typeof(MessageBoxButton),
        typeof(MessageBoxCommand),
        new FrameworkPropertyMetadata(MessageBoxButton.OKCancel)
        );

    public ICommand YesCommand
    {
        get { return (ICommand) GetValue(YesCommandProperty); }
        set { SetValue(YesCommandProperty, value); }
    }

    public ICommand OKCommand
    {
        get { return (ICommand) GetValue(OKCommandProperty); }
        set { SetValue(OKCommandProperty, value); }
    }

    public ICommand CancelCommand
    {
        get { return (ICommand) GetValue(CancelCommandProperty); }
        set { SetValue(CancelCommandProperty, value); }
    }

    public ICommand NoCommand
    {
        get { return (ICommand) GetValue(NoCommandProperty); }
        set { SetValue(NoCommandProperty, value); }
    }

    public MessageBoxButton MessageBoxButtons
    {
        get { return (MessageBoxButton)GetValue(MessageBoxButtonsProperty); }
        set { SetValue(MessageBoxButtonsProperty, value); }
    }

    public string Message
    {
        get { return (string) GetValue(MessageProperty); }
        set { SetValue(MessageProperty, value); }
    }

    public void Execute(object parameter)
    {
        var messageBoxResult = MessageBox.Show(Message);
        switch (messageBoxResult)
        {
            case MessageBoxResult.OK:
                OKCommand.Execute(null);
                break;
            case MessageBoxResult.Yes:
                YesCommand.Execute(null);
                break;
            case MessageBoxResult.No:
                NoCommand.Execute(null);
                break;
            case MessageBoxResult.Cancel:
                if (CancelCommand != null) CancelCommand.Execute(null); //Cancel usually means do nothing ,so can be null
                break;

        }
    }

    public bool CanExecute(object parameter)
    {
        return true;
    }

    public event EventHandler CanExecuteChanged;


    protected override Freezable CreateInstanceCore()
    {
        throw new NotImplementedException();
    }
}
 4
Author: Maxm007,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-11-17 18:25:54

Myślę, że obsługa okna dialogowego powinna być odpowiedzialna za Widok, a Widok musi mieć kod, aby to obsługiwać.

Jeśli zmienisz interakcję ViewModel-View na obsługę okien dialogowych, to 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 w porządku, ale może to być również komunikat o stanie na pasku stanu itp.

Chodzi mi o to, że głównym celem wzorca MVVM jest oddzielenie logiki biznesowej od GUI, więc nie należy mieszać logiki GUI (aby wyświetlić okno dialogowe) w warstwie biznesowej (modelu widoku).

 3
Author: Cameron MacFarland,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-01-18 09:36:34

Ciekawą alternatywą jest użycie kontrolerów odpowiedzialnych za wyświetlanie widoków (okien dialogowych).

Jak to działa pokazuje WPF Application Framework (WAF).

 3
Author: jbe,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-03-21 14:25:17

Dlaczego po prostu nie przywołać zdarzenia w maszynie wirtualnej i nie zapisać go w widoku? Zachowałoby to logikę aplikacji i widok osobno i nadal pozwalałoby na używanie okna potomnego do okien dialogowych.

 3
Author: Eric Grover,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-01-29 18:25:04

Zaimplementowałem zachowanie, które nasłuchuje wiadomości z ViewModel. Jest oparty na rozwiązaniu Laurent Bugnion, ale ponieważ nie używa kodu za i jest bardziej wielokrotnego użytku, myślę, że jest bardziej elegancki.

Jak sprawić, by WPF zachowywał się tak, jakby MVVM był obsługiwany po wyjęciu z pudełka

 3
Author: Elad Katz,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-11-09 23:48:50

Myślę, że Widok może mieć kod do obsługi zdarzenia z modelu widoku.

W zależności od zdarzenia/scenariusza, może mieć również WYZWALACZ zdarzenia, który subskrybuje wyświetlanie zdarzeń modelu, oraz jedną lub więcej akcji do wywołania w odpowiedzi.

 2
Author: Nikhil Kothari,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-04-24 01:39:29

Miałem taką samą sytuację i zamknąłem MessageBox w projektanta niewidzialnego sterowania. 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 okna modalne, sterowanie przeglądaniem plików itp.

 2
Author: mukapu,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-03-11 21:30:48

Zwijałem własną ładowarkę okienną opisaną w odpowiedzi na to pytanie:

Zarządzanie wieloma widokami WPF w aplikacji

 1
Author: Mark Bostleman,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:10:36

Karl Shifflett stworzył przykładową aplikację do wyświetlania okien dialogowych przy użyciu podejścia usługowego i podejścia Prism InteractionRequest.

Podoba mi się podejście do usług - jest mniej elastyczne, więc użytkownicy są mniej skłonni do złamania czegoś :) Jest to również zgodne z częścią WinForms mojej aplikacji (MessageBox.Pokaż) Ale jeśli planujesz pokazać wiele różnych okien dialogowych, InteractionRequest jest lepszym sposobem na idź.

Http://karlshifflett.wordpress.com/2010/11/07/in-the-box-ndash-mvvm-training/

 1
Author: surfen,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-12-12 17:38:41

Wiem, że to stare pytanie, ale kiedy zrobiłem to wyszukiwanie, znalazłem wiele powiązanych pytań, ale nie znalazłem naprawdę jasnej odpowiedzi. Robię więc własną implementację dialogbox / messagebox / popin i dzielę się nią!
Myślę, że jest to "dowód MVVM" i staram się to uprościć i poprawić, ale jestem nowy w WPF, więc nie krępuj się komentować, a nawet złożyć żądanie pull.

Https://github.com/Plasma-Paris/Plasma.WpfUtils

Możesz go użyć tak:

public RelayCommand YesNoMessageBoxCommand { get; private set; }
async void YesNoMessageBox()
{
    var result = await _Service.ShowMessage("This is the content of the message box", "This is the title", System.Windows.MessageBoxButton.YesNo);
    if (result == System.Windows.MessageBoxResult.Yes)
        // [...]
}

Lub w ten sposób, jeśli chcesz bardziej wyrafinowany popin :

var result = await _Service.ShowCustomMessageBox(new MyMessageBoxViewModel { /* What you want */ });

I pokazuje takie rzeczy:

2

 1
Author: Xav987,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-10-19 15:30:28

Po spędzeniu z nim czasu, w końcu wymyśliłem następujące rozwiązanie. Kilka kluczowych zalet tego podejścia to:

  1. implementuje własne MVVM Light IDialogService.
  2. widok nie musi dodawać referencji MVVM Light.
  3. maszyna wirtualna nie musi wykonywać żadnych czynności na poziomie prezentacji. Nie potrzebuje nawet PresentationFramework referencji.
  4. wykorzystuje własny kanał komunikatora MVVM Light, więc warstwy prezentacji i maszyny Wirtualnej są odsprzęgane.
  5. obsługuje okna dialogowe z wartością zwracaną, takie jak Pytania tak/nie lub OK / Anuluj sytuacje.
  6. obsługuje własne okna dialogowe.

Oto implementacja IDialogService (wchodzi do projektu ViewModel ):

using System;
using System.Linq;
using System.Threading.Tasks;

namespace VM
{
  public enum MessageBoxButtonVM
  {
    OK,
    OKCancel,
    YesNo
  }

  public enum MessageBoxImageVM
  {
    None,
    Information,
    Question,
    Error
  }

  public class MessageBoxArgs
  {
    public MessageBoxButtonVM Buttons { get; set; }
    public MessageBoxImageVM Icon { get; set; }
    public string Title { get; set; }
    public string Message { get; set; }
  }

  //For custom dialogs that return a value
  public class MessageBoxNotificationWithAction<T>
  {
    private readonly Action<T> _callback;

    public MessageBoxArgs Notification { get; set; }

    public MessageBoxNotificationWithAction(MessageBoxArgs notification, Action<T> callback)
    {
      Notification = notification;

      CheckCallback(callback);
      _callback = callback;
    }

    public virtual void Execute(T argument)
    {
      _callback.Invoke(argument);
    }

    private static void CheckCallback(Delegate callback)
    {
      if (callback == null)
      {
        throw new ArgumentNullException(nameof(callback), "Callback must not be null");
      }
    }
  }

  /// <summary>
  /// Provides an implementation-agnostic way of communicating with the user through dialog boxes. Clients must register for communication messages using
  /// MVVM Light messaging system.
  /// </summary>
  public class DialogService : GalaSoft.MvvmLight.Views.IDialogService
  {
    private static GalaSoft.MvvmLight.Messaging.IMessenger Messenger = GalaSoft.MvvmLight.Messaging.Messenger.Default;

    private string _ProductName = "";

    public string ProductName
    {
      get
      {
        if (_ProductName == "")
        {
          //The following statement returns the Title attribute of the current assembly, as defined in project properties (Assembly Information dialog).
          var TitleAttrib = System.Reflection.Assembly.GetExecutingAssembly().GetCustomAttributesData().First(x => x.AttributeType.Name == "AssemblyTitleAttribute");

          if (TitleAttrib != null)
          {
            _ProductName = TitleAttrib.ConstructorArguments[0].Value.ToString();
          }
          else
          {
            _ProductName = "Default Application Name";
          }
        }

        return _ProductName;
      }
    }

    public Task ShowError(Exception error, string title, string buttonText, Action afterHideCallback)
    {
      return ShowError(error.Message, title, buttonText, afterHideCallback);
    }

    public Task ShowMessage(string message, string title)
    {
      return Task.Run(() => MessengerSend(message, title, MessageBoxButtonVM.OK, MessageBoxImageVM.Error));
    }

    public Task ShowError(string message, string title, string buttonText, Action afterHideCallback)
    {
      return Task.Run(() =>
      {
        MessengerSend(message, title, MessageBoxButtonVM.OK, MessageBoxImageVM.Error);
        afterHideCallback?.Invoke();
      });
    }

    public Task ShowMessage(string message, string title, string buttonText, Action afterHideCallback)
    {
      return Task.Run(() =>
      {
        MessengerSend(message, title);
        afterHideCallback?.Invoke();
      });
    }

    public Task<bool> ShowMessage(string message, string title, string buttonConfirmText, string buttonCancelText, Action<bool> afterHideCallback)
    {
      if ((buttonConfirmText == "OK" && buttonCancelText == "Cancel") ||
        (buttonConfirmText == "Yes" && buttonCancelText == "No"))
      {
        return Task.Run<bool>(() =>
        {
          MessageBoxButtonVM btn;
          if (buttonConfirmText == "OK")
            btn = MessageBoxButtonVM.OKCancel;
          else
            btn = MessageBoxButtonVM.YesNo;


          bool Response = false;
          Messenger.Send(new MessageBoxNotificationWithAction<bool>(
                                                      new MessageBoxArgs()
                                                      {
                                                        Buttons = btn,
                                                        Icon = MessageBoxImageVM.Question,
                                                        Title = (string.IsNullOrEmpty(title) ? _ProductName : title),
                                                        Message = message
                                                      },
                                                      (result) => Response = result
                                                        ));

          afterHideCallback?.Invoke(Response);

          return Response;
        });
      }
      else
        throw new ArgumentException($"{nameof(buttonConfirmText)} and {nameof(buttonCancelText)} must either be OK/Cancel or Yes/No.");
    }

    /// <summary>
    /// For debugging purpose only
    /// </summary>
    /// <param name="message"></param>
    /// <param name="title"></param>
    /// <returns></returns>
    public Task ShowMessageBox(string message, string title) => ShowMessage(message, title);

    private void MessengerSend(string msg, string title = "", MessageBoxButtonVM btn = MessageBoxButtonVM.OK, MessageBoxImageVM icon = MessageBoxImageVM.Information)
    {
      Messenger.Send(new MessageBoxArgs()
      {
        Buttons = MessageBoxButtonVM.OK,
        Icon = MessageBoxImageVM.Information,
        Title = (string.IsNullOrEmpty(title) ? _ProductName : title),
        Message = msg
      });
    }
  }
}

Oto warstwa prezentacji (wchodzi do widok projekt)

using System.Windows;
using VM;

namespace View
{
  class DialogPresenter
  {
    private Window _Parent;

    public DialogPresenter()
    {
      //For simple information boxes
      GalaSoft.MvvmLight.Messaging.Messenger.Default.Register<MessageBoxArgs>(this, (arg) => ShowDialog(arg));

      //For Yes/No or OK/Cancel dialog boxes.
      GalaSoft.MvvmLight.Messaging.Messenger.Default.Register<MessageBoxNotificationWithAction<bool>>(this, (arg) => arg.Execute(ShowDialog(arg.Notification)));

      //For notifications that require a string response (such as Manual Timeslot Description)
      GalaSoft.MvvmLight.Messaging.Messenger.Default.Register<MessageBoxNotificationWithAction<string>>(this,
        (arg) => arg.Execute(ShowStringInputDialog(arg.Notification.Title, arg.Notification.Message)));
    }

    private bool ShowDialog(MessageBoxArgs arg)
    {
      MessageBoxButton btn = MessageBoxButton.OK;
      MessageBoxImage ico = MessageBoxImage.None;

      switch (arg.Buttons)
      {
        case MessageBoxButtonVM.OK: btn = MessageBoxButton.OK; break;
        case MessageBoxButtonVM.OKCancel: btn = MessageBoxButton.OKCancel; break;
        case MessageBoxButtonVM.YesNo: btn = MessageBoxButton.YesNo; break;
      }

      switch (arg.Icon)
      {
        case MessageBoxImageVM.Error: ico = MessageBoxImage.Error; break;
        case MessageBoxImageVM.Information: ico = MessageBoxImage.Information; break;
        case MessageBoxImageVM.None: ico = MessageBoxImage.None; break;
        case MessageBoxImageVM.Question: ico = MessageBoxImage.Question; break;
      }

      bool Result = false;
      _Parent.Dispatcher.Invoke(() =>
      {
        var Res = MessageBox.Show(arg.Message, arg.Title, btn, ico);
        Result = (Res == MessageBoxResult.OK || Res == MessageBoxResult.Yes);
      });

      return Result;
    }

    private string ShowStringInputDialog(string title, string description, string value = "", int maxLength = 100)
    {
      string Result = null;

      _Parent.Dispatcher.Invoke(() =>
      {
        //InputBox is a WPF Window I created for taking simple
        //string values from the user. This also shows that you can
        //any custom dialog using this approach.

        InputBox input = new InputBox();
        input.Title = title;
        input.Owner = _Parent;
        if (input.ShowDialog(description, value, maxLength).Value)
          Result=input.Value;
        else
          Result=null;
      });

      return Result;
    }

    //Call this somewhere at application startup so that the dialog boxes
    //appear as child windows.
    public void SetParentWindow(Window parent)
    {
      _Parent = parent;
    }
  }
}
 1
Author: dotNET,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-11-08 03:17:27

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 tak:

public class SelectionTaskModel<TChoosable> : ViewModel
    where TChoosable : ViewModel
{
    public SelectionTaskModel(ICollection<TChoosable> choices);
    public ReadOnlyCollection<TChoosable> Choices { get; }
    public void Choose(TChoosable choosen);
    public void Abort();
}

Gdy model widoku zdecyduje, że dane wejściowe użytkownika są wymagane, pobiera instancję SelectionTaskModel z możliwymi wyborami dla użytkownika. Infrastruktura zadba o wywołanie odpowiedniego widoku, który w odpowiednim czasie wywoła funkcję Choose() z wyborem użytkownika.

 0
Author: David Schmitt,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 11:54:50

Zmagałem się z tym samym problemem. Wymyśliłem sposób na interkomunikę między widokiem a ViewModel. Możesz zainicjować wysyłanie wiadomości z modelu widoku do widoku, aby powiedzieć mu, aby pokazał messagebox i zgłosi z powrotem wynik. Następnie model widoku może odpowiedzieć na wynik zwrócony z widoku.

Demonstruję to w Mój blog :

 0
Author: Dan Neely,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-10-20 19:33:36

Napisałem dość obszerny artykuł na ten temat, a także stworzyłem bibliotekę pop-in dla okien dialogowych MVVM. Ścisłe przestrzeganie MVVM jest nie tylko możliwe, ale bardzo czyste, gdy jest poprawnie zaimplementowane, i może być łatwo rozszerzone na biblioteki innych firm, które same się do niego nie stosują:

Https://www.codeproject.com/Articles/820324/Implementing-Dialog-Boxes-in-MVVM

 0
Author: Mark Feldman,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-04-05 11:12:30

Przepraszam, ale muszę zadzwonić. Przejrzałem kilka proponowanych rozwiązań, zanim znalazłem pryzmat.Wpf.Interaktywna przestrzeń nazw w projekcie Prism. Możesz użyć żądań interakcji i akcji wyskakującego okna, aby przewrócić okno niestandardowe lub dla prostszych potrzeb wbudowane są wyskakujące okienka powiadomień i potwierdzeń. Tworzą one prawdziwe okna i są zarządzane jako takie. w oknie dialogowym można przekazać obiekt kontekstowy z dowolnymi zależnościami. Takie rozwiązanie stosujemy w mojej pracy odkąd go znalazłem. Mamy tu wielu starszych programistów i nikt nie wymyślił nic lepszego. Naszym poprzednim rozwiązaniem była usługa dialog w nakładce i użycie klasy presenter, aby to osiągnąć, ale trzeba było mieć fabryki dla wszystkich modeli widoków dialogowych itp.

To nie jest trywialne, ale też nie jest zbyt skomplikowane. I jest wbudowany w Pryzmat i dlatego jest najlepsza (lub lepsza) praktyka IMHO.

Moje 2 centy!

 0
Author: jogi,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-05-07 17:33:49

EDIT: Tak zgadzam się, że nie jest to poprawne podejście do MVVM i używam teraz czegoś podobnego do tego, co sugeruje blindmeis.

Jednym ze sposobów na to jest

W modelu widoku głównego (gdzie otwierasz modal):

void OpenModal()
{
    ModalWindowViewModel mwvm = new ModalWindowViewModel();
    Window mw = new Window();
    mw.content = mwvm;
    mw.ShowDialog()
    if(mw.DialogResult == true)
    { 
        // Your Code, you can access property in mwvm if you need.
    }
}

I w oknie modalnym Widok / ViewModel:

XAML:

<Button Name="okButton" Command="{Binding OkCommand}" CommandParameter="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}}">OK</Button>
<Button Margin="2" VerticalAlignment="Center" Name="cancelButton" IsCancel="True">Cancel</Button>

ViewModel:

public ICommand OkCommand
{
    get
    {
        if (_okCommand == null)
        {
            _okCommand = new ActionCommand<Window>(DoOk, CanDoOk);
        }
        return _okCommand ;
    }
}

void DoOk(Window win)
{
    <!--Your Code-->
    win.DialogResult = true;
    win.Close();
}

bool CanDoOk(Window win) { return true; }

Lub podobne do tego, co jest zamieszczone tutaj WPF MVVM: jak zamknąć okno

 -1
Author: Simone,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 11:47:11