Bonne ou mauvaise pratique pour les Dialogues en wpf avec MVVM?

J'ai eu dernièrement le problème de la création d'ajouter et de modifier des boîtes de dialogue pour mon application wpf.

Tout ce que je veux faire dans mon code était quelque chose comme cela. (J'utilise surtout viewmodel première approche avec mvvm)

ViewModel qui appelle une fenêtre de dialogue:

var result = this.uiDialogService.ShowDialog("Dialogwindow Title", dialogwindowVM);
//Do anything with the dialog result

Comment ça fonctionne?

Tout d'abord, j'ai créé une boîte de dialogue service:

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 est une simple fenêtre. J'en ai besoin pour tenir mon contenu:

<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>

Un problème avec les dialogues en wpf est le dialogresult = true ne peut être atteint que dans le code. C'est pourquoi j'ai créé une interface pour mon dialogviewmodel à mettre en œuvre.

public class RequestCloseDialogEventArgs : EventArgs
{
    public bool DialogResult { get; set; }
    public RequestCloseDialogEventArgs(bool dialogresult)
    {
        this.DialogResult = dialogresult;
    }
}

public interface IDialogResultVMHelper
{
    event EventHandler<RequestCloseDialogEventArgs> RequestCloseDialog;
}

Chaque fois que mon ViewModel pense qu'il est temps pour dialogresult = true, puis déclencher cet événement.

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;
    }
 }

Maintenant au moins, j'ai créer un DataTemplate dans mon fichier de ressources(app.xaml ou quelque chose):

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

Bien c'est tout, je peux maintenant appeler les boîtes de dialogue à partir de mon viewmodel:

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

Maintenant, ma question, voyez-vous des problèmes avec cette solution?

Edit: pour être complet. Ce Dernier doit mettre en œuvre IDialogResultVMHelper et puis elle peut le relever dans un OkCommand ou quelque chose comme ceci:

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);
    }
 }

EDIT 2: j'ai utilisé le code à partir d'ici pour faire mon Gestionnaire enregistrer de faibles:

http://diditwith.net/2007/03/23/SolvingTheProblemWithEventsWeakEventHandlers.aspx

(Site web n'existe plus, WebArchive Miroir)

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;
}
}
  • vous êtes probablement manquant la xmlns:x="schemas.microsoft.com/winfx/2006/xaml" refernece dans votre WindowDialog XAML.
  • En fait l'espace de noms est xmlns:x="[http://]schemas.microsoft.com/winfx/2006/xaml" sans les crochets
  • voir stackoverflow.com/questions/16993433/...
  • Salut @blindmeis. Je sais que près de 5 ans ont passé, mais je suis bloqué à la fermeture de la boîte de dialogue de la partie. Pouvez-vous nous expliquer en partie?
  • votre dernier doit mettre en œuvre IDialogResultVMHelper et de soulever le cas de cours. voir mon edit
  • je vous remercie pour votre réponse. J'ai déjà mis en place cette interface. Mon problème est que j'ai aussi placé le contexte de données dans le code xaml de mon contrôle de l'utilisateur. en tant que <UserControl.DataContext> </UserControl.DataContext>. À cause de cela, à la InitializeComponenet() de la partie nouvelle instance de cette classe a été créée pour l'événement a été supprimé. Après une journée coincé j'ai supprimé les données de réglage contexte de xaml et il fonctionne comme un charme. Merci pour ce superbe post. Il est très utile.
  • j'ai toujours utiliser viewmodel première approche avec les DataTemplates dans mes applications, sauf dans la mainwindow. donc je n'ai pas besoin de UserControl.DataContext dans une de mon point de vue 🙂
  • Salut! Retardataire ici. Je ne suis pas la compréhension de la manière dont le Service a une référence à la WindowDialog. Quelle est la hiérarchie de vos modèles? Dans mon esprit, la Vue contient une référence à l'Viewmodel de l'assemblée et du Viewmodel pour le Service et le Modèle des assemblées. Ainsi, la couche de Service aurait aucune connaissance de la WindowDialog vue. Ce qui me manque?
  • Cela pourrait être mieux posté dans codereview.stackexchange.com
  • Salut @blindmeis, juste essayer d'envelopper ma tête autour de ce concept, je ne crois pas qu'il y a une certaine ligne exemple de projet que je puisse reprendre le dessus? Il y a un certain nombre de choses, je suis confus.

InformationsquelleAutor blindmeis | 2010-09-27