source

MVVM을 사용하는 wpf의 대화상자에 대한 바람직한 방법 또는 나쁜 방법?

gigabyte 2023. 4. 14. 21:46
반응형

MVVM을 사용하는 wpf의 대화상자에 대한 바람직한 방법 또는 나쁜 방법?

최근에 wpf 앱의 추가 및 편집 대화상자를 만드는 데 문제가 있었습니다.

코드로 하고 싶은 것은 이것뿐입니다.(주로 mvvm에서는 뷰모델 우선접근법을 사용합니다)

대화창을 호출하는 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;
}

Model이 Model이 필요한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;
    }
 }

나는 제제 a a a a a를 .DataTemplate리소스 파일)에서app.xaml[ ] :

<DataTemplate DataType="{x:Type DialogViewModel:EditOrNewAuswahlItemVM}" >
        <DialogView:EditOrNewAuswahlItem/>
</DataTemplate>

이것으로 뷰모델에서 다이얼로그를 호출할 수 있게 되었습니다.

 var result = this.uiDialogService.ShowDialog("Dialogwindow Title", dialogwindowVM);

이제 질문입니다. 이 솔루션에 문제가 있다고 생각하십니까?

편집: 완전성을 확보합니다.은 View Model을 구현해야 .IDialogResultVMHelper 하면 1번으로 수 요.OkCommand것: " " or or or or or or "

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: 여기서부터의 코드를 사용하여, Event Handler 의 등록을 약하게 했습니다.
http://diditwith.net/2007/03/23/SolvingTheProblemWithEventsWeakEventHandlers.aspxhttpdiditwith.net/2007/03/23/.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; }
}

저는 몇 달 동안 거의 같은 방식을 사용하고 있으며, 매우 만족하고 있습니다(즉, 아직 완전히 다시 쓰고 싶은 충동을 느끼지 못했습니다).

「」를 .IDialogViewModel), 「」, 「」(」, 「Dialogs」, 「Dialogs」, 「Dialogs」, 「Dialogs」, 「Dialogs」의 것이 됩니다.RequestClose 및 윈도 할 수 몇

팝업 메시지 상자뿐만 아니라 대화창에 대해서도 언급하고 있다면 아래의 접근 방식을 고려해 주십시오.요점은 다음과 같습니다.

  1. Module ControllerViewModel(일부러)
  2. ★★★Module Controller에는 (결과를 반환하지 않고 작성만 하고) 대화창을 작성하는 퍼블릭/내부 방법이 있습니다. 에서 .ViewModel나는 다음과 같이 쓰고 있다.controller.OpenDialogEntity(bla, bla...)
  3. 각 대화창은 Weak Events를 통해 결과를 알립니다(확인, 저장, 취소 등).PRISM을 사용하면 이 Event Aggregator를 사용하여 알림을 게시할 수 있습니다.
  4. 대화 결과를 처리하기 위해 알림 구독(PRISM의 경우 Weak Events와 Event Aggregator)을 사용하고 있습니다.이러한 통지에 대한 의존성을 줄이려면 표준 통지에 독립 클래스를 사용하십시오.

장점:

  • 코드를 줄이다.인터페이스를 사용하는 것은 상관없지만, 인터페이스나 추상화 레이어를 과도하게 사용하는 것이 도움이 되기보다 더 많은 문제를 일으키는 프로젝트를 너무 많이 봐왔습니다.
  • 대화창 열기Module Controller는 강력한 참조를 피하기 위한 간단한 방법으로, 테스트에 목업을 사용할 수 있습니다.
  • 약한 이벤트를 통한 알림은 잠재적인 메모리 누전 수를 줄입니다.

단점:

  • 핸들러 내의 다른 알림과 필요한 알림을 구별하기가 쉽지 않습니다.두 가지 솔루션:
    • 대화창을 열 때 고유 토큰을 전송하고 구독에서 해당 토큰을 확인합니다.
    • 범용 알림 클래스 사용<T>어디에T는 엔티티의 열거형입니다(간단히 말하면 ViewModel 유형으로 할 수 있습니다).
  • 프로젝트에는 알림 클래스를 사용하여 중복을 방지하는 데 대한 동의서가 있어야 합니다.
  • 대규모 프로젝트에서는Module Controller창 작성 방법에 시달릴 수 있습니다.이 경우 여러 모듈로 분할하는 것이 좋습니다.

P.S. 저는 이 접근방식을 꽤 오랫동안 사용해 왔고, 필요한 경우 코멘트로 적격성을 옹호하고 몇 가지 예를 제공할 준비가 되어 있습니다.

언급URL : https://stackoverflow.com/questions/3801681/good-or-bad-practice-for-dialogs-in-wpf-with-mvvm

반응형