Donc, ma première tentative a tout fait hors du code derrière, et maintenant j'essaye de refactoriser mon code pour utiliser le modèle MVVM, en suivant les indications du MVVM dans la boîte information.
J'ai créé une classe viewmodel pour correspondre à ma classe, et je déplace le code du code précédent vers le modèle viewmodel en commençant par les commandes.
Mon premier problème est d'implémenter un bouton 'Fermer' qui ferme la fenêtre si les données n'ont pas été modifiées. J'ai configuré une CloseCommand pour remplacer la méthode 'onClick' et tout va bien, sauf pour les cas où le code tente de s'exécuter this.Close()
. De toute évidence, étant donné que le code a été déplacé d'une fenêtre à une classe normale, "ceci" n'est pas une fenêtre et ne peut donc pas être fermé. Cependant, selon MVVM, le modèle de vue ne connaît pas la vue. Je ne peux donc pas appeler view.Close()
.
Quelqu'un peut-il suggérer comment je peux fermer la fenêtre à partir de la commande viewmodel?
Vous n'avez pas besoin de transmettre l'instance View à votre couche ViewModel. Vous pouvez accéder à la fenêtre principale comme ceci -
Application.Current.MainWindow.Close()
Je ne vois aucun problème à accéder à votre fenêtre principale dans la classe ViewModel, comme indiqué ci-dessus. Conformément au principe MVVM, il ne devrait pas exister de lien étroit entre votre View et ViewModel, c’est-à-dire qu’ils devraient fonctionner sans se soucier des autres opérations. Ici, nous ne transmettons rien à ViewModel de View. Si vous souhaitez rechercher d'autres options, cela pourrait vous aider - Fermer la fenêtre avec MVVM
J'utilise personnellement une approche très simple: pour chaque ViewModel associé à une View pouvant être fermée, j'ai créé une ViewModel de base comme dans l'exemple suivant:
public abstract class CloseableViewModel
{
public event EventHandler ClosingRequest;
protected void OnClosingRequest()
{
if (this.ClosingRequest != null)
{
this.ClosingRequest(this, EventArgs.Empty);
}
}
}
Ensuite, dans votre modèle de vue héritant de CloseableViewModel
, appelez simplement this.OnClosingRequest();
pour la commande Close
.
Dans la vue:
public class YourView
{
...
var vm = new ClosableViewModel();
this.Datacontext = vm;
vm.ClosingRequest += (sender, e) => this.Close();
}
Ma solution pour fermer une fenêtre du modèle de vue en cliquant sur un bouton est la suivante:
Dans le modèle
public RelayCommand CloseWindow;
Constructor()
{
CloseWindow = new RelayCommand(CloseWin);
}
public void CloseWin(object obj)
{
Window win = obj as Window;
win.Close();
}
Dans View, définissez comme suit
<Button Command="{Binding CloseWindowCommand}" CommandParameter="{Binding ElementName=WindowNameTobeClose}" Content="Cancel" />
Je le fais en créant une propriété attachée appelée DialogResult:
public static class DialogCloser
{
public static readonly DependencyProperty DialogResultProperty =
DependencyProperty.RegisterAttached(
"DialogResult",
typeof(bool?),
typeof(DialogCloser),
new PropertyMetadata(DialogResultChanged));
private static void DialogResultChanged(
DependencyObject d,
DependencyPropertyChangedEventArgs e)
{
var window = d as Window;
if (window != null && (bool?)e.NewValue == true)
window.Close();
}
public static void SetDialogResult(Window target, bool? value)
{
target.SetValue(DialogResultProperty, value);
}
}
puis écrivez ceci à vous XAML, dans la balise window
WindowActions:DialogCloser.DialogResult="{Binding Close}"
enfin dans le ViewModel
private bool _close;
public bool Close
{
get { return _close; }
set
{
if (_close == value)
return;
_close = value;
NotifyPropertyChanged("Close");
}
}
si vous modifiez le fermer à vrai, la fenêtre sera fermée
Close = True;
Méfiez-vous des paradigmes à la mode. MVVM peut être utile, mais vous ne devriez vraiment pas le traiter comme un ensemble rigide de règles. Utilisez votre propre jugement, et quand cela n’a aucun sens, ne l’utilisez pas.
Les solutions fournies ici (à l'exception de la solution de @ RV1987) sont un très bon exemple de choses qui sortent des mains. Vous remplacez un seul appel Close()
par une telle quantité de code, dans quel but? Vous ne gagnez absolument rien en déplaçant le code de fermeture de la vue vers la vue-modèle. La seule chose que vous gagnez est la place pour plus de bugs.
Maintenant, je ne dis pas que MVVM doit être ignoré. Bien au contraire, cela peut être très utile. Ne fais pas trop.
Voici la solution la plus simple et la solution pure MVVM
Code ViewModel
public class ViewModel
{
public Action CloseAction { get; set; }
private void CloseCommandFunction()
{
CloseAction();
}
}
Voici le code de vue XAML
public partial class DialogWindow : Window
{
public DialogWindow()
{
ViewModel vm = new ViewModel();
this.DataContext = vm;
vm.CloseAction = new Action(() => this.Close());
}
}
Cette solution est rapide et facile. L'inconvénient est qu'il y a un certain couplage entre les couches.
Dans votre vue modèle:
public class MyWindowViewModel: ViewModelBase
{
public Command.StandardCommand CloseCommand
{
get
{
return new Command.StandardCommand(Close);
}
}
public void Close()
{
foreach (System.Windows.Window window in System.Windows.Application.Current.Windows)
{
if (window.DataContext == this)
{
window.Close();
}
}
}
}
MVVM-light avec une notification de message personnalisée pour éviter la fenêtre permettant de traiter chaque message de notification
Dans le modèle de vue:
public class CloseDialogMessage : NotificationMessage
{
public CloseDialogMessage(object sender) : base(sender, "") { }
}
private void OnClose()
{
Messenger.Default.Send(new CloseDialogMessage(this));
}
Enregistrez le message dans le constructeur de fenêtre:
Messenger.Default.Register<CloseDialogMessage>(this, nm =>
{
Close();
});
Ceci est très similaire à la réponse de eoldre. Son fonctionnement est identique en ce sens qu'il recherche dans la même collection Windows une fenêtre dont le modèle de vue est son contexte de données; mais j'ai utilisé un RelayCommand et un peu de LINQ pour obtenir le même résultat.
public RelayCommand CloseCommand
{
get
{
return new RelayCommand(() => Application.Current.Windows
.Cast<Window>()
.Single(w => w.DataContext == this)
.Close());
}
}
en utilisant MVVM-light toolkit:
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();
}
});
tout d'abord donner à votre fenêtre un nom comme
x:Name="AboutViewWindow"
sur mon bouton de fermeture, j'ai défini Command and Command Parameter comme
CommandParameter="{Binding ElementName=AboutViewWindow}"
Command="{Binding CancelCommand}"
alors à mon avis modèle
private ICommand _cancelCommand;
public ICommand CancelCommand
{
get
{
if (_cancelCommand == null)
{
_cancelCommand = new DelegateCommand<Window>(
x =>
{
x?.Close();
});
}
return _cancelCommand;
}
}
Ceci est tiré de la réponse de ken2k (merci!), En ajoutant simplement CloseCommand
également à la base CloseableViewModel
.
public class CloseableViewModel
{
public CloseableViewModel()
{
CloseCommand = new RelayCommand(this.OnClosingRequest);
}
public event EventHandler ClosingRequest;
protected void OnClosingRequest()
{
if (this.ClosingRequest != null)
{
this.ClosingRequest(this, EventArgs.Empty);
}
}
public RelayCommand CloseCommand
{
get;
private set;
}
}
Votre modèle de vue en hérite
public class MyViewModel : CloseableViewModel
Puis sur toi voir
public MyView()
{
var viewModel = new StudyDataStructureViewModel(studyId);
this.DataContext = viewModel;
//InitializeComponent(); ...
viewModel.ClosingRequest += (sender, e) => this.Close();
}
Compte tenu d'un chemin, s'il vous plaît vérifier
https://stackoverflow.com/a/30546407/3659387
Brève description