web-dev-qa-db-fra.com

WPF (MVVM): Fermer une vue de Viewmodel?

Quelqu'un a-t-il trouvé un moyen intelligent de fermer une vue dans un modèle de vue utilisant MVVM?

Peut-être y a-t-il un moyen d'utiliser la liaison pour signaler la fermeture de la vue (fenêtre)?

J'apprécierais vraiment toute contribution de quiconque.

Fondamentalement, j'ai un loginView qui est lié à un loginViewModel, dans le viewmodel (en utilisant la liaison sur une commande), je teste pour voir si la connexion a réussi et si c'est le cas, je charge une nouvelle vue (mainview) et attache son contexte de données. .. 

mais j'ai toujours le loginView affiché - donc je dois le signaler pour décharger ..

J'espérais aussi une solution générique parce que je suis sûr que je vais devoir faire ce genre de chose dans d'autres situations.

Des idées?

46
mark smith

Edit: Voir mon article de blog pour une explication plus détaillée.

Lorsque je dois atteindre cet objectif, j'utilise une interface IRequestCloseViewModel que j'ai créée. 

Cette interface ne contient qu'un seul événement: RequestClose. Cet événement est déclenché par ViewModel (qui hérite d'une classe ViewModelBase ET implémente IRequestCloseViewModel) lorsqu'il souhaite fermer sa vue associée.

Dans mon application, toutes les fenêtres héritent d'une classe abstraite ApplicationWindow. Cette classe abstraite est notifiée chaque fois que le DataContext est modifié et que le gestionnaire vérifie si le DataContext prend en charge IRequestCloseViewModel. Si tel est le cas, un gestionnaire d'événements est configuré pour fermer la fenêtre lorsque l'événement est déclenché.

Alternativement, comme l'a dit Kent, vous pouvez utiliser un contrôleur d'écran qui gère ce mécanisme dans une classe externe.

35
japf

Vous ne savez pas quel framework MVVM vous utilisez, mais la plupart contiennent une sorte de solution de messagerie/notification qui permet de facilement enregistrer les messages envoyés. Il n'y a aucune raison que je puisse imaginer que votre vue ne puisse pas s'inscrire pour un message tel que "CloseWindowsBoundTo" et le viewModel en tant qu'expéditeur. Ensuite, à votre avis, vous pouvez simplement vous inscrire à ce message et comparer votre contexte de données actuel à celui de l'expéditeur. S'ils correspondent, fermez la fenêtre.

Simple et conserve votre vue abstraite de votre modèle de vue.

Voici mon approche utilisant la boîte à outils MVVM-light:

Dans le ViewModel:

public void notifyWindowToClose()
{
    Messenger.Default.Send<NotificationMessage>(
        new NotificationMessage(this, "CloseWindowsBoundToMe")
    );
}

Et dans la vue:

Messenger.Default.Register<NotificationMessage>(this, (nm) =>
{
    if (nm.Notification == "CloseWindowsBoundToMe")
    {
        if (nm.Sender == this.DataContext)
            this.Close();
    }
});
22
Ryan from Denver

Je sais que c’est une vieille question, mais j’ai une bonne façon de faire cela, alors j’ai pensé que je partagerais avec tous ceux qui tombent dessus. J'avais l'habitude d'utiliser le comportement attaché de dialogcloser, mais je trouve la solution ci-dessous plus facile à utiliser. L'exemple ci-dessous prend l'exemple d'un bouton de fermeture de la fenêtre pour plus de simplicité.

passer la fenêtre comme paramètre de commande.

dans le bouton xaml pour la vue:

CommandParameter="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=Window}}"

dans la commande execute, méthode dans le modèle de vue:

if (parameter is System.Windows.Window)
{
    (parameter as System.Windows.Window).Close();
}
8
cjmurph

