Dialogue sur la gestion dans la FPF avec MVVM

dans le modèle MVVM pour WPF, la manipulation dialogs est l'une des opérations les plus complexes. Comme votre modèle de vue ne sait rien de la vue, la communication de dialogue peut être intéressante. Je peux exposer un ICommand que lorsque la vue l'invoque, un dialogue peut apparaître.

est-ce que quelqu'un connaît une bonne façon de gérer les résultats des dialogues? Je parle de dialogues windows tels que MessageBox.

l'une des façons dont nous avons fait ceci était avoir un événement sur le modèlevue auquel la vue s'abonnerait quand une boîte de dialogue était nécessaire.

public event EventHandler<MyDeleteArgs> RequiresDeleteDialog;

C'est OK, mais cela signifie que la vue nécessite du code, ce qui est quelque chose que je voudrais éviter.

220
demandé sur BoltClock 2009-01-18 12:23:11

23 réponses

je suggère de renoncer aux dialogues modaux des années 1990 et de mettre en place une commande comme superposition (toile+positionnement absolu) avec visibilité liée à un booléen dans la VM. Plus proche d'une commande de type ajax.

C'est très utile:

<BooleanToVisibilityConverter x:Key="booltoVis" />

comme dans:

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

Voici comment j'ai implémenté un contrôle utilisateur. Cliquer sur le "x" ferme la commande dans une ligne de code dans le code de usercontrol derrière. (Puisque j'ai mon point de vue dans an .exe et les Modèles de vue dans une dll, Je ne me sens pas mal à propos du code qui manipule L'UI.)

Wpf dialog

132
répondu Jeff K. 2014-02-27 04:30:38

vous devez utiliser un médiateur pour cela. Mediator est un modèle de conception commune également connu sous le nom de Messager dans certaines de ses implémentations. Il s'agit d'un paradigme de type Register/Notify et permet à votre modèle de vue et vos vues de communiquer à travers un mécanisme de messagerie à faible couplage.

vous devriez consulter le groupe de Disciples de google WPF, et juste la recherche de médiateur. Vous serez bien heureux avec les réponses...

vous pouvez cependant commencez par ceci:

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

Profitez-en !

Edit: vous pouvez voir la réponse à ce problème avec le MVVM Light Toolkit ici:

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

51
répondu Roubachof 2010-06-14 14:49:47

un bon dialogue MVVM devrait:

  1. ne doit être déclaré qu'avec XAML.
  2. récupérez tout son comportement à partir de la base de données.

malheureusement, la WPF ne fournit pas ces caractéristiques. L'affichage d'une boîte de dialogue nécessite un appel code-behind vers ShowDialog(). La classe Window, qui supporte les dialogues, ne peut pas être déclarée dans XAML donc elle ne peut pas facilement être databound vers le DataContext.

pour résoudre ceci, je a écrit un contrôle de butée XAML qui s'assoit dans l'arbre logique et relaie les bases de données à une fenêtre et des poignées montrant et cachant la boîte de dialogue. Vous pouvez le trouver ici: http://www.codeproject.com/KB/WPF/XAMLDialog.aspx

il est vraiment simple d'utiliser et ne nécessite pas de changements étranges à votre modèle de vue et ne nécessite pas d'événements ou de messages. L'appel de base ressemble à ceci:

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

vous voulez probablement ajouter un style qui Indiquant l'. Je l'explique dans mon article. J'espère que cela vous aide.

28
répondu user92541 2011-08-28 17:33:28

j'utilise ce approche pour les dialogues avec MVVM.

Tout ce que je dois faire maintenant est d'appeler le suivant de mon modèle de vue.

var result = this.uiDialogService.ShowDialog("Dialogwindow title goes here", dialogwindowVM);
23
répondu blindmeis 2017-05-23 12:10:36

ma solution actuelle résout la plupart des problèmes que vous avez mentionnés mais elle est complètement abstraite des choses spécifiques à la plate-forme et peut être réutilisée. En outre, je n'ai utilisé aucun code-derrière seulement liant avec les commandes Delegatec qui mettent en œuvre ICommand. Le dialogue est essentiellement une vue-une commande séparée qui a son propre modèle de vue et il est montré à partir du modèle de vue de l'écran principal mais déclenché à partir de L'UI via DelagateCommand binding.

voir la solution complète de Silverlight 4 ici dialogues modaux avec MVVM et Silverlight 4

