View Model이 양식을 닫는 방법을 선택합니다.
저는 WPF와 MVVM 문제를 배우려고 하지만 난관에 부딪혔습니다.이 질문은 유사하지만 이 질문과 완전히 같지는 않습니다(handling-dialogs-in-wpf-with-mvm)...
MVVM 패턴을 사용하여 작성된 "로그인" 양식이 있습니다.
이 양식에는 일반 데이터 바인딩을 사용하여 XAML의 보기에 바인딩된 사용자 이름 및 암호를 보유하는 View Model이 있습니다.또한 일반 데이터 바인딩을 사용하여 양식의 "로그인" 단추에 바인딩된 "로그인" 명령도 있습니다.
로그인 명령이 실행되면 View 모델에서 꺼지고 네트워크를 통해 데이터를 전송하여 로그인하는 기능을 호출합니다.이 기능이 완료되면 다음 두 가지 작업이 수행됩니다.
로그인이 잘못되었습니다. MessageBox만 표시하면 됩니다.
합니다. "로그인"으로 . 로그인 양식을 닫고 true로 반환해야 합니다.
DialogResult
...
문제는 뷰 모델이 실제 뷰에 대해 아무것도 모른다는 것입니다. 그러면 뷰를 닫고 특정 Dialog Result를 반환하도록 지시하는 방법은 무엇입니까?코드 뒤에 코드를 삽입하거나 View 모델에 View를 전달할 수 있지만 MVVM의 전체 포인트를 완전히 무시할 수 있습니다.
갱신하다
에 "MVVM" "view"를 .Closed
이트벤, 출노를 노출하기Close
그러면 이 View Model을 호출합니다.view.Close
보기는 인터페이스를 통해서만 알 수 있고 IOC 컨테이너를 통해 연결되므로 테스트 가능성이나 유지관리 가능성이 손실되지 않습니다.
승인된 답이 -5표라는 것은 다소 바보같이 보입니다!'순수'하면서 문제를 풀면 얻는 좋은 감정은 잘 알고 있지만, '패턴'과 '순수'라는 이름으로 한 줄의 방법을 피하기 위해 사건과 명령, 행동을 200줄로 늘어놓는 것이 좀 우스꽝스럽다고 생각하는 사람은 나뿐만이 아닐 것입니다.
더 간단한 부속 부동산을 쓰라는 테주안의 대답에 영감을 받았습니다.스타일이나 트리거 없이 다음 작업만 수행할 수 있습니다.
<Window ...
xmlns:xc="clr-namespace:ExCastle.Wpf"
xc:DialogCloser.DialogResult="{Binding DialogResult}">
이는 WPF 팀이 이 문제를 해결하고 DialogResult를 종속성 속성으로 만든 것과 거의 유사합니다. 냥그a를 .bool? DialogResult
View INOTIF 성의 , 을 닫을 수 할 수 있습니다).속성을 설정하는 것만으로 뷰 모델이 창을 닫고 대화상자 결과를 설정할 수 있습니다.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
이벤트
, 창을닫으면려문,면▁the,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
}
추신. 그런 출처 때문에 저를 나쁘게 취급하지 마세요!내가 어제 그것들을 가지고 있었다면 몇 시간을 절약했을 것입니다.
추신: 어떤 의견이나 제안도 환영합니다.
여기 MVVM의 장단점을 따지는 댓글이 많습니다.저는 Nir의 의견에 동의합니다. 패턴을 적절하게 사용하는 것이 문제이며 MVVM이 항상 적합하지는 않습니다.사람들은 MVVM에 맞추기 위해 소프트웨어 설계의 가장 중요한 원칙을 기꺼이 희생하게 된 것 같습니다.
그렇긴 하지만, 당신의 경우는 약간의 리팩터링과 잘 맞을 수 있을 것 같습니다.
제가, 개의 를 사용하지 않고도 할 수 .Window
아마도 당신은 사용해 볼 수 있을 것입니다.Frame
모래땅Page
를 사 윈대신즈도우가 있는 DialogResult
s의
당신의 경우, 제 제안은 다음과 같습니다.LoginFormViewModel
을다를 LoginCommand
유효하지 않은 에는 그인이잘경설속다정니합에 합니다.LoginFormViewModel
적절한 값으로(false
또같 열값형거와 같은 .UserAuthenticationStates.FailedAuthentication
. 성공적인 로그인에도 동일한 작업을 수행할 수 있습니다).true
또는 일부 다른 열거값).그러면 다음을 사용합니다.DataTrigger
인증 사용자 인증 상태를 할 수 .Setter
를 Source
의 Frame
.
로그인 창에서 a를 반환하도록 하는 중DialogResult
요; 그 나네혼러하워부는생고라다각니;합것그는이분가란스▁i그;것▁where;▁that.DialogResult
View 모델의 속성입니다.WPF에 대한 제한적인 경험을 바탕으로 WinForms에서 동일한 작업을 수행할 수 있었는지에 대해 생각하고 있기 때문에 WPF에 대한 경험이 부족한 경우가 많습니다.
도움이 되길 바랍니다.
로그인 대화 상자가 처음 생성되는 창이라고 가정하면 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;
}
}
이는 간단하고 깨끗한 솔루션입니다. 이벤트를 View Model에 추가하고 이벤트가 발생할 때 창을 닫도록 지시합니다.
자세한 내용은 블로그 게시물인 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);
}
참: 예에는프의즘리를 합니다.DelegateCommand
(프리즘: 명령 참조), 그러나 그 문제에 대해서는 어떠한 구현도 사용할 수 있습니다.
이 공식 패키지의 동작을 사용할 수 있습니다.
이를 처리하는 방법은 View Model에 이벤트 핸들러를 추가하는 것입니다.사용자가 성공적으로 로그인하면 이벤트를 실행합니다.제가 보기에는 이 이벤트에 첨부하고 이벤트가 시작되면 창을 닫습니다.
여기 제가 처음에 했던 것이 있습니다. 효과가 있지만, 다소 장황하고 추해 보입니다(글로벌 정적인 것은 결코 좋지 않습니다).
1: App.xaml.cs
public partial class App : Application
{
// create a new global custom WPF Command
public static readonly RoutedUICommand LoggedIn = new RoutedUICommand();
}
2: LoginForm.xaml
// bind the global command to a local eventhandler
<CommandBinding Command="client:App.LoggedIn" Executed="OnLoggedIn" />
3: LoginForm.xaml.cs
// implement the local eventhandler in codebehind
private void OnLoggedIn( object sender, ExecutedRoutedEventArgs e )
{
DialogResult = true;
Close();
}
4: LoginFormViewModel.cs
// fire the global command from the viewmodel
private void OnRemoteServerReturnedSuccess()
{
App.LoggedIn.Execute(this, null);
}
나는 나중에 이 코드를 모두 제거했고, 그냥.LoginFormViewModel
보기에서 닫기 메서드를 호출합니다.그것은 결국 훨씬 더 친절하고 따라하기 쉬워졌습니다.IMHO 패턴의 요점은 사람들에게 당신의 앱이 무엇을 하는지 더 쉽게 이해할 수 있는 방법을 제공하는 것입니다. 이 경우 MVVM은 제가 사용하지 않은 경우보다 훨씬 더 이해하기 어렵게 만들었고, 이제는 패턴에 반대하게 되었습니다.
좋아요, 이 질문은 거의 6년이 지났고 아직도 여기서 제가 생각하는 적절한 답을 찾을 수가 없어요. 그러니 제 "2센트"를 공유하겠습니다.
저는 사실 두 가지 방법이 있습니다. 첫 번째 방법은 간단한 것입니다.두 번째는 오른쪽에 있습니다. 따라서 올바른 것을 찾고 있다면 #1을 건너뛰고 #2로 점프하십시오.
빠르고 간편(완전하지는 않음)
작은 프로젝트만 있는 경우에는 보기 모델에서 창 닫기 작업을 만들기도 합니다.
public Action CloseWindow { get; set; } // In MyViewModel.cs
그리고 보기를 만드는 사람이나 뒤에 있는 보기의 코드에서 작업이 호출할 메소드를 설정합니다.
(MVVM은 View와 View Model의 분리에 관한 것입니다...올바른 분리가 있는 한 패턴을 위반하지 않는 것은 여전히 보기의 코드입니다.)
일부 View Model이 새 창을 만드는 경우:
private void CreateNewView()
{
MyView window = new MyView();
window.DataContext = new MyViewModel
{
CloseWindow = window.Close,
};
window.ShowDialog();
}
또는 기본 창에 원하는 경우 View의 생성자 아래에 배치합니다.
public MyView()
{
InitializeComponent();
this.DataContext = new MainViewModel
{
CloseWindow = this.Close
};
}
창을 닫으려면 View Model에서 Action(작업)을 호출합니다.
올바른 방법
이제 적절한 방법은 프리즘(IMHO)을 사용하는 것이며, 여기에서 모든 것을 찾을 수 있습니다.
상호 작용 요청을 만들고, 새 창에 필요한 모든 데이터를 채우고, 점심을 먹고, 닫고, 데이터를 다시 받을 수 있습니다.이 모든 것이 캡슐화되고 MVVM이 승인되었습니다.사용자가 창을 닫은 방법에 대한 상태도 표시됩니다.Canceled
또는Accepted
(확인 버튼) 필요한 경우 창 및 데이터를 반환합니다.이것은 좀 더 복잡하고 #1 답이지만 훨씬 더 완성도가 높고 Microsoft의 권장 패턴입니다.
제가 제공한 링크에는 코드 스니펫과 예제가 모두 포함되어 있으므로 여기에 코드를 넣을 필요 없이 프리즘 빠른 시작을 다운로드하고 실행하십시오. 작동하려면 조금 더 자세히 설명하는 것이 정말 간단하지만, 창을 닫는 것보다 더 큰 이점이 있습니다.
public partial class MyWindow: Window
{
public ApplicationSelection()
{
InitializeComponent();
MyViewModel viewModel = new MyViewModel();
DataContext = viewModel;
viewModel.RequestClose += () => { Close(); };
}
}
public class MyViewModel
{
//...Your code...
public event Action RequestClose;
public virtual void Close()
{
if (RequestClose != null)
{
RequestClose();
}
}
public void SomeFunction()
{
//...Do something...
Close();
}
}
참고로, 저는 같은 문제에 부딪혔고, 비록 그것이 최선의 답은 아닐지라도, 글로벌이나 통계를 필요로 하지 않는 작품을 발견했다고 생각합니다.당신들이 알아서 결정하도록 놔뒀습니다.
이 경우 표시할 창을 인스턴스화하는 ViewModel(ViewModelMain이라고 함)도 LoginFormViewModel에 대해 알고 있습니다(위의 상황을 예로 사용).
그래서 LoginFormView 모델에서 I 명령 유형의 속성을 만들었습니다(CloseWindowCommand라고 부릅니다).그럼, 전화하기 전에.창에 ShowDialog()를 표시하고, LoginFormViewModel의 CloseWindowCommand 속성을 창에 설정했습니다.인스턴스화된 Window I의 Close() 메서드입니다.그런 다음 LoginFormView 모델에서 CloseWindowCommand를 호출하기만 하면 됩니다.창을 닫으려면()을 실행합니다.
약간의 해결책/해킹이지만 MVVM 패턴을 깨지 않고도 잘 작동합니다.
얼마든지 이 과정을 비판해 보세요, 참을 수 있어요! :)
이것은 아마도 매우 늦었을 것입니다. 하지만 저는 같은 문제를 발견했고 저에게 맞는 해결책을 찾았습니다.
대화 상자가 없는 앱을 만드는 방법을 알 수 없습니다(마인드 블록일 수도 있습니다).그래서 저는 MVVM과 교착 상태에 빠졌고 대화상자를 보여주었습니다.그래서 저는 코드 프로젝트 기사를 접했습니다.
http://www.codeproject.com/KB/WPF/XAMLDialog.aspx
기본적으로 창이 다른 창의 시각적 트리 내에 있도록 허용하는 사용자 컨트롤입니다(xaml에서는 허용되지 않음).또한 IsShowing이라는 부울 종속성 속성도 표시합니다.
일반적으로!= null 컨트롤의 Content 속성이 트리거를 통해 표시될 때마다 기본적으로 대화 상자를 표시하는 다음과 같은 유형을 리소스 사전에서 설정할 수 있습니다.
<Style TargetType="{x:Type d:Dialog}">
<Style.Triggers>
<Trigger Property="HasContent" Value="True">
<Setter Property="Showing" Value="True" />
</Trigger>
</Style.Triggers>
</Style>
대화상자를 표시할 보기에서 다음과 같이 설정합니다.
<d:Dialog Content="{Binding Path=DialogViewModel}"/>
또한 ViewModel에서 속성을 값으로 설정하기만 하면 됩니다(참고: ViewModel 클래스는 INNotify를 지원해야 함).속성이 변경되어 어떤 일이 발생했음을 알 수 있습니다.
이와 같이:
DialogViewModel = new DisplayViewModel();
View Model을 View와 일치시키려면 리소스 사전에 다음과 같은 것이 있어야 합니다.
<DataTemplate DataType="{x:Type vm:DisplayViewModel}">
<vw:DisplayView/>
</DataTemplate>
이 모든 것을 통해 대화상자를 표시할 한 줄 코드를 얻을 수 있습니다.문제는 위의 코드만으로는 대화상자를 닫을 수 없다는 것입니다.따라서 DisplayViewModel이 상속하는 ViewModel 기본 클래스에 이벤트를 넣고 위의 코드 대신 다음과 같이 기록해야 합니다.
var vm = new DisplayViewModel();
vm.RequestClose += new RequestCloseHandler(DisplayViewModel_RequestClose);
DialogViewModel = vm;
그런 다음 콜백을 통해 대화 상자의 결과를 처리할 수 있습니다.
이것은 조금 복잡해 보일 수 있지만, 일단 기초가 마련되면, 꽤 간단합니다.다시 한 번 이것이 제 구현입니다, 다른 것들이 있을 것이라고 확신합니다 :)
이게 도움이 됐으면 좋겠네요, 절 구했어요.
View 모델에서 View가 등록되는 이벤트를 노출하도록 할 수 있습니다.그런 다음 보기 모델이 보기를 닫을 시간을 결정하면 해당 이벤트가 실행되어 보기가 닫힙니다.특정 결과 값을 반환하려면 View Model에 이에 대한 속성이 있어야 합니다.
명령 매개 변수로 창을 전달하는 것이 어떻습니까?
C#:
private void Cancel( Window window )
{
window.Close();
}
private ICommand _cancelCommand;
public ICommand CancelCommand
{
get
{
return _cancelCommand ?? ( _cancelCommand = new Command.RelayCommand<Window>(
( window ) => Cancel( window ),
( window ) => ( true ) ) );
}
}
XAML:
<Window x:Class="WPFRunApp.MainWindow"
x:Name="_runWindow"
...
<Button Content="Cancel"
Command="{Binding Path=CancelCommand}"
CommandParameter="{Binding ElementName=_runWindow}" />
엄청난 수의 답변에 추가하기 위해 다음과 같은 내용을 추가하고자 합니다.View Model에 IC 명령이 있고 해당 명령이 창을 닫거나 해당 작업을 수행할 것이라고 가정하면 다음과 같은 방법을 사용할 수 있습니다.
var windows = Application.Current.Windows;
for (var i=0;i< windows.Count;i++ )
if (windows[i].DataContext == this)
windows[i].Close();
이 솔루션은 완벽하지 않고 테스트하기 어려울 수도 있지만(정적을 모방하거나 고정하는 것이 어렵기 때문에) 다른 솔루션보다 더 깨끗합니다(IMHO).
에릭
Joe White의 솔루션을 구현했지만 가끔 "DialogResult는 Window가 생성되어 대화 상자로 표시된 후에만 설정할 수 있습니다" 오류가 발생했습니다.
View가 닫힌 후에도 View 모델을 유지하다가 나중에 동일한 VM을 사용하여 새 View를 열기도 했습니다.이전 보기가 가비지 수집되기 전에 새 보기를 닫으면 닫힌 창에서 DialogResultChanged가 DialogResult 속성을 설정하려고 시도하여 오류가 발생한 것 같습니다.
제 해결책은 DialogResultChanged를 변경하여 창의 IsLoaded 속성을 확인하는 것이었습니다.
private static void DialogResultChanged(
DependencyObject d,
DependencyPropertyChangedEventArgs e)
{
var window = d as Window;
if (window != null && window.IsLoaded)
window.DialogResult = e.NewValue as bool?;
}
이렇게 변경한 후에는 닫힌 대화상자에 대한 첨부 파일이 무시됩니다.
저는 프로그래밍 방식으로 만들어진 창에서 사용자 컨트롤을 보여줘야 했기 때문에 결국 조 화이트의 대답과 아담 밀스의 대답에서 나온 코드를 섞었습니다.따라서 DialogCloser는 창에 있을 필요가 없으며 사용자 제어 자체에 있을 수 있습니다.
<UserControl ...
xmlns:xw="clr-namespace:Wpf"
xw:DialogCloser.DialogResult="{Binding DialogResult}">
또한 DialogCloser는 창 자체에 연결되지 않은 경우 사용자 컨트롤의 창을 찾습니다.
namespace 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.GetWindow();
if (window != null)
window.DialogResult = e.NewValue as bool?;
}
public static void SetDialogResult(DependencyObject target, bool? value)
{
target.SetValue(DialogResultProperty, value);
}
}
public static class Extensions
{
public static Window GetWindow(this DependencyObject sender_)
{
Window window = sender_ as Window;
return window ?? Window.GetWindow( sender_ );
}
}
}
행동이 여기서 가장 편리한 방법입니다.
한 손에서 해당 뷰 모델에 바인딩할 수 있습니다("폼 닫기!" 신호를 보낼 수 있음).
다른 방법으로는 필요한 양식별 이벤트나 확인 대화 상자 표시 또는 기타 모든 이벤트에 가입할 수 있도록 양식 자체에 액세스할 수 있습니다.
필요한 행동을 쓰는 것은 아주 처음에는 지루하게 보일 수 있습니다.그러나 이제부터는 정확한 한 줄 XAML 스니펫으로 필요한 모든 양식에서 재사용할 수 있습니다.필요한 경우 별도의 어셈블리로 추출하여 원하는 다음 프로젝트에 포함할 수 있습니다.
또 다른 솔루션은 INOTIFY를 사용하여 속성을 생성하는 것입니다.DialogResult와 같이 뷰 모델에서 변경된 속성을 코드 뒤에 다음과 같이 기록합니다.
public class SomeWindow: ChildWindow
{
private SomeViewModel _someViewModel;
public SomeWindow()
{
InitializeComponent();
this.Loaded += SomeWindow_Loaded;
this.Closed += SomeWindow_Closed;
}
void SomeWindow_Loaded(object sender, RoutedEventArgs e)
{
_someViewModel = this.DataContext as SomeViewModel;
_someViewModel.PropertyChanged += _someViewModel_PropertyChanged;
}
void SomeWindow_Closed(object sender, System.EventArgs e)
{
_someViewModel.PropertyChanged -= _someViewModel_PropertyChanged;
this.Loaded -= SomeWindow_Loaded;
this.Closed -= SomeWindow_Closed;
}
void _someViewModel_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == SomeViewModel.DialogResultPropertyName)
{
this.DialogResult = _someViewModel.DialogResult;
}
}
}
은 가장중조각은입니다._someViewModel_PropertyChanged
.DialogResultPropertyName
제약조수있다니습건일에 있는 일부 공용 일 수 .SomeViewModel
.
View Model에서 수행하기 어려운 경우를 대비하여 View Control을 몇 가지 변경하기 위해 이러한 방법을 사용합니다.View Model에서 변경된 특성에서는 View에서 원하는 모든 작업을 수행할 수 있습니다.ViewModel은 여전히 'unit testable'이며 코드 뒤에 있는 일부 작은 줄의 코드는 아무런 차이가 없습니다.
저는 이 길을 갈 것입니다.
using GalaSoft.MvvmLight;
using GalaSoft.MvvmLight.Command;
using GalaSoft.MvvmLight.Messaging;
// View
public partial class TestCloseWindow : Window
{
public TestCloseWindow() {
InitializeComponent();
Messenger.Default.Register<CloseWindowMsg>(this, (msg) => Close());
}
}
// View Model
public class MainViewModel: ViewModelBase
{
ICommand _closeChildWindowCommand;
public ICommand CloseChildWindowCommand {
get {
return _closeChildWindowCommand?? (_closeChildWindowCommand = new RelayCommand(() => {
Messenger.Default.Send(new CloseWindowMsg());
}));
}
}
}
public class CloseWindowMsg
{
}
저는 모든 답을 읽었지만, 대부분은 충분하지 않거나 더 나쁘다고 말해야 합니다.
대화 상자 창을 표시하고 대화 상자 결과를 반환하는 DialogService 클래스를 사용하여 이 작업을 적절하게 처리할 수 있습니다.구현 및 사용법을 보여주는 샘플 프로젝트를 만들었습니다.
가장 중요한 부분은 다음과 같습니다.
//we will call this interface in our viewmodels
public interface IDialogService
{
bool? ShowDialog(object dialogViewModel, string caption);
}
//we need to display logindialog from mainwindow
public class MainWindowViewModel : ViewModelBase
{
public string Message {get; set;}
public void ShowLoginCommandExecute()
{
var loginViewModel = new LoginViewModel();
var dialogResult = this.DialogService.ShowDialog(loginViewModel, "Please, log in");
//after dialog is closed, do someting
if (dialogResult == true && loginViewModel.IsLoginSuccessful)
{
this.Message = string.Format("Hello, {0}!", loginViewModel.Username);
}
}
}
public class DialogService : IDialogService
{
public bool? ShowDialog(object dialogViewModel, string caption)
{
var contentView = ViewLocator.GetView(dialogViewModel);
var dlg = new DialogWindow
{
Title = caption
};
dlg.PART_ContentControl.Content = contentView;
return dlg.ShowDialog();
}
}
이벤트 애그리게이터나 다른 유사한 솔루션보다 더 간단하고, 읽기 쉽고, 마지막이지만 디버깅하기가 더 어렵지 않습니까?
보시는 바와 같이, 제가 보기 모델에서는 여기 게시물에 설명된 ViewModel 첫 번째 접근 방식을 사용했습니다. WPF에서 ViewModel을 호출하는 모범 사례
에서는, 론물현, 세는서에계실▁of▁world실세,,DialogService.ShowDialog
대화 상자를 구성하려면 더 많은 옵션(예: 실행해야 하는 단추 및 명령)이 있어야 합니다. :) 법은다방범벗어납니다 :)
보기 모델을 사용하여 이 작업을 수행하는 방법에 대한 질문에는 대답하지 않지만 XAML + 혼합 SDK만 사용하여 수행하는 방법을 보여줍니다.
저는 블렌드 SDK에서 두 개의 파일을 다운로드하여 사용하기로 결정했는데, 두 파일 모두 Microsoft에서 NuGet을 통해 패키지로 사용할 수 있습니다.파일은 다음과 같습니다.
시스템. 윈도우.Interactivity.dll 및 마이크로소프트.표현.Interactions.dll
마이크로소프트.표현.Interactions.dll은 뷰 모델이나 다른 대상에서 속성을 설정하거나 메서드를 호출하는 기능과 같은 기능을 제공하며 내부에 다른 위젯도 있습니다.
일부 XAML:
<Window x:Class="Blah.Blah.MyWindow"
...
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
xmlns:ei="http://schemas.microsoft.com/expression/2010/interactions"
...>
<StackPanel>
<Button x:Name="OKButton" Content="OK">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Click">
<ei:ChangePropertyAction
TargetObject="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}}"
PropertyName="DialogResult"
Value="True"
IsEnabled="{Binding SomeBoolOnTheVM}" />
</i:EventTrigger>
</Button>
<Button x:Name="CancelButton" Content="Cancel">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Click">
<ei:ChangePropertyAction
TargetObject="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}}"
PropertyName="DialogResult"
Value="False" />
</i:EventTrigger>
</Button>
<Button x:Name="CloseButton" Content="Close">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Click">
<!-- method being invoked should be void w/ no args -->
<ei:CallMethodAction
TargetObject="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}}"
MethodName="Close" />
</i:EventTrigger>
</i:Interaction.Triggers>
</Button>
<StackPanel>
</Window>
단순 확인/취소 동작으로 이동하는 경우 창이 창과 함께 표시되는 한 기본값 및 취소 여부 속성을 사용하여 이동할 수 있습니다.ShowDialog().
개인적으로 IsDefault 속성이 true로 설정된 단추에 문제가 있었지만 페이지가 로드될 때 숨겨졌습니다.보여준 후에 보기 좋게 재생하고 싶지 않은 것 같아서 그냥 창을 설정하는 것입니다.대신 위에 표시된 DialogResult 속성이 사용됩니다.
여기 간단한 버그 없는 솔루션(소스 코드 포함)이 있습니다. 저에게 효과가 있습니다.
에서 View 도출
INotifyPropertyChanged
ViewModel에서 관찰 가능한 특성 CloseDialog 생성
public void Execute() { // Do your task here // if task successful, assign true to CloseDialog CloseDialog = true; } private bool _closeDialog; public bool CloseDialog { get { return _closeDialog; } set { _closeDialog = value; OnPropertyChanged(); } } public event PropertyChangedEventHandler PropertyChanged; private void OnPropertyChanged([CallerMemberName]string property = "") { if (PropertyChanged != null) { PropertyChanged(this, new PropertyChangedEventArgs(property)); } }
}
이 속성 변경에 대해 보기에서 처리기 연결
_loginDialogViewModel = new LoginDialogViewModel(); loginPanel.DataContext = _loginDialogViewModel; _loginDialogViewModel.PropertyChanged += OnPropertyChanged;
이제 거의 끝나가네요.에서 " 트이핸러만들기들벤"를 .
DialogResult = true
protected void OnPropertyChanged(object sender, PropertyChangedEventArgs args) { if (args.PropertyName == "CloseDialog") { DialogResult = true; } }
/any에서 를 만듭니다.UserControl
(또는)Window
종료하려는 경우).아래와 같이:
public bool CloseTrigger
{
get { return (bool)GetValue(CloseTriggerProperty); }
set { SetValue(CloseTriggerProperty, value); }
}
public static readonly DependencyProperty CloseTriggerProperty =
DependencyProperty.Register("CloseTrigger", typeof(bool), typeof(ControlEventBase), new PropertyMetadata(new PropertyChangedCallback(OnCloseTriggerChanged)));
private static void OnCloseTriggerChanged(DependencyObject dp, DependencyPropertyChangedEventArgs e)
{
//write Window Exit Code
}
ViewModel의 속성에서 바인딩합니다.
<Window x:Class="WpfStackOverflowTempProject.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Width="525"
CloseTrigger="{Binding Path=CloseWindow,Mode=TwoWay}"
속성 위치:
private bool closeWindow;
public bool CloseWindow
{
get { return closeWindow; }
set
{
closeWindow = value;
RaiseChane("CloseWindow");
}
}
이제 View Model에서 값을 변경하여 닫기 작업을 트리거합니다.:)
창을 닫아야 하는 경우 보기 모델에 다음과 같이 입력하면 됩니다.
짜잔
foreach (Window window in Application.Current.Windows)
{
if (window.DataContext == this)
{
window.Close();
return;
}
}
Application.Current.MainWindow.Close()
그만해요!
언급URL : https://stackoverflow.com/questions/501886/how-should-the-viewmodel-close-the-form
'programing' 카테고리의 다른 글
POI 라이브러리를 사용하여 Excel 파일의 행 수를 가져오는 방법은 무엇입니까? (0) | 2023.05.09 |
---|---|
postgre에서 왼쪽으로 0 채우기SQL (0) | 2023.05.04 |
WPF 및 초기 초점 (0) | 2023.05.04 |
django.db.dll 파일ProgrammingError: 관계가 이미 있습니다. (0) | 2023.05.04 |
xcode에서 iOS App 아카이브를 생성할 수 없습니다. (0) | 2023.05.04 |