Últimamente tuve el problema de crear cuadros de diálogo para agregar y editar para mi aplicación wpf.
Todo lo que quiero hacer en mi código es algo como esto. (Principalmente uso el primer enfoque de viewmodel con mvvm)
ViewModel que llama a una ventana de diálogo:
var result = this.uiDialogService.ShowDialog("Dialogwindow Title", dialogwindowVM);
// Do anything with the dialog result
¿Como funciona?
Primero, creé un servicio de diálogo:
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
Es una ventana especial pero simple. Lo necesito para guardar mi contenido:
<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 problema con los diálogos en wpf es dialogresult = true
que solo se puede lograr en el código. Es por eso que creé una interfaz para dialogviewmodel
que la implemente.
public class RequestCloseDialogEventArgs : EventArgs
{
public bool DialogResult { get; set; }
public RequestCloseDialogEventArgs(bool dialogresult)
{
this.DialogResult = dialogresult;
}
}
public interface IDialogResultVMHelper
{
event EventHandler<RequestCloseDialogEventArgs> RequestCloseDialog;
}
Cada vez que mi ViewModel piense que es hora de hacerlo dialogresult = true
, entonces genere este evento.
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;
}
}
Ahora al menos tengo que crear un DataTemplate
en mi archivo de recursos ( app.xaml
o algo así):
<DataTemplate DataType="{x:Type DialogViewModel:EditOrNewAuswahlItemVM}" >
<DialogView:EditOrNewAuswahlItem/>
</DataTemplate>
Bueno, eso es todo, ahora puedo llamar diálogos desde mis modelos de vista:
var result = this.uiDialogService.ShowDialog("Dialogwindow Title", dialogwindowVM);
Ahora mi pregunta, ¿ves algún problema con esta solución?
Editar: para completar. El ViewModel debe implementarse IDialogResultVMHelper
y luego puede elevarlo dentro de OkCommand
o algo así:
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: utilicé el código desde aquí para hacer que mi EventHandler se registre débil:
http://diditwith.net/2007/03/23/SolvingTheProblemWithEventsWeakEventHandlers.aspx
(El sitio web ya no existe, 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;
}
}
Respuestas:
Este es un buen enfoque y utilicé otros similares en el pasado. ¡Ve a por ello!
Una cosa menor que definitivamente haría es hacer que el evento reciba un valor booleano para cuando necesite establecer "falso" en DialogResult.
y la clase EventArgs:
fuente
He estado usando un enfoque casi idéntico durante varios meses y estoy muy contento con él (es decir, todavía no he sentido la necesidad de volver a escribirlo por completo ...)
En mi implementación, utilizo un
IDialogViewModel
que expone cosas como el título, los botones de standad para mostrar (para tener una apariencia consistente en todos los cuadros de diálogo), unRequestClose
evento y algunas otras cosas para poder controlar el tamaño de la ventana y comportamientofuente
Si está hablando de ventanas de diálogo y no solo de los cuadros de mensajes emergentes, considere mi enfoque a continuación. Los puntos clave son:
Module Controller
constructor de cada unoViewModel
(puede usar inyección).Module Controller
tiene métodos públicos / internos para la creación de ventanas de diálogo (sólo la creación, sin devolver un resultado). Por lo tanto, para abrir una ventana de diálogo enViewModel
escribo:controller.OpenDialogEntity(bla, bla...)
Pros:
Module Controller
es una forma simple de evitar referencias fuertes y aún permite usar maquetas para realizar pruebas.Contras:
<T>
dondeT
es la enumeración de entidades (o por simplicidad puede ser un tipo de ViewModel).Module Controller
métodos para crear ventanas pueden abrumarlo. En este caso, es mejor dividirlo en varios módulos.PD: He estado usando este enfoque durante bastante tiempo y estoy listo para defender su elegibilidad en los comentarios y proporcionar algunos ejemplos si es necesario.
fuente