WPF MVVM: как закрыть окно
У меня есть Button
это закрывает мое окно при нажатии:
<Button x:Name="buttonOk" IsCancel="True">Ok</Button>
Это нормально, пока я не добавлю Command
к Button
т.е.
<Button x:Name="buttonOk"
Command="{Binding SaveCommand}"
IsCancel="True">Ok</Button>
Теперь это не закрывается, вероятно, потому что я обрабатываю Command
, Я могу это исправить, поставив EventHandler
и зовет this.Close()
т.е.
<Button x:Name="buttonOk"
Click="closeWindow"
Command="{Binding SaveCommand}"
IsCancel="True">Ok</Button>
но теперь у меня есть код в моем коде, т.е. метод SaveCommand
, Я использую шаблон MVVM и SaveCommand
это единственный код в моем коде позади.
Как я могу сделать это по-другому, чтобы не использовать код позади?
23 ответа
Я только что закончил запись в блоге на эту тему. В двух словах, добавить Action
свойство вашей ViewModel с get
а также set
аксессоры. Затем определите Action
от твоего View
конструктор. Наконец, вызовите ваше действие в связанной команде, которая должна закрыть окно.
В ViewModel:
public Action CloseAction { get; set;}
и в View
конструктор:
private View()
{
InitializeComponent();
ViewModel vm = new ViewModel();
this.DataContext = vm;
if ( vm.CloseAction == null )
vm.CloseAction = new Action(this.Close);
}
Наконец, в любой связанной команде, которая должна закрывать окно, мы можем просто вызвать
CloseAction(); // Calls Close() method of the View
Это сработало для меня, показалось довольно элегантным решением и сэкономило мне кучу кода.
Очень чистый и MVVM способ заключается в использовании InteractionTrigger
а также CallMethodAction
определяется в Microsoft.Interactivity.Core
Вам нужно будет добавить два пространства имен, как показано ниже
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
xmlns:ei="http://schemas.microsoft.com/expression/2010/interactions"
И сборки System.Windows.Interactivity и Microsoft.Expression.Interactions, а затем код ниже XAML будет работать.
<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>
Вам не нужен какой-либо код или что-то еще, и вы также можете вызвать любой другой метод Window
,
К сожалению, отображение окон - это настоящая проблема в MVVM, поэтому вам нужно немного поработать над инфраструктурой или использовать инфраструктуру MVVM, такую как Cinch. Если вы хотите потратить время, чтобы сделать это самостоятельно, вот ссылка на то, как Cinch делает это.
Хорошо, что вы пытаетесь не допустить никакой логики в представление, но на самом деле это не конец света, если вы это сделаете. В этом случае это не звучит так, как будто это вызовет слишком много проблем.
Как кто-то прокомментировал, код, который я разместил, не является дружественным к MVVM, как насчет второго решения?
1-ое, не решение MVVM (я не буду удалять это как ссылку)
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-е, возможно, лучшее решение: использование прикрепленного поведения
XAML
<Button Content="Ok and Close" Command="{Binding OkCommand}" b:CloseOnClickBehaviour.IsEnabled="True" />
Посмотреть модель
public ICommand OkCommand
{
get { return _okCommand; }
}
Класс поведения Что-то похожее на это:
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();
}
}
Я бы лично использовал поведение, чтобы делать такие вещи:
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();
};
}
}
Затем вы можете прикрепить это к вашему Window
а также Button
сделать работу:
<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>
я добавил Command
а также CommandParameter
здесь, чтобы вы могли запустить команду до Window
закрывается.
Для небольших приложений я использую свой собственный Application Controller для отображения, закрытия и удаления окон и DataContexts. Это центральная точка в пользовательском интерфейсе приложения.
Это что-то вроде этого:
//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();
}
}
и их вызовы из ViewModels:
// Show new Window with DataContext
ApplicationController.Instance.ShowNewWindow(
new ClientCardsWindow(),
new ClientCardsVM(),
false);
// Close Current Window from viewModel
ApplicationController.Instance.CloseWindow(this);
Конечно, вы можете найти некоторые ограничения в моем решении. Опять же: я использую это для небольших проектов, и этого достаточно. Если вам интересно, я могу выложить полный код здесь или где-нибудь еще /
Я пытался решить эту проблему каким-то общим способом MVVM, но я всегда нахожу, что в итоге получаю ненужную сложную логику. Чтобы добиться близкого поведения, я сделал исключение из правила отсутствия кода и прибегнул к простому использованию хороших старых событий в коде:
XAML:
<Button Content="Close" Click="OnCloseClicked" />
Код позади:
private void OnCloseClicked(object sender, EventArgs e)
{
Visibility = Visibility.Collapsed;
}
Хотелось бы, чтобы это лучше поддерживалось с помощью команд /MVVM, я просто думаю, что не существует более простого и понятного решения, чем использование событий.
Я использую шаблон " Опубликовать подписку" для сложных зависимостей классов:
ViewModel:
public class ViewModel : ViewModelBase
{
public ViewModel()
{
CloseComand = new DelegateCommand((obj) =>
{
MessageBus.Instance.Publish(Messages.REQUEST_DEPLOYMENT_SETTINGS_CLOSED, null);
});
}
}
Окно:
public partial class SomeWindow : Window
{
Subscription _subscription = new Subscription();
public SomeWindow()
{
InitializeComponent();
_subscription.Subscribe(Messages.REQUEST_DEPLOYMENT_SETTINGS_CLOSED, obj =>
{
this.Close();
});
}
}
Вы можете использовать Bizmonger.Patterns, чтобы получить 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
}
}
Подписка
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);
}
}
У нас есть свойство name в определении.xaml:
x:Name="WindowsForm"
Тогда у нас есть кнопка:
<Button Command="{Binding CloseCommand}"
CommandParameter="{Binding ElementName=WindowsForm}" />
Затем в ViewModel:
public DelegateCommand <Object> CloseCommand { get; private set; }
Constructor for that view model:
this.CloseCommand = new DelegateCommand<object>(this.CloseAction);
Тогда, наконец, метод действия:
private void CloseAction (object obj)
{
Window Win = obj as Window;
Win.Close();
}
Я использовал этот код, чтобы закрыть всплывающее окно из приложения..
Некоторое время я боролся с этой темой и в конце концов выбрал самый простой подход, который по-прежнему совместим с MVVM: кнопка выполняет команду, выполняющую всю тяжелую работу, и закрывает окно обработчиком нажатия кнопки.
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!
Правда, код еще есть, но в этом нет ничего плохого. И для меня имеет смысл, с точки зрения ОО, просто сказать окну закрыться.
Для этой задачи есть полезное поведение, которое не нарушает MVVM, поведение, введенное в Expression Blend 3, чтобы позволить представлению подключаться к командам, полностью определенным в ViewModel.
Это поведение демонстрирует простой метод, позволяющий ViewModel управлять событиями закрытия View в приложении Model-View-ViewModel.
Это позволяет вам подключить поведение в вашем View (UserControl), которое обеспечит контроль над окном элемента управления, позволяя ViewModel контролировать, можно ли закрыть окно с помощью стандартных ICommands.
Использование поведений, позволяющих ViewModel управлять временем жизни представления в MV-VM
http://gallery.expression.microsoft.com/WindowCloseBehavior/
Выше ссылка была заархивирована на http://code.msdn.microsoft.com/Window-Close-Attached-fef26a66
Мне пришлось делать это в приложении WPF на основе.Net Core 3.0, где, к сожалению, поддержка поведения еще не была официально доступна в пакете NuGet Microsoft.Xaml.Behaviors.Wpf.
Вместо этого я выбрал решение, в котором использовался шаблон проектирования "Фасад".
Интерфейс:
public interface IWindowFacade
{
void Close();
}
Окно:
public partial class MainWindow : Window, IWindowFacade
…
Стандартное свойство команды в модели представления:
public ICommand ExitCommand
…
Привязка управления:
<MenuItem Header="E_xit" Command="{Binding ExitCommand}" CommandParameter="{Binding RelativeSource={RelativeSource AncestorType=Window}}"/>
Команда:
public class ExitCommand : ICommand
{
…
public void Execute(object parameter)
{
var windowFacade = parameter as IWindowFacade;
windowFacade?.Close();
}
…
}
Поскольку Close()
метод уже реализован Window
Класс, применение интерфейса фасада к окну - единственный требуемый код на уровне пользовательского интерфейса (для этого простого примера). Команда на уровне представления избегает любых зависимостей от уровня представления / пользовательского интерфейса, поскольку она не знает, с чем она говорит, когда вызываетClose
метод на фасаде.
В вашем текущем окне xaml.cs
файл, вызовите приведенный ниже код:
var curWnd = Window.GetWindow(this); // passing current window context
curWnd?.Close();
Это должно сработать.
У меня сработало, надеюсь, сделаю то же самое для вас)
Мне тоже приходилось сталкиваться с этой проблемой, поэтому здесь мое решение. Это прекрасно работает для меня.
1. Создайте класс 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. Определите свою команду
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. Свяжите свою команду в представлении
public MyView(Window win) //ctor of your view, window as parameter
{
InitializeComponent();
MyButton.CommandParameter = win;
MyButton.Command = ((MyViewModel)this.DataContext).CloseWindowCommand;
}
4. А теперь окно
Window win = new Window()
{
Title = "My Window",
Height = 800,
Width = 800,
WindowStartupLocation = WindowStartupLocation.CenterScreen,
};
win.Content = new MyView(win);
win.ShowDialog();
Вот и все, вы также можете связать команду в файле xaml, найти окно с FindAncestor и связать его с параметром команды.
Я думаю, что самый простой способ не был включен (почти). Вместо использования Поведения, которое добавляет новые зависимости, просто используйте вложенные свойства:
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;
}
}
Затем просто установите его на свои диалоговые кнопки:
<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>
Это может помочь вам, закрыв окно wpf с помощью mvvm с минимальным кодом: http://jkshay.com/closing-a-wpf-window-using-mvvm-and-minimal-code-behind/
У меня есть следующее решение в Silverlight. Также будет в 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>
Желаю всем хороших идей и проектов;-)
Простой подход - закрыть окно при реализации saveComand. Используйте приведенный ниже код, чтобы закрыть окно.
Application.Current.Windows[1].Close();
Это закроет дочернее окно.
Я искал решение той же проблемы и обнаружил, что выполнение следующих действий работает нормально. Решение похоже на то, что OP упомянул в своем вопросе с некоторыми отличиями:
Нет необходимости
IsCancel
имущество.Код позади не должен закрывать окно. Просто установите
DialogResult
В моем случае сначала выполняется код, а затем просматривается команда модели, привязанная к кнопке.
XAML
<Button x:Name="buttonOk" Click="Save_Click" Command="{Binding SaveCommand}">OK</Button>
Код позади
private void Apply_OnClick(object sender, RoutedEventArgs e)
{
this.DialogResult = true;
}
Посмотреть модель
private void Save()
{
// Save data.
}
Надеюсь это поможет.
Решение о закрытии окна в wpf, которое сработало для меня, здесь не дано, поэтому я подумал, что тоже добавлю свое решение.
private static Window GetWindow(DependencyObject sender)
{
Window window = null;
if (sender is Window)
window = (Window)sender;
if (window == null)
window = Window.GetWindow(sender);
return window;
}
private void CloseWindow(object sender, RoutedEventArgs e)
{
var button = (Button)sender as DependencyObject;
Window window = GetWindow(button);
if (window != null)
window.Close();
// window.Visibility = Visibility.Hidden;
// choose between window.close or set window.visibility to close or hide the window.
// }
}
Добавьте событие CloseWindow к кнопке в вашем окне следующим образом.
<Button Content="Cancel" Click="CloseWindow" >
Без всяких зависимостей.
<Window ...>
...
<Button Command="{x:Static SystemCommands.CloseWindowCommand}" />
</Window>
Вы можете перефразировать вопрос, и тем самым - придумать другое решение. Как я могу включить связь между представлениями, моделями представления и еще чем-то в среде MVVM? Вы можете использовать шаблон Посредника. Это в основном система уведомлений. Для фактической реализации Посредника, Google для этого или спросите меня, и я могу написать по электронной почте.
Составьте команду, цель которой - закрыть вид.
public void Execute( object parameter )
{
this.viewModel.DisposeMyStuff();
Mediator.NotifyColleagues(Mediator.Token.ConfigWindowShouldClose);
}
Посредник выдаст уведомление (токен)
Прослушайте это уведомление (токен), например, в конструкторе View codebehind:
public ClientConfigView()
{
InitializeComponent();
Mediator.ListenOn(Mediator.Token.ConfigWindowShouldClose, callback => this.Close() );
}
Вы можете сделать это без кода. Создайте команду, в методе Выполнить вызов метода "Сохранить" в viewmodel и после этого вызовите метод close в окне редактирования, который вы можете передать команде по параметру:
public void Execute(object parameter)
{
_mainViewModel.SaveSomething();
var editWindow = parameter as MyEditWindow;
editWindow?.Close();
}
Кнопка Сохранить и закрыть XAML:
<Button Content"Save&Close" Command="{Binding SaveCmd}" CommandParameter="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=Window}}" IsDefault="True" />