Saya akhir-akhir ini memiliki masalah membuat tambah dan edit dialog untuk aplikasi WPF saya.
Yang ingin saya lakukan dalam kode saya adalah sesuatu seperti ini. (Saya kebanyakan menggunakan pendekatan viewmodel pertama dengan mvvm)
ViewModel yang memanggil jendela dialog:
var result = this.uiDialogService.ShowDialog("Dialogwindow Title", dialogwindowVM);
// Do anything with the dialog result
Bagaimana cara kerjanya?
Pertama, saya membuat layanan dialog:
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
adalah jendela khusus tetapi sederhana. Saya membutuhkannya untuk menyimpan konten saya:
<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>
Masalah dengan dialog di wpf adalah dialogresult = true
hanya dapat dicapai dalam kode. Itu sebabnya saya membuat antarmuka untuk dialogviewmodel
mengimplementasikannya.
public class RequestCloseDialogEventArgs : EventArgs
{
public bool DialogResult { get; set; }
public RequestCloseDialogEventArgs(bool dialogresult)
{
this.DialogResult = dialogresult;
}
}
public interface IDialogResultVMHelper
{
event EventHandler<RequestCloseDialogEventArgs> RequestCloseDialog;
}
Kapan pun ViewModel saya menganggap sudah saatnya dialogresult = true
, maka naikkan acara ini.
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;
}
}
Sekarang setidaknya saya harus membuat DataTemplate
di file sumber saya ( app.xaml
atau sesuatu):
<DataTemplate DataType="{x:Type DialogViewModel:EditOrNewAuswahlItemVM}" >
<DialogView:EditOrNewAuswahlItem/>
</DataTemplate>
Baiklah itu saja, sekarang saya dapat memanggil dialog dari viewmodels saya:
var result = this.uiDialogService.ShowDialog("Dialogwindow Title", dialogwindowVM);
Sekarang pertanyaan saya, apakah Anda melihat ada masalah dengan solusi ini?
Edit: untuk kelengkapan. ViewModel harus menerapkan IDialogResultVMHelper
dan kemudian dapat meningkatkannya dalam OkCommand
atau sesuatu seperti ini:
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: Saya menggunakan kode dari sini untuk membuat register EventHandler saya lemah:
http://diditwith.net/2007/03/23/SolvingTheProblemWithEventsWeakEventHandlers.aspx
(Situs web tidak lagi ada, 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;
}
}
Jawaban:
Ini adalah pendekatan yang baik dan saya menggunakan yang serupa di masa lalu. Lakukan itu!
Satu hal kecil yang saya pasti akan lakukan adalah membuat acara menerima boolean ketika Anda perlu mengatur "false" di DialogResult.
dan kelas EventArgs:
sumber
Saya telah menggunakan pendekatan yang hampir identik selama beberapa bulan sekarang, dan saya sangat senang dengan itu (yaitu saya belum merasakan keinginan untuk menulis ulang sepenuhnya ...)
Dalam implementasi saya, saya menggunakan
IDialogViewModel
yang memaparkan hal-hal seperti judul, tombol standar untuk ditampilkan (agar memiliki kemunculan yang konsisten di semua dialog),RequestClose
acara, dan beberapa hal lain untuk dapat mengontrol ukuran jendela dan tingkah lakusumber
Jika Anda berbicara tentang jendela dialog dan bukan hanya tentang kotak pesan munculan, harap pertimbangkan pendekatan saya di bawah ini. Poin kuncinya adalah:
Module Controller
ke konstruktor masing-masingViewModel
(Anda dapat menggunakan injeksi).Module Controller
memiliki metode publik / internal untuk membuat jendela dialog (hanya membuat, tanpa mengembalikan hasil). Maka dari itu untuk membuka jendela dialog diViewModel
saya menulis:controller.OpenDialogEntity(bla, bla...)
Pro:
Module Controller
adalah cara sederhana untuk menghindari referensi yang kuat dan masih memungkinkan untuk menggunakan mock-up untuk pengujian.Cons:
<T>
manaT
enumerasi entitas (atau untuk kesederhanaan itu bisa menjadi tipe ViewModel).Module Controller
dapat dibanjiri oleh metode untuk membuat windows. Dalam hal ini lebih baik membaginya dalam beberapa modul.PS Saya telah menggunakan pendekatan ini untuk waktu yang cukup lama sekarang dan siap untuk mempertahankan kelayakannya dalam komentar dan memberikan beberapa contoh jika diperlukan.
sumber