MVVM을 사용하여 wpf의 대화 상자에 대한 좋은 습관 또는 나쁜 습관?
최근에 wpf 앱에 대한 추가 및 편집 대화 상자를 만드는 데 문제가있었습니다.
내 코드에서하고 싶은 것은 이와 같은 것입니다. (주로 mvvm과 함께 viewmodel 첫 번째 접근 방식을 사용합니다)
대화창을 호출하는 ViewModel :
var result = this.uiDialogService.ShowDialog("Dialogwindow Title", dialogwindowVM);
// Do anything with the dialog result
어떻게 작동합니까?
먼저 대화 서비스를 만들었습니다.
public interface IUIWindowDialogService
{
bool? ShowDialog(string title, object datacontext);
}
public class WpfUIWindowDialogService : IUIWindowDialogService
{
public bool? ShowDialog(string title, object datacontext)
{
var win = new WindowDialog();
win.Title = title;
win.DataContext = datacontext;
return win.ShowDialog();
}
}
WindowDialog
특별하지만 간단한 창입니다. 내 콘텐츠를 보유하려면 필요합니다.
<Window x:Class="WindowDialog"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
Title="WindowDialog"
WindowStyle="SingleBorderWindow"
WindowStartupLocation="CenterOwner" SizeToContent="WidthAndHeight">
<ContentPresenter x:Name="DialogPresenter" Content="{Binding .}">
</ContentPresenter>
</Window>
wpf의 대화 상자 문제 dialogresult = true
는 코드에서만 가능하다는 것입니다. 그렇기 때문에 dialogviewmodel
구현할 인터페이스를 만들었 습니다.
public class RequestCloseDialogEventArgs : EventArgs
{
public bool DialogResult { get; set; }
public RequestCloseDialogEventArgs(bool dialogresult)
{
this.DialogResult = dialogresult;
}
}
public interface IDialogResultVMHelper
{
event EventHandler<RequestCloseDialogEventArgs> RequestCloseDialog;
}
내 ViewModel이 시간이라고 생각할 때마다이 dialogresult = true
이벤트를 발생시킵니다.
public partial class DialogWindow : Window
{
// Note: If the window is closed, it has no DialogResult
private bool _isClosed = false;
public DialogWindow()
{
InitializeComponent();
this.DialogPresenter.DataContextChanged += DialogPresenterDataContextChanged;
this.Closed += DialogWindowClosed;
}
void DialogWindowClosed(object sender, EventArgs e)
{
this._isClosed = true;
}
private void DialogPresenterDataContextChanged(object sender,
DependencyPropertyChangedEventArgs e)
{
var d = e.NewValue as IDialogResultVMHelper;
if (d == null)
return;
d.RequestCloseDialog += new EventHandler<RequestCloseDialogEventArgs>
(DialogResultTrueEvent).MakeWeak(
eh => d.RequestCloseDialog -= eh;);
}
private void DialogResultTrueEvent(object sender,
RequestCloseDialogEventArgs eventargs)
{
// Important: Do not set DialogResult for a closed window
// GC clears windows anyways and with MakeWeak it
// closes out with IDialogResultVMHelper
if(_isClosed) return;
this.DialogResult = eventargs.DialogResult;
}
}
이제 적어도 DataTemplate
내 리소스 파일 ( app.xaml
또는 무언가) 을 만들어야합니다 .
<DataTemplate DataType="{x:Type DialogViewModel:EditOrNewAuswahlItemVM}" >
<DialogView:EditOrNewAuswahlItem/>
</DataTemplate>
자, 이제 뷰 모델에서 대화 상자를 호출 할 수 있습니다.
var result = this.uiDialogService.ShowDialog("Dialogwindow Title", dialogwindowVM);
이제 내 질문,이 솔루션에 문제가 있습니까?
편집 : 완전성을 위해. ViewModel은 구현해야 IDialogResultVMHelper
하며 다음과 같이 만들 수 있습니다 OkCommand
.
public class MyViewmodel : IDialogResultVMHelper
{
private readonly Lazy<DelegateCommand> _okCommand;
public MyViewmodel()
{
this._okCommand = new Lazy<DelegateCommand>(() =>
new DelegateCommand(() =>
InvokeRequestCloseDialog(
new RequestCloseDialogEventArgs(true)), () =>
YourConditionsGoesHere = true));
}
public ICommand OkCommand
{
get { return this._okCommand.Value; }
}
public event EventHandler<RequestCloseDialogEventArgs> RequestCloseDialog;
private void InvokeRequestCloseDialog(RequestCloseDialogEventArgs e)
{
var handler = RequestCloseDialog;
if (handler != null)
handler(this, e);
}
}
편집 2 : 여기에서 코드를 사용하여 EventHandler 레지스터를 약하게 만들었습니다 :
http://diditwith.net/2007/03/23/SolvingTheProblemWithEventsWeakEventHandlers.aspx
(웹 사이트가 더 이상 존재하지 않음, WebArchive Mirror )
public delegate void UnregisterCallback<TE>(EventHandler<TE> eventHandler)
where TE : EventArgs;
public interface IWeakEventHandler<TE>
where TE : EventArgs
{
EventHandler<TE> Handler { get; }
}
public class WeakEventHandler<T, TE> : IWeakEventHandler<TE>
where T : class
where TE : EventArgs
{
private delegate void OpenEventHandler(T @this, object sender, TE e);
private readonly WeakReference mTargetRef;
private readonly OpenEventHandler mOpenHandler;
private readonly EventHandler<TE> mHandler;
private UnregisterCallback<TE> mUnregister;
public WeakEventHandler(EventHandler<TE> eventHandler,
UnregisterCallback<TE> unregister)
{
mTargetRef = new WeakReference(eventHandler.Target);
mOpenHandler = (OpenEventHandler)Delegate.CreateDelegate(
typeof(OpenEventHandler),null, eventHandler.Method);
mHandler = Invoke;
mUnregister = unregister;
}
public void Invoke(object sender, TE e)
{
T target = (T)mTargetRef.Target;
if (target != null)
mOpenHandler.Invoke(target, sender, e);
else if (mUnregister != null)
{
mUnregister(mHandler);
mUnregister = null;
}
}
public EventHandler<TE> Handler
{
get { return mHandler; }
}
public static implicit operator EventHandler<TE>(WeakEventHandler<T, TE> weh)
{
return weh.mHandler;
}
}
public static class EventHandlerUtils
{
public static EventHandler<TE> MakeWeak<TE>(this EventHandler<TE> eventHandler,
UnregisterCallback<TE> unregister)
where TE : EventArgs
{
if (eventHandler == null)
throw new ArgumentNullException("eventHandler");
if (eventHandler.Method.IsStatic || eventHandler.Target == null)
throw new ArgumentException("Only instance methods are supported.",
"eventHandler");
var wehType = typeof(WeakEventHandler<,>).MakeGenericType(
eventHandler.Method.DeclaringType, typeof(TE));
var wehConstructor = wehType.GetConstructor(new Type[]
{
typeof(EventHandler<TE>), typeof(UnregisterCallback<TE>)
});
IWeakEventHandler<TE> weh = (IWeakEventHandler<TE>)wehConstructor.Invoke(
new object[] { eventHandler, unregister });
return weh.Handler;
}
}
이것은 좋은 접근 방법이며 과거에도 비슷한 방법을 사용했습니다. 해봐!
내가 분명히 할 한 가지 작은 일은 DialogResult에서 "false"를 설정해야 할 때 이벤트가 부울을 받도록하는 것입니다.
event EventHandler<RequestCloseEventArgs> RequestCloseDialog;
그리고 EventArgs 클래스 :
public class RequestCloseEventArgs : EventArgs
{
public RequestCloseEventArgs(bool dialogResult)
{
this.DialogResult = dialogResult;
}
public bool DialogResult { get; private set; }
}
나는 몇 달 동안 거의 동일한 접근법을 사용 해 왔으며 매우 만족합니다 (즉, 아직 완전히 다시 작성하려는 충동을 느끼지 못했습니다 ...)
In my implementation, I use a IDialogViewModel
that exposes things such as the title, the standad buttons to show (in order to have a consistent apparence across all dialogs), a RequestClose
event, and a few other things to be able to control the window size and behavior
If you are talking about dialogue windows and not just about the pop-up message boxes, please consider my approach below. The key points are:
- I pass a reference to
Module Controller
into the constructor of eachViewModel
(you can use injection). - That
Module Controller
has public/internal methods for creating dialogue windows (just creating, without returning a result). Hence to open a dialogue window inViewModel
I write:controller.OpenDialogEntity(bla, bla...)
- Each dialogue window notifies about its result (like OK, Save, Cancel, etc.) via Weak Events. If you use PRISM, then it's easier to publish notifications using this EventAggregator.
- To handle dialogue results, I'm using subscription to notifications (again Weak Events and EventAggregator in case of PRISM). To reduce dependency on such notifications, use independent classes with standard notifications.
Pros:
- Less code. I don't mind using interfaces, but I've seen too many projects where excessiveness of using interfaces and abstraction layers cause more trouble than help.
- Open dialogue windows through
Module Controller
is a simple way to avoid strong references and still allows to use mock-ups for testing. - Notification through weak events reduce number of potential memory leaks.
Cons:
- Not easy to distinguish required notification from others in the handler. Two solutions:
- send a unique token on opening a dialogue window and check that token in the subscription
- use generic notification classes
<T>
whereT
is enumeration of entities (or for simplicity it can be type of ViewModel).
- For a project should be an agreement about using notification classes to prevent duplicating them.
- For enormously large projects the
Module Controller
can be overwhelmed by methods for creating windows. In this case it's better to split it up in several modules.
P.S. I have been using this approach for quite a long time now and ready to defend its eligibility in comments and provide some examples if required.
참고URL : https://stackoverflow.com/questions/3801681/good-or-bad-practice-for-dialogs-in-wpf-with-mvvm
'IT' 카테고리의 다른 글
GHC-mod는 타입에 전체 이름을 사용해야합니까? (0) | 2020.06.18 |
---|---|
PHP MySQL 구글 차트 JSON-완전한 예제 (0) | 2020.06.18 |
Java 웹 애플리케이션에 사용하는 아키텍처를 설명 하시겠습니까? (0) | 2020.06.18 |
UIWebView에 표시되는 HTML 페이지의 제목을 얻는 방법? (0) | 2020.06.18 |
IFRAME로드가 완료되면 Javascript 콜백? (0) | 2020.06.18 |