Я создаю логин, используя, window control
чтобы позволить пользователю войти в WPF
приложение, которое я создаю.
До сих пор я создал метод, который проверяет, ввел ли пользователь правильные учетные данные для username
и password
в a textbox
на экране входа в систему, binding
два properties
.
Я добился этого, создав такой bool
метод;
public bool CheckLogin()
{
var user = context.Users.Where(i => i.Username == this.Username).SingleOrDefault();
if (user == null)
{
MessageBox.Show("Unable to Login, incorrect credentials.");
return false;
}
else if (this.Username == user.Username || this.Password.ToString() == user.Password)
{
MessageBox.Show("Welcome " + user.Username + ", you have successfully logged in.");
return true;
}
else
{
MessageBox.Show("Unable to Login, incorrect credentials.");
return false;
}
}
public ICommand ShowLoginCommand
{
get
{
if (this.showLoginCommand == null)
{
this.showLoginCommand = new RelayCommand(this.LoginExecute, null);
}
return this.showLoginCommand;
}
}
private void LoginExecute()
{
this.CheckLogin();
}
У меня также есть то, command
что я bind
к моей кнопке внутри xaml
вроде так;
<Button Name="btnLogin" IsDefault="True" Content="Login" Command="{Binding ShowLoginCommand}" />
Когда я ввожу имя пользователя и пароль, он выполняет соответствующий код, независимо от того, правильный он или неправильный. Но как я могу закрыть это окно из ViewModel, если и имя пользователя, и пароль верны?
Ранее я пробовал использовать, dialog modal
но это не совсем сработало. Кроме того, в моем app.xaml я сделал что-то вроде следующего: сначала загружает страницу входа, а затем, когда она истинна, загружает реальное приложение.
private void ApplicationStart(object sender, StartupEventArgs e)
{
Current.ShutdownMode = ShutdownMode.OnExplicitShutdown;
var dialog = new UserView();
if (dialog.ShowDialog() == true)
{
var mainWindow = new MainWindow();
Current.ShutdownMode = ShutdownMode.OnMainWindowClose;
Current.MainWindow = mainWindow;
mainWindow.Show();
}
else
{
MessageBox.Show("Unable to load application.", "Error", MessageBoxButton.OK);
Current.Shutdown(-1);
}
}
Вопрос: Как мне закрыть логин Window control
из ViewModel?
Заранее спасибо.
Ответы:
Вы можете передать окно своей ViewModel, используя
CommandParameter
. См. Мой пример ниже.Я реализовал
CloseWindow
метод, который принимает параметр Windows как параметр и закрывает его. Окно передается в ViewModel черезCommandParameter
. Обратите внимание, что вам нужно определитьx:Name
для окна, которое должно быть закрыто. В моем окне XAML я вызываю этот метод черезCommand
и передаю само окно в качестве параметра ViewModel, используяCommandParameter
.Command="{Binding CloseWindowCommand, Mode=OneWay}" CommandParameter="{Binding ElementName=TestWindow}"
ViewModel
public RelayCommand<Window> CloseWindowCommand { get; private set; } public MainViewModel() { this.CloseWindowCommand = new RelayCommand<Window>(this.CloseWindow); } private void CloseWindow(Window window) { if (window != null) { window.Close(); } }
Посмотреть
<Window x:Class="ClientLibTestTool.ErrorView" x:Name="TestWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:localization="clr-namespace:ClientLibTestTool.ViewLanguages" DataContext="{Binding Main, Source={StaticResource Locator}}" Title="{x:Static localization:localization.HeaderErrorView}" Height="600" Width="800" ResizeMode="NoResize" WindowStartupLocation="CenterScreen"> <Grid> <Button Content="{x:Static localization:localization.ButtonClose}" Height="30" Width="100" Margin="0,0,10,10" IsCancel="True" VerticalAlignment="Bottom" HorizontalAlignment="Right" Command="{Binding CloseWindowCommand, Mode=OneWay}" CommandParameter="{Binding ElementName=TestWindow}"/> </Grid> </Window>
Обратите внимание, что я использую среду MVVM light, но принцип применим ко всем приложениям wpf.
Это решение нарушает шаблон MVVM, потому что модель представления ничего не должна знать о реализации пользовательского интерфейса. Если вы хотите строго следовать парадигме программирования MVVM, вам необходимо абстрагировать тип представления с помощью интерфейса.
Решение, соответствующее MVVM (бывший EDIT2)
пользователь Crono упоминает действительную точку в разделе комментариев:
Вы можете исправить это, представив интерфейс, содержащий метод закрытия.
Интерфейс:
public interface ICloseable { void Close(); }
Ваша реорганизованная ViewModel будет выглядеть так:
ViewModel
public RelayCommand<ICloseable> CloseWindowCommand { get; private set; } public MainViewModel() { this.CloseWindowCommand = new RelayCommand<IClosable>(this.CloseWindow); } private void CloseWindow(ICloseable window) { if (window != null) { window.Close(); } }
Вы должны указать и реализовать
ICloseable
интерфейс в своем представлении.Просмотр (код позади)
public partial class MainWindow : Window, ICloseable { public MainWindow() { InitializeComponent(); } }
Ответ на исходный вопрос: (бывший EDIT1)
Ваша кнопка входа в систему (добавлен параметр CommandParameter):
<Button Name="btnLogin" IsDefault="True" Content="Login" Command="{Binding ShowLoginCommand}" CommandParameter="{Binding ElementName=LoginWindow}"/>
Ваш код:
public RelayCommand<Window> CloseWindowCommand { get; private set; } // the <Window> is important for your solution! public MainViewModel() { //initialize the CloseWindowCommand. Again, mind the <Window> //you don't have to do this in your constructor but it is good practice, thought this.CloseWindowCommand = new RelayCommand<Window>(this.CloseWindow); } public bool CheckLogin(Window loginWindow) //Added loginWindow Parameter { var user = context.Users.Where(i => i.Username == this.Username).SingleOrDefault(); if (user == null) { MessageBox.Show("Unable to Login, incorrect credentials."); return false; } else if (this.Username == user.Username || this.Password.ToString() == user.Password) { MessageBox.Show("Welcome "+ user.Username + ", you have successfully logged in."); this.CloseWindow(loginWindow); //Added call to CloseWindow Method return true; } else { MessageBox.Show("Unable to Login, incorrect credentials."); return false; } } //Added CloseWindow Method private void CloseWindow(Window window) { if (window != null) { window.Close(); } }
источник
private void LoginExecute(){this.CheckLogin();}
<- CheckLogin должен принимать параметр.CommandParameter="{Binding RelativeSource={RelativeSource AncestorType={x:Type Window}}}"
Window
объекта в модель представления нарушает шаблон MVVM IMHO, потому что он заставляет вашу виртуальную машину знать, в чем он просматривается. Что, если бы вместо этого представление было закрепленной вкладкой в интерфейсе MDI? Правильный способ сделать это IMHO - передать какой-то интерфейс IUIHost, который реализует метод Close, и иметь любое представление, которое вы хотите, чтобы показать, что ваш vm реализует его.Я обычно помещаю событие в модель представления, когда мне нужно это сделать, а затем подключаю его к
Window.Close()
при привязке модели представления к окну.public class LoginViewModel { public event EventHandler OnRequestClose; private void Login() { // Login logic here OnRequestClose(this, new EventArgs()); } }
И при создании окна входа в систему
var vm = new LoginViewModel(); var loginWindow = new LoginWindow { DataContext = vm }; vm.OnRequestClose += (s, e) => loginWindow.Close(); loginWindow.ShowDialog();
источник
Loaded
,ContentRendered
для главного окна, диалоговых служб и т. Д.), Добавление немного к нему через событие ViewModel довольно чистое, как для меня. Три строки кода на самом деле не требуют решения для повторного использования. PS: чистый MVVM в любом случае предназначен для ботаников.Оставаясь MVVM, я думаю, что использование Behaviors из Blend SDK (System.Windows.Interactivity) или пользовательского запроса взаимодействия из Prism может очень хорошо сработать в такой ситуации.
Если вы идете по маршруту поведения, вот общая идея:
public class CloseWindowBehavior : Behavior<Window> { public bool CloseTrigger { get { return (bool)GetValue(CloseTriggerProperty); } set { SetValue(CloseTriggerProperty, value); } } public static readonly DependencyProperty CloseTriggerProperty = DependencyProperty.Register("CloseTrigger", typeof(bool), typeof(CloseWindowBehavior), new PropertyMetadata(false, OnCloseTriggerChanged)); private static void OnCloseTriggerChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { var behavior = d as CloseWindowBehavior; if (behavior != null) { behavior.OnCloseTriggerChanged(); } } private void OnCloseTriggerChanged() { // when closetrigger is true, close the window if (this.CloseTrigger) { this.AssociatedObject.Close(); } } }
Затем в своем окне вы просто привяжете CloseTrigger к логическому значению, которое будет установлено, когда вы хотите, чтобы окно закрылось.
<Window x:Class="TestApp.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity" xmlns:local="clr-namespace:TestApp" Title="MainWindow" Height="350" Width="525"> <i:Interaction.Behaviors> <local:CloseWindowBehavior CloseTrigger="{Binding CloseTrigger}" /> </i:Interaction.Behaviors> <Grid> </Grid> </Window>
Наконец, ваш DataContext / ViewModel будет иметь свойство, которое вы должны установить, когда хотите, чтобы окно закрывалось следующим образом:
public class MainWindowViewModel : INotifyPropertyChanged { private bool closeTrigger; /// <summary> /// Gets or Sets if the main window should be closed /// </summary> public bool CloseTrigger { get { return this.closeTrigger; } set { this.closeTrigger = value; RaisePropertyChanged(nameof(CloseTrigger)); } } public MainWindowViewModel() { // just setting for example, close the window CloseTrigger = true; } protected void RaisePropertyChanged(string propertyName) { if (PropertyChanged != null) { PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); } } public event PropertyChangedEventHandler PropertyChanged; }
(установите Window.DataContext = new MainWindowViewModel ())
источник
boolean
значению. Когда вы это сказали, вы имели в виду, что я создаюDataTrigger
для этого?может быть уже поздно, но вот мой ответ
foreach (Window item in Application.Current.Windows) { if (item.DataContext == this) item.Close(); }
источник
Вот кое-что, что я использовал в нескольких проектах. Это может выглядеть как взлом, но работает нормально.
public class AttachedProperties : DependencyObject //adds a bindable DialogResult to window { public static readonly DependencyProperty DialogResultProperty = DependencyProperty.RegisterAttached("DialogResult", typeof(bool?), typeof(AttachedProperties), new PropertyMetaData(default(bool?), OnDialogResultChanged)); public bool? DialogResult { get { return (bool?)GetValue(DialogResultProperty); } set { SetValue(DialogResultProperty, value); } } private static void OnDialogResultChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { var window = d as Window; if (window == null) return; window.DialogResult = (bool?)e.NewValue; } }
Теперь вы можете
DialogResult
выполнить привязку к виртуальной машине и установить для нее значение свойства.Window
Закроется, когда значение установлено.<!-- Assuming that the VM is bound to the DataContext and the bound VM has a property DialogResult --> <Window someNs:AttachedProperties.DialogResult={Binding DialogResult} />
Это резюме того, что работает в нашей производственной среде.
<Window x:Class="AC.Frontend.Controls.DialogControl.Dialog" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:DialogControl="clr-namespace:AC.Frontend.Controls.DialogControl" xmlns:hlp="clr-namespace:AC.Frontend.Helper" MinHeight="150" MinWidth="300" ResizeMode="NoResize" SizeToContent="WidthAndHeight" WindowStartupLocation="CenterScreen" Title="{Binding Title}" hlp:AttachedProperties.DialogResult="{Binding DialogResult}" WindowStyle="ToolWindow" ShowInTaskbar="True" Language="{Binding UiCulture, Source={StaticResource Strings}}"> <!-- A lot more stuff here --> </Window>
Как видите, я
xmlns:hlp="clr-namespace:AC.Frontend.Helper"
сначала объявляю пространство имен, а затем привязкуhlp:AttachedProperties.DialogResult="{Binding DialogResult}"
.Это
AttachedProperty
выглядит так. Это не то же самое, что я писал вчера, но имхо это не должно иметь никакого эффекта.public class AttachedProperties { #region DialogResult public static readonly DependencyProperty DialogResultProperty = DependencyProperty.RegisterAttached("DialogResult", typeof (bool?), typeof (AttachedProperties), new PropertyMetadata(default(bool?), OnDialogResultChanged)); private static void OnDialogResultChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { var wnd = d as Window; if (wnd == null) return; wnd.DialogResult = (bool?) e.NewValue; } public static bool? GetDialogResult(DependencyObject dp) { if (dp == null) throw new ArgumentNullException("dp"); return (bool?)dp.GetValue(DialogResultProperty); } public static void SetDialogResult(DependencyObject dp, object value) { if (dp == null) throw new ArgumentNullException("dp"); dp.SetValue(DialogResultProperty, value); } #endregion }
источник
<Window />
элемент, как я проиллюстрировал в своем фрагменте. Мне было просто лень писать остальное (объявления пространств имен и т. Д.), Которые обычно там тоже объявляются.datatrigger
и назначить его кнопке, чтобы она работала? Снова извините за новостной вопрос.DataTrigger¬ and setting value
истину?DataContext
этомDialog
. Я бы ожидал, что виртуальная машина, установленная как,DataContext
предоставляет команду, которая устанавливает свойствоDialogResult
или все, что вы связалиtrue
илиfalse
, так чтоDialog
закрывается.Простой способ
public interface IRequireViewIdentification { Guid ViewID { get; } }
Реализовать в ViewModel
public class MyViewVM : IRequireViewIdentification { private Guid _viewId; public Guid ViewID { get { return _viewId; } } public MyViewVM() { _viewId = Guid.NewGuid(); } }
Добавить общий помощник оконного менеджера
public static class WindowManager { public static void CloseWindow(Guid id) { foreach (Window window in Application.Current.Windows) { var w_id = window.DataContext as IRequireViewIdentification; if (w_id != null && w_id.ViewID.Equals(id)) { window.Close(); } } } }
И закройте его вот так в viewmodel
WindowManager.CloseWindow(ViewID);
источник
WindowManager
, которая, в свою очередь, тесно связана сView
(с точки зренияPresentationFramework
). Было бы лучше, еслиWindowManager
бы сервис передавался в viewmodel через интерфейс. Тогда вы сможете (скажем) легко перенести свое решение на другую платформу.Вот простой пример использования MVVM Light Messenger вместо события. Модель представления отправляет сообщение о закрытии при нажатии кнопки:
public MainViewModel() { QuitCommand = new RelayCommand(ExecuteQuitCommand); } public RelayCommand QuitCommand { get; private set; } private void ExecuteQuitCommand() { Messenger.Default.Send<CloseMessage>(new CloseMessage()); }
Затем он передается в коде за окном.
public Main() { InitializeComponent(); Messenger.Default.Register<CloseMessage>(this, HandleCloseMessage); } private void HandleCloseMessage(CloseMessage closeMessage) { Close(); }
источник
Как насчет этого ?
ViewModel:
class ViewModel { public Action CloseAction { get; set; } private void Stuff() { // Do Stuff CloseAction(); // closes the window } }
В вашей ViewModel используйте CloseAction (), чтобы закрыть окно, как в примере выше.
Посмотреть:
public View() { InitializeComponent(); ViewModel vm = new ViewModel (); // this creates an instance of the ViewModel this.DataContext = vm; // this sets the newly created ViewModel as the DataContext for the View if (vm.CloseAction == null) vm.CloseAction = new Action(() => this.Close()); }
источник
Я знаю, что это старый пост, наверное, никто не стал бы прокручивать так далеко, я знаю, что не делал. Итак, после нескольких часов пробования разных вещей, я нашел этот блог, и чувак убил его. Самый простой способ сделать это, попробовал, и он отлично работает.
Блог
В ViewModel:
... public bool CanClose { get; set; } private RelayCommand closeCommand; public ICommand CloseCommand { get { if(closeCommand == null) ( closeCommand = new RelayCommand(param => Close(), param => CanClose); ) } } public void Close() { this.Close(); } ...
добавьте свойство Action в ViewModel, но определите его из файла кода программной части View. Это позволит нам динамически определять ссылку на ViewModel, которая указывает на View.
В ViewModel мы просто добавим:
public Action CloseAction { get; set; }
А в View мы определим его как таковой:
public View() { InitializeComponent() // this draws the View ViewModel vm = new ViewModel(); // this creates an instance of the ViewModel this.DataContext = vm; // this sets the newly created ViewModel as the DataContext for the View if ( vm.CloseAction == null ) vm.CloseAction = new Action(() => this.Close()); }
источник
Вы можете создать новый обработчик событий в ViewModel следующим образом.
public event EventHandler RequestClose; protected void OnRequestClose() { if (RequestClose != null) RequestClose(this, EventArgs.Empty); }
Затем определите RelayCommand для ExitCommand.
private RelayCommand _CloseCommand; public ICommand CloseCommand { get { if(this._CloseCommand==null) this._CloseCommand=new RelayCommand(CloseClick); return this._CloseCommand; } } private void CloseClick(object obj) { OnRequestClose(); }
Затем в наборе файлов XAML
<Button Command="{Binding CloseCommand}" />
Установите DataContext в файле xaml.cs и подпишитесь на созданное нами событие.
public partial class MainWindow : Window { private ViewModel mainViewModel = null; public MainWindow() { InitializeComponent(); mainViewModel = new ViewModel(); this.DataContext = mainViewModel; mainViewModel.RequestClose += delegate(object sender, EventArgs args) { this.Close(); }; } }
источник
Мой предложенный способ - объявить событие в ViewModel и использовать смесь InvokeMethodAction, как показано ниже.
Образец ViewModel
public class MainWindowViewModel : BindableBase, ICloseable { public DelegateCommand SomeCommand { get; private set; } #region ICloseable Implementation public event EventHandler CloseRequested; public void RaiseCloseNotification() { var handler = CloseRequested; if (handler != null) { handler.Invoke(this, EventArgs.Empty); } } #endregion public MainWindowViewModel() { SomeCommand = new DelegateCommand(() => { //when you decide to close window RaiseCloseNotification(); }); } }
I Closeable интерфейс, как показано ниже, но не требует выполнения этого действия. ICloseable поможет в создании общей службы просмотра, поэтому, если вы создаете представление и ViewModel путем внедрения зависимостей, вы можете сделать следующее:
internal interface ICloseable { event EventHandler CloseRequested; }
Использование ICloseable
var viewModel = new MainWindowViewModel(); // As service is generic and don't know whether it can request close event var window = new Window() { Content = new MainView() }; var closeable = viewModel as ICloseable; if (closeable != null) { closeable.CloseRequested += (s, e) => window.Close(); }
А ниже - Xaml. Вы можете использовать этот xaml, даже если вы не реализуете интерфейс, ему понадобится только ваша модель представления для повышения CloseRquested.
<Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:local="clr-namespace:WPFRx" xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity" xmlns:ei="http://schemas.microsoft.com/expression/2010/interactions" xmlns:ViewModels="clr-namespace:WPFRx.ViewModels" x:Name="window" x:Class="WPFRx.MainWindow" mc:Ignorable="d" Title="MainWindow" Height="350" Width="525" d:DataContext="{d:DesignInstance {x:Type ViewModels:MainWindowViewModel}}"> <i:Interaction.Triggers> <i:EventTrigger SourceObject="{Binding Mode=OneWay}" EventName="CloseRequested" > <ei:CallMethodAction TargetObject="{Binding ElementName=window}" MethodName="Close"/> </i:EventTrigger> </i:Interaction.Triggers> <Grid> <Button Content="Some Content" Command="{Binding SomeCommand}" Width="100" Height="25"/> </Grid>
источник
Вы можете использовать
Messenger
из набора инструментов MVVMLight. в вашемViewModel
отправьте сообщение, подобное этому:Messenger.Default.Send(new NotificationMessage("Close"));
затем в своем коде Windows
InitializeComponent
зарегистрируйтесь для этого сообщения следующим образом:Messenger.Default.Register<NotificationMessage>(this, m=>{ if(m.Notification == "Close") { this.Close(); } });
вы можете найти больше об инструментарии MVVMLight здесь: инструментарий MVVMLight на Codeplex
Обратите внимание, что в MVVM нет правила «никакого кода программной части», и вы можете регистрироваться для сообщений в представлении кода программной части.
источник
Это просто. Вы можете создать свой собственный класс ViewModel для входа в систему - LoginViewModel. Вы можете создать представление var dialog = new UserView (); внутри вашего LoginViewModel. И вы можете настроить Command LoginCommand на кнопку.
<Button Name="btnLogin" IsDefault="True" Content="Login" Command="{Binding LoginCommand}" />
а также
<Button Name="btnCancel" IsDefault="True" Content="Login" Command="{Binding CancelCommand}" />
ViewModel класс:
public class LoginViewModel { Window dialog; public bool ShowLogin() { dialog = new UserView(); dialog.DataContext = this; // set up ViewModel into View if (dialog.ShowDialog() == true) { return true; } return false; } ICommand _loginCommand public ICommand LoginCommand { get { if (_loginCommand == null) _loginCommand = new RelayCommand(param => this.Login()); return _loginCommand; } } public void CloseLoginView() { if (dialog != null) dialog.Close(); } public void Login() { if(CheckLogin()==true) { CloseLoginView(); } else { // write error message } } public bool CheckLogin() { // ... check login code return true; } }
источник
Object reference not set to an instance of an object.
для метода CloseLoginView. Есть предложения, как решить эту проблему?Я сделал это довольно просто:
YourWindow.xaml.cs
//In your constructor public YourWindow() { InitializeComponent(); DataContext = new YourWindowViewModel(this); }
YourWindowViewModel.cs
private YourWindow window;//so we can kill the window //In your constructor public YourWindowViewModel(YourWindow window) { this.window = window; } //to close the window public void CloseWindow() { window.Close(); }
Я не вижу ничего плохого в выбранном вами ответе, я просто подумал, что это может быть более простой способ сделать это!
источник
Вы можете рассматривать окно как службу (например, службу пользовательского интерфейса) и передавать себя модели просмотра через интерфейс , как таковой:
public interface IMainWindowAccess { void Close(bool result); } public class MainWindow : IMainWindowAccess { // (...) public void Close(bool result) { DialogResult = result; Close(); } } public class MainWindowViewModel { private IMainWindowAccess access; public MainWindowViewModel(IMainWindowAccess access) { this.access = access; } public void DoClose() { access.Close(true); } }
У этого решения есть большинство недостатков в передаче самого представления в модель просмотра без нарушения работы MVVM, потому что, хотя физически представление передается в модель просмотра, последний все еще не знает о первом, он видит только некоторые
IMainWindowAccess
. Так, например, если мы захотим перенести это решение на другую платформу, это будет лишь вопросIMainWindowAccess
правильной реализации , скажем, дляActivity
.Я публикую здесь решение, чтобы предложить другой подход, нежели события (хотя на самом деле он очень похож), потому что он кажется немного проще, чем события для реализации (присоединение / отсоединение и т. Д.), Но все же хорошо согласуется с шаблоном MVVM.
источник
Вы можете закрыть текущее окно, просто используя следующий код:
Application.Current.Windows[0].Close();
источник
System.Environment.Exit (0); в представлении модель будет работать.
источник