ViewModel에서 창 닫기
window control사용자 WPF가 내가 만들고 있는 응용 프로그램 에 로그인 할 수 있도록를 사용하여 로그인을 만듭니다.
지금까지, 나는 사용자가에 대한 올바른 자격 증명을 입력 여부를 확인하는 방법 만든 username및 passwordA의 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();
}
나는 또한 같은 내 버튼에 commandthat I 이 있습니다.bindxaml
<Button Name="btnLogin" IsDefault="True" Content="Login" Command="{Binding ShowLoginCommand}" />
사용자 이름과 비밀번호를 입력하면 옳든 틀리 든 적절한 코드를 실행합니다. 그러나 사용자 이름과 암호가 모두 정확할 때 ViewModel에서이 창을 어떻게 닫을 수 있습니까?
이전에 a를 사용해 보았지만 dialog modal제대로 작동하지 않았습니다. 또한 내 app.xaml 내에서 다음과 같은 작업을 수행하여 로그인 페이지를 먼저로드 한 다음 true가되면 실제 애플리케이션을로드합니다.
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 controlViewModel 에서 로그인 을 닫으려면 어떻게 해야합니까?
미리 감사드립니다.
를 사용하여 창을 ViewModel에 전달할 수 있습니다 CommandParameter. 아래 내 예를 참조하십시오.
CloseWindowWindows를 매개 변수로 사용하고 닫는 메서드를 구현 했습니다. 창은를 통해 ViewModel에 전달됩니다 CommandParameter. x:Name닫아야하는 창에 대해 를 정의 해야합니다. 내 XAML 창에서이 메서드를 통해 호출 Command하고 .NET을 사용하여 창 자체를 매개 변수로 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 라이트 프레임 워크를 사용하고 있지만 원칙은 모든 wpf 애플리케이션에 적용됩니다.
이 솔루션은 MVVM 패턴을 위반합니다. 뷰 모델은 UI 구현에 대해 알 수 없기 때문입니다. MVVM 프로그래밍 패러다임을 엄격하게 따르려면 인터페이스로 뷰 유형을 추상화해야합니다.
MVVM 준수 솔루션 (이전 EDIT2)
사용자 Crono 는 주석 섹션에서 유효한 점을 언급합니다.
Window 개체를 뷰 모델에 전달하면 MVVM 패턴 IMHO가 깨집니다. vm이 무엇을보고 있는지 알도록하기 때문입니다.
close 메소드가 포함 된 인터페이스를 도입하여이 문제를 해결할 수 있습니다.
상호 작용:
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();
}
}
MVVM을 유지하면서 Blend SDK (System.Windows.Interactivity)의 Behaviors 또는 Prism의 사용자 지정 상호 작용 요청을 사용하면 이런 종류의 상황에서 정말 잘 작동 할 수 있다고 생각합니다.
Behavior 경로를 사용하는 경우 일반적인 아이디어는 다음과 같습니다.
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("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 () 설정)
일반적으로이 작업을 수행해야 할 때 뷰 모델에 이벤트를 넣은 다음 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();
늦을 수도 있지만 여기에 내 대답이 있습니다.
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;
}
}
이제 DialogResultVM에 바인딩 하고 속성 값을 설정할 수 있습니다 . 는 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"먼저 선언 하고 그 후에 binding hlp:AttachedProperties.DialogResult="{Binding DialogResult}".
AttachedProperty모습이 맘에. 어제 게시 한 것과 같지는 않지만 IMHO는 아무런 효과가 없어야합니다.
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
}
쉬운 방법
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();
}
}
}
}
뷰 모델에서 이렇게 닫습니다
WindowManager.CloseWindow(ViewID);
어떻게 약 이 ?
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());
}
이것은 내가 아주 간단하게 한 방법입니다.
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();
}
선택한 답변에 잘못된 것이 없습니다.이 방법이 더 간단한 방법이라고 생각했습니다.
다음은 이벤트 대신 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에서 새로운 이벤트 핸들러를 생성 할 수 있습니다.
public event EventHandler RequestClose;
protected void OnRequestClose()
{
if (RequestClose != null)
RequestClose(this, EventArgs.Empty);
}
그런 다음 ExitCommand에 대한 RelayCommand를 정의합니다.
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}" />
xaml.cs 파일에 DataContext를 설정하고 생성 한 이벤트를 구독합니다.
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>
MessengerMVVMLight 툴킷에서 사용할 수 있습니다 . 다음 ViewModel과 같은 메시지를 보내 십시오 :
Messenger.Default.Send(new NotificationMessage("Close"));
그런 다음 Windows 코드에서 다음 InitializeComponent과 같이 해당 메시지를 등록하십시오.
Messenger.Default.Register<NotificationMessage>(this, m=>{
if(m.Notification == "Close")
{
this.Close();
}
});
MVVMLight 툴킷에 대한 자세한 내용은 여기에서 찾을 수 있습니다. Codeplex의 MVVMLight 툴킷
MVVM에는 "코드 숨김 없음 규칙"이 없으며 뷰 코드 숨김에서 메시지를 등록 할 수 있습니다.
I know this is an old post, probably no one would scroll this far, I know I didn't. So, after hours of trying different stuff, I found this blog and dude killed it. Simplest way to do this, tried it and it works like a charm.
It's simple. You can create your own ViewModel class for Login - LoginViewModel. You can create view var dialog = new UserView(); inside your LoginViewModel. And you can set-up Command LoginCommand into button.
<Button Name="btnLogin" IsDefault="True" Content="Login" Command="{Binding LoginCommand}" />
and
<Button Name="btnCancel" IsDefault="True" Content="Login" Command="{Binding CancelCommand}" />
ViewModel class:
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;
}
}
You can close the current window just by using the following code:
Application.Current.Windows[0].Close();
System.Environment.Exit(0); in view model would work.
참고URL : https://stackoverflow.com/questions/16172462/close-window-from-viewmodel
'developer tip' 카테고리의 다른 글
| 245px에서 "px"를 삭제하는 방법 (0) | 2020.10.11 |
|---|---|
| mvc4 razor의 텍스트 상자에 숫자 만 허용하는 방법 (0) | 2020.10.11 |
| PHP 메일러 다중 주소 (0) | 2020.10.11 |
| Lollipop의 backgroundTint는 버튼에 영향을주지 않습니다. (0) | 2020.10.11 |
| 이 응용 프로그램에는 / error에 대한 명시 적 매핑이 없습니다. (0) | 2020.10.11 |