J'ai récemment eu le problème de créer des dialogues d'ajout et de modification pour mon application wpf.
Tout ce que je voulais faire dans mon code était quelque chose comme ceci. (J'utilise principalement l'approche viewmodel first 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 cela fonctionne-t-il ?
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 boîtes de dialogue dans wpf est le suivant dialogresult = true
ne peut être réalisée que dans le code. C'est pourquoi j'ai créé une interface pour mon programme dialogviewmodel
pour le 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 de 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, je dois au moins créer un DataTemplate
dans mon fichier de ressources( app.xaml
ou autre) :
<DataTemplate DataType="{x:Type DialogViewModel:EditOrNewAuswahlItemVM}" >
<DialogView:EditOrNewAuswahlItem/>
</DataTemplate>
C'est tout, je peux maintenant appeler des dialogues à 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 : pour être complet. Le ViewModel doit implémenter IDialogResultVMHelper
et il peut ensuite l'augmenter 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 que mon EventHandler s'enregistre faiblement :
http://diditwith.net/2007/03/23/SolvingTheProblemWithEventsWeakEventHandlers.aspx
(Le site web n'existe plus, Miroir WebArchive )
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;
}
}
1 votes
Il vous manque probablement le xmlns:x=" schémas.microsoft.com/winfx/2006/xaml "dans votre XAML WindowDialog.
0 votes
En fait, l'espace de noms est xmlns:x="[http://\]schemas.microsoft.com/winfx/2006/xaml" sans les parenthèses
0 votes
Voir stackoverflow.com/questions/16993433/
0 votes
Salut @blindmeis. Je sais que presque 5 ans se sont écoulés, mais je suis resté bloqué à la fermeture de la partie dialogue. Pouvez-vous m'expliquer cette partie ?
0 votes
Votre viewmodel doit implémenter IDialogResultVMHelper et lever l'événement bien sûr. voir mon édition
0 votes
@blindmeis merci pour votre réponse. J'ai déjà implémenté cette interface. Mon problème est que j'ai également défini le contexte de données dans le xaml de mon contrôle utilisateur. comme <UserControl.DataContext> </UserControl.DataContext>. A cause de cela, à la partie InitializeComponenet() une nouvelle instance de cette classe a été créée donc l'événement a été supprimé. Après un jour de blocage, j'ai supprimé la définition du contexte de données de la xaml et cela fonctionne comme un charme. Merci pour cet excellent article. Il est très utile.
0 votes
J'utilise toujours l'approche viewmodel first avec des DataTemplates dans mes applications, sauf dans la fenêtre principale. je n'ai donc pas besoin de UserControl.DataContext dans aucune de mes vues :)
1 votes
Bonjour, je suis un retardataire. Je ne comprends pas comment votre 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'assemblage Viewmodel et le Viewmodel aux assemblages Service et Model. Ainsi, la couche Service n'aurait aucune connaissance de la vue WindowDialog. Qu'est-ce qui m'échappe ?
0 votes
Il serait peut-être préférable de poster ce message sur codereview.stackexchange.com.
3 votes
Bonjour @blindmeis, j'essaie juste de comprendre ce concept, je suppose qu'il n'y a pas de projet d'exemple en ligne sur lequel je pourrais m'appuyer ? Il y a un certain nombre de choses qui m'échappent.
0 votes
Désolé, mais où se trouve une instance d'objet de DialogService et comment est-elle transmise au ViewModel des appelants ?
0 votes
J'utilise DependencyInjection pour obtenir une instance de IUIWindowDialogService.