Généralement, vous utiliseriez une sorte de contrôleur/présentateur/service pour piloter l'activation/désactivation de l'écran. MVVM n'est pas destiné à être le Un modèle pour les gouverner tous . Vous devrez le combiner avec d'autres modèles dans toute application non triviale.

Cela dit, dans certaines situations, il est logique d’avoir un modèle de vue qui gère le cycle de vie des modèles de vue d’enfant. Par exemple, vous pouvez avoir une EditorViewModel qui gère une collection de modèles de vue enfant - un pour chaque document en cours de modification. Dans ce cas, le simple ajout/retrait de/à cette collection peut entraîner l'activation/la désactivation de la vue. Mais cela ne semble pas correspondre à votre cas d'utilisation.

8
Kent Boogaart

http://adammills.wordpress.com/2009/07/01/window-close-from-xaml/

<Style.Triggers> <DataTrigger Binding="{Binding CloseSignal}" Value="true"> <Setter Property="Behaviours:WindowCloseBehaviour.Close" Value="true" /> </DataTrigger> </Style>

7
Adam Mills

Vous pouvez créer une commande qui s'attache à la fenêtre et ferme la fenêtre lorsqu'elle est exécutée. Vous pouvez ensuite lier cette commande à une propriété de votre modèle de vue et exécuter la commande lorsque vous souhaitez fermer la fenêtre.

4
user231521

Je voudrais utiliser un ApplicationController qui instancie le LoginViewModel et affiche le LoginView. Lorsque l'utilisateur passe à l'écran de connexion, ApplicationController ferme le LoginView et affiche le MainView avec son MainViewModel.

La façon dont cela peut être fait est montrée dans les exemples d'applications du WPF Application Framework (WAF) project.

2
jbe

Fermez simplement un gestionnaire d’événements dans le code derrière et gérez tout le reste du modèle de vue dans lequel vous pouvez utiliser une liaison de commande.

2
Tarion

Cette réponse montre une autre façon de faire ceci:

Comment le ViewModel doit-il fermer le formulaire?

Il utilise une propriété attachée pour lier la propriété de fenêtre DialogResult à une propriété ViewModel. Lorsque vous définissez la valeur de DialogResult sur true ou false, la vue est fermée.

2
Jorge Vargas

Vous pouvez également faire cela en utilisant event. Bien que vous ayez besoin de 3 lignes de codes dans votre code de vue (certains puristes de MVVM n’aiment pas cela);

Dans votre modèle de vue, vous créez un événement auquel la vue peut s'abonner:

    public event CloseEventHandler Closing;
    public delegate void CloseEventHandler();
    private void RaiseClose()
    {
        if (Closing != null)
            Closing();
    }

Dans votre, visualisez que vous vous abonnez à l'événement après votre méthode initializecomponent comme ci-dessous:

        public View
        {
           *//The event can be put in an interface to avoid direct dependence of the view on the viewmodel. So below becomes
            //ICloseView model = (ICloseView)this.DataContext;*
            ProgressWindowViewModel model = (ProgressWindowViewModel)this.DataContext;
            model.Closing += Model_Closing;
        }
        private void Model_Closing()
        {
             this.Close();
        }

Vous appelez simplement RaiseClose () lorsque vous êtes prêt à fermer la vue à partir du ViewModel.

Vous pouvez même utiliser cette méthode pour envoyer un message à la vue à partir du modèle de vue.

L'événement peut être placé dans une interface pour éviter la dépendance directe de la vue sur le modèle de vue.

1
falopsy

Pour fermer la vue de viewmodel, j’utilisais le toolkit Galasoft MVVM Light que vous pouvez télécharger ici: http://www.mvvmlight.net/

  1. créez une classe comme celle-ci: public class ClosingRequested: MessageBase {}

  2. ajoutez ceci à votre constructeur de vue: Messenger.Default.Register (this, vm, msg => Close ());

  3. appelez ceci pour fermer votre fenêtre: Messenger.Default.Send (new ClosingRequested (), this);

0
Mick3y