c# - WPF MVVM: Cómo cerrar una ventana
.net xaml (18)
Acabo de completar una publicación de blog sobre este mismo tema. En pocas palabras, agregue una propiedad de Action
a su ViewModel con los accesadores get
y set
. Luego defina la Action
de su constructor de View
. Finalmente, invoque su acción en el comando vinculado que debe cerrar la ventana.
En ViewModel:
public Action CloseAction { get; set;}
y en el constructor de View
:
private View()
{
InitializeComponent();
ViewModel vm = new ViewModel();
this.DataContext = vm;
if ( vm.CloseAction == null )
vm.CloseAction = new Action(this.Close);
}
Finalmente, en cualquier comando vinculado que cierre la ventana, podemos simplemente invocar
CloseAction(); // Calls Close() method of the View
Esto funcionó para mí, parecía una solución bastante elegante, y me ahorró un montón de codificación.
Tengo un Button
que cierra mi ventana cuando se hace clic:
<Button x:Name="buttonOk" IsCancel="True">Ok</Button>
Eso está bien hasta que agregue un Command
al Button
es decir,
<Button x:Name="buttonOk"
Command="{Binding SaveCommand}"
IsCancel="True">Ok</Button>
Ahora no se cierra presumiblemente porque estoy manejando el Command
. Puedo solucionar esto poniendo un EventHandler
y llamando a this.Close()
ie
<Button x:Name="buttonOk"
Click="closeWindow"
Command="{Binding SaveCommand}"
IsCancel="True">Ok</Button>
pero ahora tengo código en mi código detrás, es decir, el método SaveCommand
. Estoy usando el patrón MVVM y SaveCommand
es el único código en mi código.
¿Cómo puedo hacer esto de manera diferente para no usar código detrás?
Como alguien comentó, el código que publiqué no es compatible con MVVM, ¿qué hay de la segunda solución?
Primero, no solución MVVM (no borraré esto como referencia)
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; }
Segunda, probablemente una mejor solución: usar comportamientos adjuntos
XAML
<Button Content="Ok and Close" Command="{Binding OkCommand}" b:CloseOnClickBehaviour.IsEnabled="True" />
Ver modelo
public ICommand OkCommand
{
get { return _okCommand; }
}
Clase de comportamiento Algo similar a esto:
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();
}
}
Creo que la forma más simple no se ha incluido ya (casi). En lugar de usar Comportamientos que agrega nuevas dependencias, simplemente use las propiedades adjuntas:
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;
}
}
Luego simplemente configúralo en tus botones de diálogo:
<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>
Esto podría ayudarte, cerrando una ventana de wpf usando mvvm con un código mínimo: http://jkshay.com/closing-a-wpf-window-using-mvvm-and-minimal-code-behind/
Hay un comportamiento útil para esta tarea que no rompe MVVM, un Comportamiento, introducido con Expression Blend 3, para permitir que la Vista se enganche en comandos definidos completamente dentro de ViewModel.
Este comportamiento demuestra una técnica simple para permitir que ViewModel administre los eventos de cierre de la Vista en una aplicación Model-View-ViewModel.
Esto le permite conectar un comportamiento en su Vista (UserControl) que proporcionará control sobre la Ventana del control, permitiendo que ViewModel controle si la ventana se puede cerrar a través de ICommands estándar.
Uso de comportamientos para permitir que ViewModel administre View Lifetime en MV-VM
http://gallery.expression.microsoft.com/WindowCloseBehavior/
El enlace anterior ha sido archivado en http://code.msdn.microsoft.com/Window-Close-Attached-fef26a66#content
He estado buscando una solución para el mismo problema y descubrí que seguir funciona bien. La solución es similar a lo que OP ha mencionado en su pregunta con algunas diferencias:
Sin necesidad de la propiedad
IsCancel
.El código detrás no debe cerrar la ventana. Solo establece
DialogResult
En mi caso, primero ejecuta código detrás y luego ve el comando modelo vinculado al botón.
XAML
<Button x:Name="buttonOk" Click="Save_Click" Command="{Binding SaveCommand}">OK</Button>
Código detrás
private void Apply_OnClick(object sender, RoutedEventArgs e)
{
this.DialogResult = true;
}
Ver modelo
private void Save()
{
// Save data.
}
Espero que esto ayude.
Intenté resolver este problema de alguna manera genérica, MVVM, pero siempre encuentro que termino con lógica compleja innecesaria. Para lograr un comportamiento cercano, hice una excepción a la regla de no usar código y recurrí simplemente al uso de buenos eventos en el código subyacente:
XAML:
<Button Content="Close" Click="OnCloseClicked" />
Código detrás:
private void OnCloseClicked(object sender, EventArgs e)
{
Visibility = Visibility.Collapsed;
}
Aunque desearía que esto se respaldara mejor con el uso de comandos / MVVM, simplemente creo que no hay una solución más simple y más clara que el uso de eventos.
Lamentablemente, la visualización de ventanas es un verdadero problema en MVVM, por lo que debe hacer un poco de trabajo de infraestructura o utilizar un marco de MVVM como Cinch . Si desea invertir el tiempo para hacerlo usted mismo, here''s un enlace de cómo lo hace Cinch.
Es bueno que trates de mantener la lógica fuera de la Vista, pero realmente no es el fin del mundo si lo haces. En este caso, no parece que cause demasiados problemas.
Luché con este tema durante algún tiempo y, finalmente, opté por el enfoque más simple que sigue siendo coherente con MVVM: haga que el botón ejecute el comando que hace todo el trabajo pesado y haga que el manejador de clic del botón cierre la ventana.
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!
Es cierto, todavía hay código subyacente, pero no hay nada intrínsecamente malo en eso. Y para mí tiene más sentido, desde la perspectiva de OO, simplemente decirle a la ventana que se cierre.
Muy limpio y MVVM es utilizar InteractionTrigger
y CallMethodAction
definidos en Microsoft.Interactivity.Core
Deberá agregar dos espacios de nombres como se muestra a continuación
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
xmlns:ei="http://schemas.microsoft.com/expression/2010/interactions"
Y Assemblies System.Windows.Interactivity y Microsoft.Expression.Interactions y luego el código Below xaml funcionarán.
<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>
No necesita ningún código ni nada más y también puede llamar a cualquier otro método de Window
.
Para aplicaciones pequeñas, utilizo mi propio controlador de aplicaciones para mostrar, cerrar y eliminar ventanas y DataContexts. Es un punto central en la interfaz de usuario de una aplicación.
Es algo como esto:
//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();
}
}
y sus invocaciones de ViewModels :
// Show new Window with DataContext
ApplicationController.Instance.ShowNewWindow(
new ClientCardsWindow(),
new ClientCardsVM(),
false);
// Close Current Window from viewModel
ApplicationController.Instance.CloseWindow(this);
Por supuesto, puedes encontrar algunas restricciones en mi solución. De nuevo: lo uso para proyectos pequeños, y es suficiente. Si está interesado, puedo publicar el código completo aquí o en otro lugar /
Podrías reformular la pregunta, y al hacerlo, encontrando otra solución. ¿Cómo puedo habilitar la comunicación entre vistas, viewmodels y whatnot en un entorno MVVM? Puedes usar el patrón Mediator. Básicamente es un sistema de notificación. Para la implementación actual de Mediator, busque google o pregúnteme y puedo enviarla por correo electrónico.
Haz un comando cuyo propósito sea cerrar la vista.
public void Execute( object parameter )
{
this.viewModel.DisposeMyStuff();
Mediator.NotifyColleagues(Mediator.Token.ConfigWindowShouldClose);
}
El Mediador levantará una notificación (un token)
Escucha esta notificación (token) de esta manera en el constructor Ver código subyacente:
public ClientConfigView()
{
InitializeComponent();
Mediator.ListenOn(Mediator.Token.ConfigWindowShouldClose, callback => this.Close() );
}
Puedes hacerlo sin código detrás. Crear comando, en Método de ejecución, llamar al método "Guardar" en viewmodel y después de ese método call close en la ventana de edición, que puede pasar al comando por parámetro:
public void Execute(object parameter)
{
_mainViewModel.SaveSomething();
var editWindow = parameter as MyEditWindow;
editWindow?.Close();
}
Botón Guardar y Cerrar XAML:
<Button Content"Save&Close" Command="{Binding SaveCmd}" CommandParameter="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=Window}}" IsDefault="True" />
También tuve que lidiar con este problema, así que aquí está mi solución. Funciona muy bien para mí.
1. Crear clase 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. Defina su comando
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. Vincula tu comando en la vista
public MyView(Window win) //ctor of your view, window as parameter
{
InitializeComponent();
MyButton.CommandParameter = win;
MyButton.Command = ((MyViewModel)this.DataContext).CloseWindowCommand;
}
4. Y ahora la ventana
Window win = new Window()
{
Title = "My Window",
Height = 800,
Width = 800,
WindowStartupLocation = WindowStartupLocation.CenterScreen,
};
win.Content = new MyView(win);
win.ShowDialog();
entonces eso es todo, también puedes vincular el comando en el archivo xaml y encontrar la ventana con FindAncestor y vincularla al parámetro del comando.
Tenemos la propiedad del nombre en la definición .xaml:
x:Name="WindowsForm"
Entonces tenemos el botón:
<Button Command="{Binding CloseCommand}"
CommandParameter="{Binding ElementName=WindowsForm}" />
Luego en ViewModel:
public DelegateCommand <Object> CloseCommand { get; private set; }
Constructor for that view model:
this.CloseCommand = new DelegateCommand<object>(this.CloseAction);
Entonces, por fin, el método de acción:
private void CloseAction (object obj)
{
Window Win = obj as Window;
Win.Close();
}
Usé este código para cerrar una ventana emergente de una aplicación.
Tengo la siguiente solución en Silverlight. También estaría en 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;
}
}
MainPage.xaml:
<Grid x:Name="LayoutRoot">
<Button Command="{Binding InsertItemCommand}" Content="Add New" Width="70" HorizontalAlignment="Left" VerticalAlignment="Center" />
</Grid>
Les deseo buenas ideas y proyectos ;-)
Uso el patrón Publicar suscripción para dependencias de clase complicadas:
ViewModel:
public class ViewModel : ViewModelBase
{
public ViewModel()
{
CloseComand = new DelegateCommand((obj) =>
{
MessageBus.Instance.Publish(Messages.REQUEST_DEPLOYMENT_SETTINGS_CLOSED, null);
});
}
}
Ventana:
public partial class SomeWindow : Window
{
Subscription _subscription = new Subscription();
public SomeWindow()
{
InitializeComponent();
_subscription.Subscribe(Messages.REQUEST_DEPLOYMENT_SETTINGS_CLOSED, obj =>
{
this.Close();
});
}
}
Puede aprovechar Bizmonger.Patterns para obtener 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
}
}
Suscripción
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);
}
}
Yo personalmente usaría un comportamiento para hacer este tipo de cosas:
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();
};
}
}
A continuación, puede adjuntar esto a su Window
y Button
para hacer el trabajo:
<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>
CommandParameter
Command
and CommandParameter
aquí para que pueda ejecutar un comando antes de que se cierre la Window
.