J'ai récemment eu le problème de créer des boîtes de dialogue d'ajout et de modification pour mon application wpf.
Tout ce que je veux faire dans mon code, c'est quelque chose comme ça. (J'utilise principalement la première approche de viewmodel 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 marche?
Tout d'abord, j'ai créé un service de dialogue:
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 fenêtre spéciale mais simple. J'en ai besoin pour contenir 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 dans wpf est dialogresult = true
que cela ne peut être réalisé que dans le code. C'est pourquoi j'ai créé une interface pour l' dialogviewmodel
implémenter.
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 dialogresult = true
, déclenchez 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 je dois créer un DataTemplate
dans mon fichier de ressources ( app.xaml
ou quelque chose):
<DataTemplate DataType="{x:Type DialogViewModel:EditOrNewAuswahlItemVM}" >
<DialogView:EditOrNewAuswahlItem/>
</DataTemplate>
Eh bien c'est tout, je peux maintenant appeler des boîtes de dialogue à partir de mes modèles de vue:
var result = this.uiDialogService.ShowDialog("Dialogwindow Title", dialogwindowVM);
Maintenant ma question, voyez-vous des problèmes avec cette solution?
Edit: par souci d'exhaustivité. Le ViewModel devrait implémenter IDialogResultVMHelper
et ensuite il peut le lever dans un OkCommand
ou quelque chose comme ça:
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 d'ici pour rendre mon registre EventHandler faible:
http://diditwith.net/2007/03/23/SolvingTheProblemWithEventsWeakEventHandlers.aspx
(Le site Web n'existe plus, 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;
}
}
Réponses:
C'est une bonne approche et j'ai utilisé des approches similaires dans le passé. Fonce!
Une chose mineure que je ferais certainement est de faire en sorte que l'événement reçoive un booléen lorsque vous devez définir "false" dans le DialogResult.
et la classe EventArgs:
la source
J'utilise une approche presque identique depuis plusieurs mois maintenant, et j'en suis très content (c'est-à-dire que je n'ai pas encore ressenti le besoin de la réécrire complètement ...)
Dans mon implémentation, j'utilise un
IDialogViewModel
qui expose des choses telles que le titre, les boutons standad à afficher (afin d'avoir une apparence cohérente dans toutes les boîtes de dialogue), unRequestClose
événement, et quelques autres choses pour pouvoir contrôler la taille de la fenêtre et comportementla source
Si vous parlez de fenêtres de dialogue et pas seulement des boîtes de message contextuelles, veuillez considérer mon approche ci-dessous. Les points clés sont:
Module Controller
dans le constructeur de chacunViewModel
(vous pouvez utiliser l'injection).Module Controller
a des méthodes publiques / internes pour créer des fenêtres de dialogue (simplement créer, sans renvoyer de résultat). Par conséquent pour ouvrir une fenêtre de dialogue dansViewModel
j'écris:controller.OpenDialogEntity(bla, bla...)
Avantages:
Module Controller
est un moyen simple d'éviter les références fortes et permet toujours d'utiliser des maquettes pour les tests.Les inconvénients:
<T>
où seT
trouve l'énumération des entités (ou pour plus de simplicité, il peut s'agir du type ViewModel).Module Controller
méthodes de création de fenêtres peuvent déborder. Dans ce cas, il est préférable de le diviser en plusieurs modules.PS J'utilise cette approche depuis assez longtemps maintenant et je suis prêt à défendre son éligibilité dans les commentaires et à donner quelques exemples si nécessaire.
la source