WPF와 MVVM 문제를 배우려고 노력하고 있지만 걸림돌을 겪었습니다. 이 질문은이 질문과 비슷하지만 완전히 같지는 않습니다 (wpf-with-mvvm 처리-대화) …
MVVM 패턴을 사용하여 작성된 “로그인”양식이 있습니다.
이 양식에는 사용자 이름과 비밀번호를 보유한 ViewModel이 있으며, 이는 일반 데이터 바인딩을 사용하여 XAML의보기에 바인딩됩니다. 또한 일반 데이터 바인딩을 사용하여 폼에있는 “로그인”버튼에 바인딩 된 “로그인”명령이 있습니다.
“로그인”명령이 실행되면 ViewModel에서 기능을 호출하여 로그 오프하는 네트워크를 통해 데이터를 전송합니다.이 기능이 완료되면 2 가지 작업이 있습니다.
-
로그인이 잘못되었습니다-MessageBox 만 표시하면됩니다.
-
로그인이 유효합니다. 로그인 양식을 닫고 true로 리턴해야합니다
DialogResult
.
문제는 ViewModel이 실제 뷰에 대해 아무것도 모르므로 뷰를 닫고 특정 DialogResult를 반환하도록 어떻게 말할 수 있습니까? CodeBehind에 일부 코드를 붙일 수 있고 View를 ViewModel에 전달할 수는 있지만 MVVM의 요점을 완전히 물리 칠 것 같습니다 …
최신 정보
결국 나는 방금 MVVM 패턴의 “순도”를 위반하고 View가 Closed
이벤트를 게시 하고 Close
메소드를 공개하도록했습니다 . 그런 다음 ViewModel은을 호출 view.Close
합니다. 보기는 인터페이스를 통해서만 알려져 있으며 IOC 컨테이너를 통해 연결되므로 테스트 가능성이나 유지 관리 성이 손실되지 않습니다.
허용 된 답변이 -5 표인 것보다는 어리석은 것 같습니다! “순수한”상태에서 문제를 해결함으로써 얻는 좋은 감정을 잘 알고 있지만 확실히 한 줄 방법을 피하기 위해 200 줄의 이벤트, 명령 및 행동을 생각하는 유일한 사람은 아닙니다. “패턴”과 “순도”의 이름은 조금 말도 안됩니다 …
답변
나는 Thejuan의 대답 에서 영감을 얻어 더 단순한 부착물 을 작성했습니다. 스타일도없고 트리거도 없습니다. 대신, 당신은 이것을 할 수 있습니다 :
<Window ...
xmlns:xc="clr-namespace:ExCastle.Wpf"
xc:DialogCloser.DialogResult="{Binding DialogResult}">
이것은 WPF 팀이 올바르게 이해하고 DialogResult를 종속성 속성으로 만든 것처럼 거의 깨끗합니다. bool? DialogResult
ViewModel에 속성을 넣고 INotifyPropertyChanged를 구현하면 ViewModel 은 속성을 설정하여 창을 닫고 DialogResult를 설정할 수 있습니다. 그대로 MVVM.
DialogCloser의 코드는 다음과 같습니다.
using System.Windows;
namespace ExCastle.Wpf
{
public static class DialogCloser
{
public static readonly DependencyProperty DialogResultProperty =
DependencyProperty.RegisterAttached(
"DialogResult",
typeof(bool?),
typeof(DialogCloser),
new PropertyMetadata(DialogResultChanged));
private static void DialogResultChanged(
DependencyObject d,
DependencyPropertyChangedEventArgs e)
{
var window = d as Window;
if (window != null)
window.DialogResult = e.NewValue as bool?;
}
public static void SetDialogResult(Window target, bool? value)
{
target.SetValue(DialogResultProperty, value);
}
}
}
나는 또한 이것을 내 블로그에 게시했습니다 .
답변
내 관점에서 볼 때, 동일한 접근 방식이 “로그인”창뿐만 아니라 모든 종류의 창에 사용되기 때문에 질문은 꽤 좋습니다. 나는 많은 제안을 검토했지만 아무도 괜찮습니다. MVVM 디자인 패턴 기사 에서 가져온 제안을 검토 하십시오 .
각 ViewModel 클래스는 유형 WorkspaceViewModel
의 RequestClose
이벤트 및 CloseCommand
속성 이있는 클래스를 상속해야 ICommand
합니다. CloseCommand
속성 의 기본 구현은 RequestClose
이벤트를 발생시킵니다.
창을 닫으 OnLoaded
려면 창의 방법을 재정의해야합니다.
void CustomerWindow_Loaded(object sender, RoutedEventArgs e)
{
CustomerViewModel customer = CustomerViewModel.GetYourCustomer();
DataContext = customer;
customer.RequestClose += () => { Close(); };
}
또는 OnStartup
당신의 앱 방법 :
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
MainWindow window = new MainWindow();
var viewModel = new MainWindowViewModel();
viewModel.RequestClose += window.Close;
window.DataContext = viewModel;
window.Show();
}
나는 RequestClose
이벤트 및 CloseCommand
속성 구현 WorkspaceViewModel
이 매우 분명하다고 생각하지만 일관성이 있음을 보여줄 것입니다.
public abstract class WorkspaceViewModel : ViewModelBase
// There's nothing interesting in ViewModelBase as it only implements the INotifyPropertyChanged interface
{
RelayCommand _closeCommand;
public ICommand CloseCommand
{
get
{
if (_closeCommand == null)
{
_closeCommand = new RelayCommand(
param => Close(),
param => CanClose()
);
}
return _closeCommand;
}
}
public event Action RequestClose;
public virtual void Close()
{
if ( RequestClose != null )
{
RequestClose();
}
}
public virtual bool CanClose()
{
return true;
}
}
그리고 소스 코드 RelayCommand
:
public class RelayCommand : ICommand
{
#region Constructors
public RelayCommand(Action<object> execute, Predicate<object> canExecute)
{
if (execute == null)
throw new ArgumentNullException("execute");
_execute = execute;
_canExecute = canExecute;
}
#endregion // Constructors
#region ICommand Members
[DebuggerStepThrough]
public bool CanExecute(object parameter)
{
return _canExecute == null ? true : _canExecute(parameter);
}
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
public void Execute(object parameter)
{
_execute(parameter);
}
#endregion // ICommand Members
#region Fields
readonly Action<object> _execute;
readonly Predicate<object> _canExecute;
#endregion // Fields
}
추신 그 소스에 대해 나에게 나쁜 취급하지 마십시오! 어제 그들을 가지고 있다면 몇 시간을 절약 할 수있을 것입니다 …
PPS 의견이나 제안을 환영합니다.
답변
첨부 된 동작을 사용하여 창을 닫았습니다. ViewModel의 “signal”속성을 연결된 비헤이비어에 바인딩 (실제로 트리거를 사용합니다) true로 설정하면 비헤이비어가 창을 닫습니다.
http://adammills.wordpress.com/2009/07/01/window-close-from-xaml/
답변
여기에 MVVM의 장단점을 주장하는 의견이 많이 있습니다. 저에게는 Nir에 동의합니다. 패턴을 적절히 사용하는 문제이며 MVVM이 항상 적합하지는 않습니다. 사람들은 MVVM에 맞추기 위해 소프트웨어 디자인의 가장 중요한 원칙을 모두 기꺼이 희생하려고 한 것 같습니다.
즉, 귀하의 사례는 약간의 리팩토링에 적합하다고 생각합니다.
내가 접한 대부분의 경우 WPF를 사용하면 여러없이 사용할 수 있습니다 Window
. 아마도 s를 사용하는 Windows 대신 Frame
s와 Page
s를 사용해 볼 수 DialogResult
있습니다.
귀하의 경우 내 제안은 LoginFormViewModel
처리 할 수 있으며 LoginCommand
로그인이 유효하지 않은 경우 속성을 LoginFormViewModel
적절한 값 ( false
또는 같은 열거 형 값 UserAuthenticationStates.FailedAuthentication
)으로 설정하십시오. 성공적인 로그인 ( true
또는 다른 열거 형 값)에 대해서도 동일하게 수행합니다 . 그런 다음 DataTrigger
다양한 사용자 인증 상태에 응답하는를 사용하고 Setter
의 Source
속성 을 변경 하는 간단한 방법 을 사용할 수 있습니다 Frame
.
로그인 창을 반환하면 DialogResult
혼란 스러울 수 있습니다. 그것은 DialogResult
실제로 ViewModel의 속성입니다. 내 생각에 WPF에 대한 경험이 제한적이지만, WinForms에서 어떻게했는지에 대해 생각하고 있기 때문에 일반적으로 옳지 않은 느낌이들 때.
희망이 도움이됩니다.
답변
로그인 대화 상자가 생성되는 첫 번째 창이라고 가정하면 LoginViewModel 클래스에서 다음을 시도하십시오.
void OnLoginResponse(bool loginSucceded)
{
if (loginSucceded)
{
Window1 window = new Window1() { DataContext = new MainWindowViewModel() };
window.Show();
App.Current.MainWindow.Close();
App.Current.MainWindow = window;
}
else
{
LoginError = true;
}
}
답변
이것은 간단하고 깨끗한 솔루션입니다. ViewModel에 이벤트를 추가하고 해당 이벤트가 시작될 때 창을 닫도록 지시합니다.
자세한 내용은 내 블로그 게시물, ViewModel에서 창 닫기를 참조하십시오 .
XAML :
<Window
x:Name="this"
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
xmlns:ei="http://schemas.microsoft.com/expression/2010/interactions">
<i:Interaction.Triggers>
<i:EventTrigger SourceObject="{Binding}" EventName="Closed">
<ei:CallMethodAction
TargetObject="{Binding ElementName=this}"
MethodName="Close"/>
</i:EventTrigger>
</i:Interaction.Triggers>
<Window>
뷰 모델 :
private ICommand _SaveAndCloseCommand;
public ICommand SaveAndCloseCommand
{
get
{
return _SaveAndCloseCommand ??
(_SaveAndCloseCommand = new DelegateCommand(SaveAndClose));
}
}
private void SaveAndClose()
{
Save();
Close();
}
public event EventHandler Closed;
private void Close()
{
if (Closed != null) Closed(this, EventArgs.Empty);
}
참고 :이 예에서는 Prism을 사용 하지만 DelegateCommand
( Prism : Commanding 참조 ) 해당 ICommand
구현에 모든 구현을 사용할 수 있습니다.
이 공식 패키지 에서 동작을 사용할 수 있습니다 .
답변
내가 처리하는 방법은 내 ViewModel에 이벤트 핸들러를 추가하는 것입니다. 사용자가 성공적으로 로그인하면 이벤트가 발생합니다. 내보기 에서이 이벤트에 첨부하고 이벤트가 발생하면 창을 닫습니다.