16
répondu Roboblob 2010-01-21 21:14:17

j'ai vraiment eu du mal avec ce concept pendant un moment quand j'ai appris (encore en apprentissage) MVVM. Ce que j'ai décidé, et ce que je pense que d'autres ont déjà décidé mais qui n'était pas clair pour moi est ceci:

ma pensée originale était qu'un modèle de vue ne devrait pas être autorisé à appeler une boîte de dialogue directement car il n'a pas d'affaires de décider comment une boîte de dialogue devrait apparaître. En raison de cela, j'ai commencé à penser à la façon dont je pouvais passer des messages un peu comme je l'aurais fait dans MVP (i.e. Vue.ShowSaveFileDialog ()). Cependant, je pense que c'est la mauvaise approche.

il est correct pour un ViewModel d'appeler une boîte de dialogue directement. Cependant, lorsque vous testez un ViewModel , cela signifie que la boîte de dialogue apparaîtra pendant votre test, ou échouera tous ensemble (je n'ai jamais vraiment essayé cela).

ainsi, ce qui doit se produire est tout en testing est d'utiliser une version" test " de votre dialogue. Cela signifie que pour toujours dialogue vous avez, vous devez créer une Interface et soit se moquer de la réponse de dialogue ou créer un test mock qui aura un comportement par défaut.

vous devriez déjà utiliser une sorte de localisateur de Service ou de Cio que vous pouvez configurer pour vous fournir la version correcte en fonction du contexte.

en utilisant cette approche, votre modèle de vue est encore testable et selon la façon dont vous vous en êtes débarrassé, vous pouvez contrôler le comportement.

Espérons que cette aide.

5
répondu Mike Rowley 2010-03-04 00:28:28

il y a deux bonnes façons de faire ceci, 1) un service de dialogue (facile, propre), et 2) une vue assistée. Vue assisté fournit certaines fonctionnalités intéressantes, mais est généralement pas la peine.

DIALOG SERVICE

a) une interface de service de dialogue comme via un constructeur ou un conteneur de dépendances:

interface IDialogService { Task ShowDialogAsync(DialogViewModel dlgVm); }

b) votre implémentation D'IDialogService doit ouvrir une fenêtre (ou injecter un certain contrôle dans le active window), créer une vue correspondant au nom du type dlgVm donné (utiliser container registration ou convention ou un ContentPresenter avec type associated Dataemplates). ShowDialogAsync devrait créer une Taskcompletitionsource et retourner its .Tâche proptery. La classe DialogViewModel elle-même a besoin d'un événement que vous pouvez invoquer dans la classe dérivée quand vous voulez fermer, et regarder dans la vue de dialogue pour fermer/Cacher réellement la boîte de dialogue et compléter la Taskcompletitionsource.

b) Pour utiliser, il suffit d'appeler attendre ceci.DialogService.ShowDialog (myDlgVm) sur votre instance d'une classe dérivée de DialogViewModel. Après les retours d'attente, regardez les propriétés que vous avez ajoutées sur votre boîte de dialogue VM pour déterminer ce qui s'est passé; vous n'avez même pas besoin d'un rappel.

VIEW ASSISTED

cela a votre vue en écoutant un événement sur le modèle de vue. Tout cela pourrait être enveloppé dans un comportement de mélange pour éviter le code derrière et l'utilisation des ressources si vous êtes si incliné (FMI, sous-classe de la classe "comportement" pour voir une sorte de biens durables attachés sur les stéroïdes). Pour l'instant, nous le ferons manuellement sur chaque vue:

a) Créer un OpenXXXXXDialogEvent avec une charge utile personnalisée (une classe dérivée de DialogViewModel).

b) avoir la vue s'abonner à l'événement dans son événement OnDataContextChanged. Assurez-vous de cacher et de se désabonner si l'ancienne valeur != null et dans l'événement non chargé de la fenêtre.

c) Lorsque l'événement se déclenche, ayez la vue ouvrir votre vue, qui pourrait être dans une ressource sur votre page, ou vous pourriez La localiser par convention ailleurs (comme dans l'approche du service de dialogue).

cette approche est plus souple, mais nécessite plus de travail à utiliser. Je ne l'utilise pas beaucoup. Le seul avantage de nice est la possibilité de placer la vue physiquement à l'intérieur d'un onglet, par exemple. J'ai utilisé un algorithme pour le placer dans les limites du contrôle de l'utilisateur courant, ou si pas assez grand, parcourez l'arborescence visuelle jusqu'à ce qu'un assez grand conteneur est trouvé.

