WPF MVVM: comment fermer une fenêtre
j'ai un Button
qui ferme ma fenêtre quand il est cliqué:
<Button x:Name="buttonOk" IsCancel="True">Ok</Button>
c'est très bien jusqu'à ce que j'ajoute un Command
au Button
i.e.
<Button x:Name="buttonOk"
Command="{Binding SaveCommand}"
IsCancel="True">Ok</Button>
maintenant il ne ferme pas probablement parce que je m'occupe du Command
. Je peux corriger ceci en mettant un EventHandler
Dans et appelant this.Close()
i.e.
<Button x:Name="buttonOk"
Click="closeWindow"
Command="{Binding SaveCommand}"
IsCancel="True">Ok</Button>
mais maintenant j'ai du code dans mon code derrière la méthode SaveCommand
. J'utilise le modèle MVVM et SaveCommand
est le seul code dans mon code derrière.
Comment faire autrement pour ne pas utiliser de code derrière?
18 réponses
je viens de compléter un blog post sur ce même sujet. En un mot, ajoutez une propriété Action
à votre Visimodel avec des accesseurs get
et set
. Puis définissez le Action
de votre constructeur View
. Enfin, invoquez votre action dans la commande liée qui devrait fermer la fenêtre.
dans le modèle de vue:
public Action CloseAction { get; set;}
et dans le View
constructeur:
private View()
{
InitializeComponent();
ViewModel vm = new ViewModel();
this.DataContext = vm;
if ( vm.CloseAction == null )
vm.CloseAction = new Action(this.Close);
}
enfin, dans n'importe quelle commande liée qui devrait fermer la fenêtre, nous pouvons simplement invoquer
CloseAction(); // Calls Close() method of the View
Cela a fonctionné pour moi, semblait assez élégante solution, et m'a sauvé un tas de codage.
malheureusement, l'affichage de fenêtres est une véritable douleur dans MVVM donc vous devez faire un peu de travail d'infrastructure ou utiliser un cadre MVVM comme Cinch . Si vous voulez investir le temps de le faire vous-même voici un lien de la façon dont Cinch le fait.
c'est bien que vous essayiez de garder toute logique hors de la vue mais ce n'est vraiment pas la fin du monde si vous le faites. Dans ce cas, il ne semble pas que cela causerait trop problème.
comme quelqu'un l'a commenté, le code que j'ai posté n'est pas amical MVVM, qu'en est-il de la deuxième solution?
le 1er, pas MVVM solution (je ne vais pas supprimer ce comme référence)
XAML:
<Button Name="okButton" Command="{Binding OkCommand}" CommandParameter="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}}">OK</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; }
2ème, probablement meilleure solution: À l'aide de joint comportements
XAML
<Button Content="Ok and Close" Command="{Binding OkCommand}" b:CloseOnClickBehaviour.IsEnabled="True" />
View Modèle
public ICommand OkCommand
{
get { return _okCommand; }
}
Classe De Comportement Quelque chose de semblable à ceci:
public static class CloseOnClickBehaviour
{
public static readonly DependencyProperty IsEnabledProperty =
DependencyProperty.RegisterAttached(
"IsEnabled",
typeof(bool),
typeof(CloseOnClickBehaviour),
new PropertyMetadata(false, OnIsEnabledPropertyChanged)
);
public static bool GetIsEnabled(DependencyObject obj)
{
var val = obj.GetValue(IsEnabledProperty);
return (bool)val;
}
public static void SetIsEnabled(DependencyObject obj, bool value)
{
obj.SetValue(IsEnabledProperty, value);
}
static void OnIsEnabledPropertyChanged(DependencyObject dpo, DependencyPropertyChangedEventArgs args)
{
var button = dpo as Button;
if (button == null)
return;
var oldValue = (bool)args.OldValue;
var newValue = (bool)args.NewValue;
if (!oldValue && newValue)
{
button.Click += OnClick;
}
else if (oldValue && !newValue)
{
button.PreviewMouseLeftButtonDown -= OnClick;
}
}
static void OnClick(object sender, RoutedEventArgs e)
{
var button = sender as Button;
if (button == null)
return;
var win = Window.GetWindow(button);
if (win == null)
return;
win.Close();
}
}
j'utiliserais personnellement un comportement pour faire ce genre de chose:
public class WindowCloseBehaviour : Behavior<Window>
{
public static readonly DependencyProperty CommandProperty =
DependencyProperty.Register(
"Command",
typeof(ICommand),
typeof(WindowCloseBehaviour));
public static readonly DependencyProperty CommandParameterProperty =
DependencyProperty.Register(
"CommandParameter",
typeof(object),
typeof(WindowCloseBehaviour));
public static readonly DependencyProperty CloseButtonProperty =
DependencyProperty.Register(
"CloseButton",
typeof(Button),
typeof(WindowCloseBehaviour),
new FrameworkPropertyMetadata(null, OnButtonChanged));
public ICommand Command
{
get { return (ICommand)GetValue(CommandProperty); }
set { SetValue(CommandProperty, value); }
}
public object CommandParameter
{
get { return GetValue(CommandParameterProperty); }
set { SetValue(CommandParameterProperty, value); }
}
public Button CloseButton
{
get { return (Button)GetValue(CloseButtonProperty); }
set { SetValue(CloseButtonProperty, value); }
}
private static void OnButtonChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var window = (Window)((WindowCloseBehaviour)d).AssociatedObject;
((Button) e.NewValue).Click +=
(s, e1) =>
{
var command = ((WindowCloseBehaviour)d).Command;
var commandParameter = ((WindowCloseBehaviour)d).CommandParameter;
if (command != null)
{
command.Execute(commandParameter);
}
window.Close();
};
}
}
vous pouvez alors joindre ceci à votre Window
et Button
pour faire le travail:
<Window x:Class="WpfApplication6.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
xmlns:local="clr-namespace:WpfApplication6"
Title="Window1" Height="300" Width="300">
<i:Interaction.Behaviors>
<local:WindowCloseBehaviour CloseButton="{Binding ElementName=closeButton}"/>
</i:Interaction.Behaviors>
<Grid>
<Button Name="closeButton">Close</Button>
</Grid>
</Window>
j'ai ajouté Command
et CommandParameter
ici pour que vous puissiez exécuter une commande avant la fermeture du Window
.
pour les petites applications, j'utilise mon propre contrôleur D'Application pour afficher, fermer et éliminer les fenêtres et les textes de données. C'est un point central dans L'UI d'une demande.
c'est quelque chose comme ça:
//It is singleton, I will just post 2 methods and their invocations
public void ShowNewWindow(Window window, object dataContext = null, bool dialog = true)
{
window.DataContext = dataContext;
addToWindowRegistry(dataContext, window);
if (dialog)
window.ShowDialog();
else
window.Show();
}
public void CloseWindow(object dataContextSender)
{
var correspondingWindows = windowRegistry.Where(c => c.DataContext.Equals(dataContextSender)).ToList();
foreach (var pair in correspondingWindows)
{
pair.Window.Close();
}
}
et leurs invocations de ViewModels :
// Show new Window with DataContext
ApplicationController.Instance.ShowNewWindow(
new ClientCardsWindow(),
new ClientCardsVM(),
false);
// Close Current Window from viewModel
ApplicationController.Instance.CloseWindow(this);
bien sûr, vous pouvez trouver quelques restrictions dans ma solution. Encore une fois: je l'utilise pour de petits projets, et c'est suffisant. Si vous êtes intéressé, je peux poster le code complet ici ou ailleurs/
j'utilise le publier le patron D'Abonnement pour les dépendances de classe compliquées:
ViewModel:
public class ViewModel : ViewModelBase
{
public ViewModel()
{
CloseComand = new DelegateCommand((obj) =>
{
MessageBus.Instance.Publish(Messages.REQUEST_DEPLOYMENT_SETTINGS_CLOSED, null);
});
}
}
fenêtre:
public partial class SomeWindow : Window
{
Subscription _subscription = new Subscription();
public SomeWindow()
{
InitializeComponent();
_subscription.Subscribe(Messages.REQUEST_DEPLOYMENT_SETTINGS_CLOSED, obj =>
{
this.Close();
});
}
}
Vous pouvez tirer parti de la Bizmonger.Motifs pour obtenir le MessageBus.
MessageBus
public class MessageBus
{
#region Singleton
static MessageBus _messageBus = null;
private MessageBus() { }
public static MessageBus Instance
{
get
{
if (_messageBus == null)
{
_messageBus = new MessageBus();
}
return _messageBus;
}
}
#endregion
#region Members
List<Observer> _observers = new List<Observer>();
List<Observer> _oneTimeObservers = new List<Observer>();
List<Observer> _waitingSubscribers = new List<Observer>();
List<Observer> _waitingUnsubscribers = new List<Observer>();
int _publishingCount = 0;
#endregion
public void Subscribe(string message, Action<object> response)
{
Subscribe(message, response, _observers);
}
public void SubscribeFirstPublication(string message, Action<object> response)
{
Subscribe(message, response, _oneTimeObservers);
}
public int Unsubscribe(string message, Action<object> response)
{
var observers = new List<Observer>(_observers.Where(o => o.Respond == response).ToList());
observers.AddRange(_waitingSubscribers.Where(o => o.Respond == response));
observers.AddRange(_oneTimeObservers.Where(o => o.Respond == response));
if (_publishingCount == 0)
{
observers.ForEach(o => _observers.Remove(o));
}
else
{
_waitingUnsubscribers.AddRange(observers);
}
return observers.Count;
}
public int Unsubscribe(string subscription)
{
var observers = new List<Observer>(_observers.Where(o => o.Subscription == subscription).ToList());
observers.AddRange(_waitingSubscribers.Where(o => o.Subscription == subscription));
observers.AddRange(_oneTimeObservers.Where(o => o.Subscription == subscription));
if (_publishingCount == 0)
{
observers.ForEach(o => _observers.Remove(o));
}
else
{
_waitingUnsubscribers.AddRange(observers);
}
return observers.Count;
}
public void Publish(string message, object payload)
{
_publishingCount++;
Publish(_observers, message, payload);
Publish(_oneTimeObservers, message, payload);
Publish(_waitingSubscribers, message, payload);
_oneTimeObservers.RemoveAll(o => o.Subscription == message);
_waitingUnsubscribers.Clear();
_publishingCount--;
}
private void Publish(List<Observer> observers, string message, object payload)
{
Debug.Assert(_publishingCount >= 0);
var subscribers = observers.Where(o => o.Subscription.ToLower() == message.ToLower());
foreach (var subscriber in subscribers)
{
subscriber.Respond(payload);
}
}
public IEnumerable<Observer> GetObservers(string subscription)
{
var observers = new List<Observer>(_observers.Where(o => o.Subscription == subscription));
return observers;
}
public void Clear()
{
_observers.Clear();
_oneTimeObservers.Clear();
}
#region Helpers
private void Subscribe(string message, Action<object> response, List<Observer> observers)
{
Debug.Assert(_publishingCount >= 0);
var observer = new Observer() { Subscription = message, Respond = response };
if (_publishingCount == 0)
{
observers.Add(observer);
}
else
{
_waitingSubscribers.Add(observer);
}
}
#endregion
}
}
Abonnement
public class Subscription
{
#region Members
List<Observer> _observerList = new List<Observer>();
#endregion
public void Unsubscribe(string subscription)
{
var observers = _observerList.Where(o => o.Subscription == subscription);
foreach (var observer in observers)
{
MessageBus.Instance.Unsubscribe(observer.Subscription, observer.Respond);
}
_observerList.Where(o => o.Subscription == subscription).ToList().ForEach(o => _observerList.Remove(o));
}
public void Subscribe(string subscription, Action<object> response)
{
MessageBus.Instance.Subscribe(subscription, response);
_observerList.Add(new Observer() { Subscription = subscription, Respond = response });
}
public void SubscribeFirstPublication(string subscription, Action<object> response)
{
MessageBus.Instance.SubscribeFirstPublication(subscription, response);
}
}
j'ai essayé de résoudre ce problème d'une manière générique, MVVM, mais je trouve toujours que je finis par une logique complexe inutile. Pour parvenir à un comportement proche, j'ai fait une exception à la règle du "Pas de code derrière" et j'ai utilisé simplement les bons vieux événements dans le code derrière:
XAML:
<Button Content="Close" Click="OnCloseClicked" />
Code derrière:
private void OnCloseClicked(object sender, EventArgs e)
{
Visibility = Visibility.Collapsed;
}
bien que je souhaite que ce soit mieux supporté en utilisant commandes / MVVM, je pense simplement que il n'y a pas de solution plus simple et plus claire que d'utiliser des événements.
Très propre et MVVM façon est d'utiliser des InteractionTrigger
et CallMethodAction
au sens de Microsoft.Interactivity.Core
vous devrez ajouter deux espaces de noms comme ci-dessous
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
xmlns:ei="http://schemas.microsoft.com/expression/2010/interactions"
Et Ensembles Système.Windows.Interactivité et Microsoft.Expression.Les Interactions et ensuite en dessous du code xaml fonctionneront.
<Button Content="Save" Command="{Binding SaveCommand}">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Click">
<ei:CallMethodAction MethodName="Close"
TargetObject="{Binding RelativeSource={RelativeSource
Mode=FindAncestor,
AncestorType=Window}}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</Button>
vous n'avez pas besoin de code ou autre chose et pouvez également appelez toute autre méthode de Window
.
il y a un comportement utile pour cette tâche qui ne casse pas MVVM, un comportement, introduit avec Expression Blend 3, pour permettre à la vue de se raccorder à des commandes définies complètement dans le ViewModel.
ce comportement démontre une technique simple pour permettre à la ViewModel pour gérer les événements de fermeture de la vue dans un Modèle-Vue-Vuemodèle application.
cela vous permet de raccorder un comportement dans votre vue (UserControl) qui fournira le contrôle sur la fenêtre de la commande, permettant le modèle de vue pour contrôler si la fenêtre peut être fermée via ICommands standard.
http://gallery.expression.microsoft.com/WindowCloseBehavior /
le lien ci-dessus a été archivé http://code.msdn.microsoft.com/Window-Close-Attached-fef26a66#content
j'ai lutté avec ce sujet pendant un certain temps, et finalement est allé avec l'approche la plus simple qui est encore compatible avec MVVM: avoir le bouton Exécuter la commande qui fait tout le levage lourd et avoir le Gestionnaire de clic du bouton fermer la fenêtre.
XAML
<Button x:Name="buttonOk"
Click="closeWindow"
Command="{Binding SaveCommand}" />
XAML.cs
public void closeWindow()
{
this.DialogResult = true;
}
SaveCommand.cs
// I'm in my own file, not the code-behind!
il est vrai qu'il y a encore du code derrière, mais il n'y a rien de mal à cela. Et c'est le plus logique pour moi, d'un point de vue OO, de juste dire à la fenêtre de se fermer.
Nous avons le nom de la propriété dans le .XAML définition:
x:Name="WindowsForm"
puis nous avons le bouton:
<Button Command="{Binding CloseCommand}"
CommandParameter="{Binding ElementName=WindowsForm}" />
puis dans le modèle de vue:
public DelegateCommand <Object> CloseCommand { get; private set; }
Constructor for that view model:
this.CloseCommand = new DelegateCommand<object>(this.CloseAction);
puis enfin, la méthode d'action:
private void CloseAction (object obj)
{
Window Win = obj as Window;
Win.Close();
}
j'ai utilisé ce code pour fermer une fenêtre pop-up d'une application..
j'ai été à la recherche d'une solution au même problème et j'ai trouvé que faire des travaux suivants très bien. La solution est similaire à ce que OP a mentionné dans sa question avec quelques différences:
-
Pas besoin de "151930920 de la propriété".
-
le Code derrière ne doit pas fermer la fenêtre. Juste mettre
DialogResult
dans mon cas, il exécute d'abord le code derrière et ensuite voir la commande model liée au bouton.
XAML
<Button x:Name="buttonOk" Click="Save_Click" Command="{Binding SaveCommand}">OK</Button>
Code Derrière
private void Apply_OnClick(object sender, RoutedEventArgs e)
{
this.DialogResult = true;
}
Modèle De Vue
private void Save()
{
// Save data.
}
Espérons que cette aide.
vous pourriez reformuler la question, et en le faisant - trouver une autre solution. Comment puis-je activer la communication entre vues, modèles de vues et autres dans un environnement MVVM? Vous pouvez utiliser le modèle de médiateur. Il s'agit essentiellement d'un système de notification. Pour la mise en œuvre effective de Mediator, googlez-le ou demandez-le moi et je peux l'envoyer par e-mail.
Faire une Commande dont le but est de fermer la vue.
public void Execute( object parameter )
{
this.viewModel.DisposeMyStuff();
Mediator.NotifyColleagues(Mediator.Token.ConfigWindowShouldClose);
}
le Médiateur soulèvera un notification (un jeton)
écouter cette notification (token) comme ceci dans la vue codebehind constructor:
public ClientConfigView()
{
InitializeComponent();
Mediator.ListenOn(Mediator.Token.ConfigWindowShouldClose, callback => this.Close() );
}
j'ai la solution suivante à Silverlight. Serait également dans WPF.
ChildWindowExt.cs:
namespace System.Windows.Controls
{
public class ChildWindowExt : ChildWindow
{
public static readonly DependencyProperty IsOpenedProperty =
DependencyProperty.Register(
"IsOpened",
typeof(bool),
typeof(ChildWindowExt),
new PropertyMetadata(false, IsOpenedChanged));
private static void IsOpenedChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if ((bool)e.NewValue == false)
{
ChildWindowExt window = d as ChildWindowExt;
window.Close();
}
else if ((bool)e.NewValue == true)
{
ChildWindowExt window = d as ChildWindowExt;
window.Show();
}
}
public bool IsOpened
{
get { return (bool)GetValue(IsOpenedProperty); }
set { SetValue(IsOpenedProperty, value); }
}
protected override void OnClosing(ComponentModel.CancelEventArgs e)
{
this.IsOpened = false;
base.OnClosing(e);
}
protected override void OnOpened()
{
this.IsOpened = true;
base.OnOpened();
}
}
}
ItemWindow.xaml:
<extControls:ChildWindowExt
x:Class="MyProject.ItemWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:extControls="clr-namespace:System.Windows.Controls"
Title="{Binding Title}" IsOpened="{Binding IsOpened, Mode=TwoWay}" Width="640" Height="480">
<Grid x:Name="LayoutRoot">
<Button Command="{Binding UpdateCommand}" Content="OK" Width="70" HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Grid>
</extControls:ChildWindowExt>
ItemViewModel.cs:
private bool _IsOpened;
public bool IsOpened
{
get
{
return _IsOpened;
}
set
{
if (!Equals(_IsOpened, value))
{
_IsOpened = value;
RaisePropertyChanged("IsOpened");
}
}
}
private RelayCommand _UpdateCommand;
/// <summary>
/// Insert / Update data entity
/// </summary>
public RelayCommand UpdateCommand
{
get
{
if (_UpdateCommand == null)
{
_UpdateCommand = new RelayCommand(
() =>
{
// Insert / Update data entity
...
IsOpened = false;
},
() =>
{
return true;
});
}
return _UpdateCommand;
}
}
ItemsViewModel.cs:
private RelayCommand _InsertItemCommand;
/// <summary>
///
/// </summary>
public RelayCommand InsertItemCommand
{
get
{
if (_InsertItemCommand == null)
{
_InsertItemCommand = new RelayCommand(
() =>
{
ItemWindow itemWin = new ItemWindow();
itemWin.DataContext = new ItemViewModel();
itemWin.Show();
// OR
// ItemWindow itemWin = new ItemWindow();
// ItemViewModel newItem = new ItemViewModel();
// itemWin.DataContext = newItem;
// newItem.IsOpened = true;
},
() =>
{
return true;
});
}
return _InsertItemCommand;
}
}
de la page d'accueil.xaml:
<Grid x:Name="LayoutRoot">
<Button Command="{Binding InsertItemCommand}" Content="Add New" Width="70" HorizontalAlignment="Left" VerticalAlignment="Center" />
</Grid>
je vous souhaite à tous de bonnes idées et de projets ;-)
cela pourrait vous aider, fermer une fenêtre wpf en utilisant mvvm avec un code minimal derrière: http://jkshay.com/closing-a-wpf-window-using-mvvm-and-minimal-code-behind/
je pense que le moyen le plus simple n'a pas été inclus déjà (presque). Au lieu d'utiliser des comportements qui ajoutent de nouvelles dépendances, il suffit d'utiliser les propriétés attachées:
using System;
using System.Windows;
using System.Windows.Controls;
public class DialogButtonManager
{
public static readonly DependencyProperty IsAcceptButtonProperty = DependencyProperty.RegisterAttached("IsAcceptButton", typeof(bool), typeof(DialogButtonManager), new FrameworkPropertyMetadata(OnIsAcceptButtonPropertyChanged));
public static readonly DependencyProperty IsCancelButtonProperty = DependencyProperty.RegisterAttached("IsCancelButton", typeof(bool), typeof(DialogButtonManager), new FrameworkPropertyMetadata(OnIsCancelButtonPropertyChanged));
public static void SetIsAcceptButton(UIElement element, bool value)
{
element.SetValue(IsAcceptButtonProperty, value);
}
public static bool GetIsAcceptButton(UIElement element)
{
return (bool)element.GetValue(IsAcceptButtonProperty);
}
public static void SetIsCancelButton(UIElement element, bool value)
{
element.SetValue(IsCancelButtonProperty, value);
}
public static bool GetIsCancelButton(UIElement element)
{
return (bool)element.GetValue(IsCancelButtonProperty);
}
private static void OnIsAcceptButtonPropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
Button button = sender as Button;
if (button != null)
{
if ((bool)e.NewValue)
{
SetAcceptButton(button);
}
else
{
ResetAcceptButton(button);
}
}
}
private static void OnIsCancelButtonPropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
Button button = sender as Button;
if (button != null)
{
if ((bool)e.NewValue)
{
SetCancelButton(button);
}
else
{
ResetCancelButton(button);
}
}
}
private static void SetAcceptButton(Button button)
{
Window window = Window.GetWindow(button);
button.Command = new RelayCommand(new Action<object>(ExecuteAccept));
button.CommandParameter = window;
}
private static void ResetAcceptButton(Button button)
{
button.Command = null;
button.CommandParameter = null;
}
private static void ExecuteAccept(object buttonWindow)
{
Window window = (Window)buttonWindow;
window.DialogResult = true;
}
private static void SetCancelButton(Button button)
{
Window window = Window.GetWindow(button);
button.Command = new RelayCommand(new Action<object>(ExecuteCancel));
button.CommandParameter = window;
}
private static void ResetCancelButton(Button button)
{
button.Command = null;
button.CommandParameter = null;
}
private static void ExecuteCancel(object buttonWindow)
{
Window window = (Window)buttonWindow;
window.DialogResult = false;
}
}
ensuite, il suffit de le régler sur vos boutons de dialogue:
<UniformGrid Grid.Row="2" Grid.Column="1" Rows="1" Columns="2" Margin="3" >
<Button Content="Accept" IsDefault="True" Padding="3" Margin="3,0,3,0" DialogButtonManager.IsAcceptButton="True" />
<Button Content="Cancel" IsCancel="True" Padding="3" Margin="3,0,3,0" DialogButtonManager.IsCancelButton="True" />
</UniformGrid>
Vous pouvez le faire sans code derrière. Créer une commande, dans la méthode exécuter, appeler la méthode" Save "sur viewmodel et, après cet appel, fermer la méthode sur edit window, que vous pouvez passer à la commande par paramètre:
public void Execute(object parameter)
{
_mainViewModel.SaveSomething();
var editWindow = parameter as MyEditWindow;
editWindow?.Close();
}
Enregistrer&Fermer bouton XAML:
<Button Content"Save&Close" Command="{Binding SaveCmd}" CommandParameter="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=Window}}" IsDefault="True" />
j'ai aussi eu à faire face à ce problème, donc voici ma solution. Il fonctionne très bien pour moi.
1. Créer la classe DelegateCommand
public class DelegateCommand<T> : ICommand
{
private Predicate<T> _canExecuteMethod;
private readonly Action<T> _executeMethod;
public event EventHandler CanExecuteChanged;
public DelegateCommand(Action<T> executeMethod) : this(executeMethod, null)
{
}
public DelegateCommand(Action<T> executeMethod, Predicate<T> canExecuteMethod)
{
this._canExecuteMethod = canExecuteMethod;
this._executeMethod = executeMethod ?? throw new ArgumentNullException(nameof(executeMethod), "Command is not specified.");
}
public void RaiseCanExecuteChanged()
{
if (this.CanExecuteChanged != null)
CanExecuteChanged(this, null);
}
public bool CanExecute(object parameter)
{
return _canExecuteMethod == null || _canExecuteMethod((T)parameter) == true;
}
public void Execute(object parameter)
{
_executeMethod((T)parameter);
}
}
2. Définissez votre commande
public DelegateCommand<Window> CloseWindowCommand { get; private set; }
public MyViewModel()//ctor of your viewmodel
{
//do something
CloseWindowCommand = new DelegateCommand<Window>(CloseWindow);
}
public void CloseWindow(Window win) // this method is also in your viewmodel
{
//do something
win?.Close();
}
3. Lier votre commande dans le point de vue
public MyView(Window win) //ctor of your view, window as parameter
{
InitializeComponent();
MyButton.CommandParameter = win;
MyButton.Command = ((MyViewModel)this.DataContext).CloseWindowCommand;
}
4. Et maintenant la fenêtre
Window win = new Window()
{
Title = "My Window",
Height = 800,
Width = 800,
WindowStartupLocation = WindowStartupLocation.CenterScreen,
};
win.Content = new MyView(win);
win.ShowDialog();
alors voilà, vous pouvez aussi lier la commande dans le fichier xaml et trouver la fenêtre avec FindAncestor et la lier au paramètre commande.