Ich hatte kürzlich das Problem, Dialogfelder zum Hinzufügen und Bearbeiten für meine wpf-App zu erstellen.
Alles, was ich in meinem Code tun möchte, war so etwas. (Ich verwende meistens den ersten Ansatz von viewmodel mit mvvm)
ViewModel, das ein Dialogfenster aufruft:
var result = this.uiDialogService.ShowDialog("Dialogwindow Title", dialogwindowVM);
// Do anything with the dialog result
Wie funktioniert es?
Zuerst habe ich einen Dialogdienst erstellt:
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
ist ein spezielles aber einfaches Fenster. Ich brauche es, um meinen Inhalt zu halten:
<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>
Ein Problem mit Dialogen in wpf ist, dass dialogresult = true
dies nur im Code erreicht werden kann. Deshalb habe ich eine Schnittstelle erstellt, über die ich dialogviewmodel
sie implementieren kann.
public class RequestCloseDialogEventArgs : EventArgs
{
public bool DialogResult { get; set; }
public RequestCloseDialogEventArgs(bool dialogresult)
{
this.DialogResult = dialogresult;
}
}
public interface IDialogResultVMHelper
{
event EventHandler<RequestCloseDialogEventArgs> RequestCloseDialog;
}
Wenn mein ViewModel glaubt, dass es Zeit für ist dialogresult = true
, lösen Sie dieses Ereignis aus.
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;
}
}
Jetzt muss ich zumindest eine DataTemplate
in meiner Ressourcendatei erstellen ( app.xaml
oder so):
<DataTemplate DataType="{x:Type DialogViewModel:EditOrNewAuswahlItemVM}" >
<DialogView:EditOrNewAuswahlItem/>
</DataTemplate>
Nun, das ist alles, ich kann jetzt Dialoge aus meinen Ansichtsmodellen aufrufen:
var result = this.uiDialogService.ShowDialog("Dialogwindow Title", dialogwindowVM);
Nun meine Frage, sehen Sie irgendwelche Probleme mit dieser Lösung?
Bearbeiten: der Vollständigkeit halber. Das ViewModel sollte implementiert werden IDialogResultVMHelper
und kann es dann in einem OkCommand
oder einem ähnlichen Bereich auslösen:
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);
}
}
BEARBEITEN 2: Ich habe den Code von hier verwendet, um mein EventHandler-Register schwach zu machen:
http://diditwith.net/2007/03/23/SolvingTheProblemWithEventsWeakEventHandlers.aspx
(Website existiert nicht mehr, 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;
}
}
Antworten:
Dies ist ein guter Ansatz, und ich habe in der Vergangenheit ähnliche verwendet. Tue es!
Eine Kleinigkeit, die ich definitiv tun würde, ist, das Ereignis einen Booleschen Wert zu erhalten, wenn Sie im DialogResult "false" setzen müssen.
und die EventArgs-Klasse:
quelle
Ich benutze seit einigen Monaten einen fast identischen Ansatz und bin sehr zufrieden damit (dh ich habe noch nicht den Drang verspürt, ihn komplett neu zu schreiben ...)
In meiner Implementierung verwende ich a
IDialogViewModel
, das Dinge wie den Titel, die anzuzeigenden Standad-Schaltflächen (um über alle Dialoge hinweg eine einheitliche Darstellung zu erhalten), einRequestClose
Ereignis und einige andere Dinge zur Steuerung der Fenstergröße und anzeigt Verhaltenquelle
Wenn Sie über Dialogfenster und nicht nur über die Popup-Meldungsfelder sprechen, beachten Sie bitte meinen Ansatz unten. Die wichtigsten Punkte sind:
Module Controller
den Konstruktor eines jedenViewModel
(Sie können die Injektion verwenden).Module Controller
hat öffentliche / interne Methoden zum Erstellen von Dialogfenstern (nur erstellen, ohne ein Ergebnis zurückzugeben). Um ein Dialogfenster in zu öffnen,ViewModel
schreibe ich:controller.OpenDialogEntity(bla, bla...)
Vorteile:
Module Controller
ist ein einfacher Weg, um starke Referenzen zu vermeiden, und ermöglicht es dennoch, Modelle zum Testen zu verwenden.Nachteile:
<T>
denenT
Entitäten aufgelistet sind (oder der Einfachheit halber kann es sich um einen ViewModel-Typ handeln).Module Controller
kann dies durch Methoden zum Erstellen von Fenstern überfordert sein. In diesem Fall ist es besser, es in mehrere Module aufzuteilen.PS Ich verwende diesen Ansatz bereits seit geraumer Zeit und bin bereit, seine Berechtigung in Kommentaren zu verteidigen und bei Bedarf einige Beispiele anzugeben.
quelle