cela permet aux boîtes de dialogue d'être près de l'endroit où elles sont réellement utilisées, seulement dim la partie de l'application liée à l'activité courante, et permet à l'utilisateur de se déplacer dans l'application sans avoir à repousser manuellement les boîtes de dialogue, même avoir plusieurs boîtes de dialogue quasi-modales ouvertes sur différents onglets ou sous-vues.

5
répondu Chris Bordeman 2015-03-21 09:38:01

utiliser une commande 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
répondu Maxm007 2011-11-17 18:25:54

je pense que le maniement d'un dialogue devrait être la responsabilité de la vue, et la vue doit avoir du code pour supporter cela.

si vous changez l'interaction ViewModel - View pour gérer les dialogues, alors le ViewModel dépend de cette implémentation. La façon la plus simple de traiter ce problème est de rendre la Vue à chargé de l'exécution de la tâche. Si cela signifie afficher un dialogue alors très bien, mais pourrait aussi être un message d'état dans la barre d'état etc.

mon point est que le point entier du modèle MVVM est de séparer la logique des affaires de L'interface graphique, donc vous ne devriez pas mélanger la logique de L'interface graphique (pour afficher un dialogue) dans la couche des affaires (le modèle de vue).

3
répondu Cameron MacFarland 2009-01-18 09:36:34

une alternative intéressante est d'utiliser des contrôleurs qui sont responsables de montrer les vues (dialogues).

comment cela fonctionne est montré par le WPF Application Framework (WAF)" .

3
répondu jbe 2010-03-21 14:25:17

pourquoi ne pas simplement soulever un événement dans la VM et souscrire à l'événement dans la vue? Cela maintiendrait la logique de l'application et la vue séparée et vous permettrait tout de même d'utiliser une fenêtre enfant pour les dialogues.

3
répondu Eric Grover 2011-01-29 18:25:04

j'ai implémenté un comportement qui écoute un Message du ViewModel. Il est basé sur la solution de Laurent Bugnion, mais comme il n'utilise pas de code derrière et est plus réutilisable, je pense qu'il est plus élégant.

Comment faire de WPF se comporter comme si MVVM est pris en charge hors de la boîte

3
répondu Elad Katz 2016-11-09 23:48:50

je pense que la vue pourrait avoir du code pour gérer l'événement à partir du modèle de la vue.

selon l'événement/scénario, il pourrait aussi avoir un déclencheur d'événement qui souscrit à la vue d'événements modèles, et une ou plusieurs actions à invoquer en réponse.

2
répondu Nikhil Kothari 2009-04-24 01:39:29

j'ai eu la même situation et enveloppé le MessageBox dans un concepteur de contrôle invisible. Les détails sont dans mon blog

http://geekswithblogs.net/mukapu/archive/2010/03/12/user-prompts-messagebox-with-mvvm.aspx

la même chose peut être étendue à n'importe quel Dialogue modal, contrôle de navigation de fichier, etc.

2
répondu mukapu 2010-03-11 21:30:48

j'ai roulé mon propre chargeur de fenêtre décrit dans une réponse à cette question:

la Gestion de plusieurs WPF vues dans une application

1
répondu Mark Bostleman 2017-05-23 12:10:36

Karl Shifflett a créé un exemple d'application pour afficher des boîtes de dialogue en utilisant l'approche service et L'approche Prism InteractionRequest.

j'aime l'approche de service - C'est moins souple, afin que les utilisateurs sont moins susceptibles de casser quelque chose :) Il est également compatible avec la partie WinForms de mon application (MessageBox.Montrer) Mais si vous prévoyez de montrer beaucoup de dialogues différents, alors InteractionRequest est une meilleure façon d'aller.

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

1
répondu surfen 2011-12-12 17:38:41

je sais que c'est une vieille question, mais quand j'ai fait cette recherche, j'ai trouver beaucoup de question connexe, mais je n'ai pas trouvé vraiment de réponse claire. Donc je fais ma propre implémentation d'une boîte de dialogue / messagebox / popin, et je la partage!

Je pense que c'est" MVVM proof", et j'essaie de le rendre simple et approprié, mais je suis nouveau à WPF, alors n'hésitez pas à commenter, ou même faire la demande pull.

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

Vous pouvez l'utiliser comme ceci:

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)
        // [...]
}

ou comme ceci si vous voulez plus sophistiqué popin:

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

Et il montre des choses comme ceci :

2

1
répondu Xav987 2016-10-19 15:30:28

après avoir passé du temps avec, j'ai finalement trouvé la solution suivante. Quelques-uns des principaux avantages de cette approche sont:

  1. met en œuvre le IDialogService de MVVM Light .
  2. View n'a pas besoin d'ajouter la référence de MVVM Light.
  3. VM n'a pas besoin de faire d'activité de présentation. Il n'a même pas besoin de la référence PresentationFramework .
  4. utilise le propre canal de messagerie de MVVM Light, de sorte que le les couches de présentation et de VM sont découplées.
  5. supporte les dialogues avec une valeur de retour, comme les questions oui/non ou les situations OK/Cancel.
  6. supporte les dialogues personnalisés.

Voici la mise en œuvre de IDialogService (va dans ViewModel projet):

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
      });
    }
  }
}

Voici la couche de présentation (va dans voir projet)

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
répondu dotNET 2016-11-08 03:17:27

je réfléchissais à un problème similaire en demandant comment le modèle de vue pour une tâche ou un dialogue devrait ressembler à .

ma solution actuelle ressemble à ceci:

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();
}

quand le modèle de vue décide que l'entrée de l'utilisateur est nécessaire, Il tire vers le haut d'une instance de SelectionTaskModel avec les choix possibles pour l'utilisateur. L'infrastructure prend soin d'élever le point de vue correspondant, qui en temps voulu appellera le Fonction Choose() au choix de l'utilisateur.

0
répondu David Schmitt 2017-05-23 11:54:50

je me suis battu avec le même problème. J'ai trouvé un moyen de communiquer entre la vue et le modèle de vue. Vous pouvez lancer l'envoi d'un message depuis le Modèlevue vers la vue pour lui dire de montrer une boîte à messages et il fera un rapport avec le résultat. Puis le ViewModel peut répondre au résultat retourné par la vue.

je le démontre dans mon blog :

0
répondu Dan Neely 2016-10-20 19:33:36

j'ai écrit un article assez complet sur ce sujet et j'ai également développé une bibliothèque pop-in pour les dialogues MVVM. Une adhésion stricte à la MVVM est non seulement possible, mais très propre lorsqu'elle est mise en œuvre correctement, et elle peut être facilement étendue aux bibliothèques tierces qui n'y adhèrent pas elles-mêmes:

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

0
répondu Mark Feldman 2018-04-05 11:12:30

désolé, mais je dois intervenir. J'ai examiné plusieurs des solutions proposées avant de trouver le prisme.Wpf.L'interactivité de l'espace de noms dans le projet Prism. Vous pouvez utiliser les requêtes d'interaction et l'action de fenêtre popup pour lancer une fenêtre personnalisée ou pour des besoins plus simples il y a des popups de Notification et de Confirmation intégrés. Ceux-ci créent de vraies fenêtres et sont gérés comme tels. vous pouvez passer un objet de contexte avec toutes les dépendances dont vous avez besoin dans la boîte de dialogue. Nous utilisons cette solution à mon travail, car je l'ai trouvé. Nous avons de nombreux devs seniors ici et personne n'a rien trouvé de mieux. Notre solution précédente était le service de dialogue dans une superposition et l'utilisation d'une classe de présentateur pour le faire se produire, mais vous avez dû avoir des usines pour tous les modèles de vue de dialogue, etc.

ce n'est pas anodin, mais ce n'est pas non plus super compliqué. Et il est construit à Prism et est donc meilleure (ou meilleure) pratique IMHO.

mes 2 cents!

0
répondu jogi 2018-05-07 17:33:49

EDIT: oui je suis d'accord ce n'est pas une bonne MVVM approche, et je suis maintenant en utilisant quelque chose de semblable à ce qui est suggéré par blindmeis.

Un de la manière vous pourriez à ce qui est

dans votre modèle de vue principale (où vous ouvrez le 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.
    }
}

et dans votre fenêtre modale View/ 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; }

ou similaire à ce qui est affiché ici WPF MVVM: Comment fermer une fenêtre

-1
répondu Simone 2017-05-23 11:47:11