MVVM ilə WPF-də dialoqlar üçün yaxşı və ya pis təcrübə?

Son vaxtlarda, mənim wpf proqramım üçün əlavə qutular yaratmaq və redaktə etmək problemi yarandı.

Mənim kodumda istədiyim hər şey bu idi. (Əsasən mvvm ilə ilk baxışdan modeli istifadə edirəm)

Dialoq qutusuna zəng edən ViewModel:

 var result = this.uiDialogService.ShowDialog("Dialogwindow Title", dialogwindowVM); // Do anything with the dialog result 

Necə işləyir?

Birincisi, bir dialoq xidməti yaratdım:

 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 xüsusi, lakin sadə bir pəncərə. İçindəkilərimi ehtiva etmək lazımdır:

 <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> 

Wpf informasiya qutusu ilə problem dialogresult = true , yalnız kodu əldə edilə bilər. Ona görə də bunu həyata keçirmək üçün mənim dialogviewmodel üçün interfeys yaratmışam.

 public class RequestCloseDialogEventArgs : EventArgs { public bool DialogResult { get; set; } public RequestCloseDialogEventArgs(bool dialogresult) { this.DialogResult = dialogresult; } } public interface IDialogResultVMHelper { event EventHandler<RequestCloseDialogEventArgs> RequestCloseDialog; } 

Mənim ViewModel dialogresult = true vaxt olduğunu düşündüyündə, bu hadisə qaldırın.

 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; } 

İndi ən azı mənim mənbə faylında ( app.xaml və ya başqa bir şey) app.xaml yaratmaq lazımdır:

 <DataTemplate DataType="{x:Type DialogViewModel:EditOrNewAuswahlItemVM}" > <DialogView:EditOrNewAuswahlItem/> </DataTemplate> 

Bəli, hamısı budur, indi görüşlər modellərimdən dialoqları çağırıram:

  var result = this.uiDialogService.ShowDialog("Dialogwindow Title", dialogwindowVM); 

İndi mənim sualım, bu həll ilə bir problem görürsənmi?

Düzenle: tamamlanma üçün. ViewModel, IDialogResultVMHelper tətbiq etməlidir və sonra OkCommand ya da bu kimi bir şeyə yüksəldə bilər:

 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: Mənim EventHandler'ımı zəif etmək üçün kodu istifadə etdim:
http://diditwith.net/2007/03/23/SolvingTheProblemWithEventsWeakEventHandlers.aspx
(Veb sayt artıq mövcud deyil, 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; } } 
125
27 сент. Blindmeis set 27 sep . 2010-09-27 09:56 '10 at 9:56 2010-09-27 09:56
@ 3 cavab

Bu yaxşı bir yanaşma və keçmişdə də buna bənzər şeylər istifadə etmişəm. Bunun üçün get!

Mütləq bir şey edəcəyəm, DialogResult'a "yanlış" etməli olduğunuzda hadisəni boolean etməkdir.

 event EventHandler<RequestCloseEventArgs> RequestCloseDialog; 

və sinif EventArgs:

 public class RequestCloseEventArgs : EventArgs { public RequestCloseEventArgs(bool dialogResult) { this.DialogResult = dialogResult; } public bool DialogResult { get; private set; } } 
42
27 сент. Julian Dominguez cavab 27 Sep 2010-09-27 21:23 '10 at 21:23 2010-09-27 21:23

Bir neçə aydan bəri demək olar ki, eyni bir yanaşma istifadə edirəm və buna çox razıyam (yəni, tamamilə yenidən yazma arzusunu hiss etməmişəm ...)

border=0

Mənim IDialogViewModel istifadə IDialogViewModel , bu ekranın başlığı, görünüş üçün standad düymələri (bütün informasiya qutularında ardıcıl görünürlük göstərmək üçün), RequestClose hadisə və pəncərənin ölçüsü və davranışını idarə etmək üçün bir çox başqa şeylər RequestClose

14
27 сент. Thomas Levesque tərəfindən verilmiş cavab 27 sentyabr 2010-09-27 22:00 '10 saat 22:00 'də 2010-09-27 22:00' də

Əgər dialoq qutularında danışırsınızsa, yalnız açılır mesajlar deyil, aşağıda mənim yanaşma hesab edin. Əsas nöqtələr:

  • Hər bir ViewModel konstruktoruna (bir enjeksiyon istifadə edə bilərsiniz) Module Controller keçid verirəm.
  • Module Controller informasiya qutuları yaratmaq üçün ictimai / daxili metodlara malikdir (yalnız nəticə çıxararaq, nəticə vermir). Buna görə, ViewModel də dialoq açmaq üçün yazıram: controller.OpenDialogEntity(bla, bla...)
  • Hər bir informasiya qutusu zəif hadisələr vasitəsilə nəticəsini (məsələn, OK, Saxla, İptal et və s.) Bildirir . PRISM istifadə edirsinizsə, bu EventAggregator istifadə edərək bildirişləri dərc etmək daha asandır.
  • Dialoqun nəticələrini işləmək üçün bildirişlərə abunə istifadə edirəm (yenidən zəif hadisələr və PRISM halında EventAggregator ). Bu bildirişlərə asılılığı azaltmaq üçün, standart bildirişlərlə müstəqil siniflərdən istifadə edin.

Pros:

  • Az kod. Mən interfeysdən istifadə etmirəm, amma abstraksiyanın interfeys və qatlarının istifadə edilməsində köhnəlmə yardımdan daha çox problemə səbəb olduğu çoxsaylı layihələri gördüm.
  • Module Controller vasitəsilə informasiya qutularının açılması güclü əlaqələrdən qaçınmaq üçün asan bir yoldur və hələ də test üçün planlarınızı istifadə etməyə imkan verir.
  • Zəif hadisələr vasitəsilə bildiriş potensial yaddaş sızmalarının sayını azaldır.

Eksiler:

  • Tələb olunan bildirişi işəgötürəndən ayırmaq asan deyil. İki həlli:
    • informasiya qutusunu açarkən unikal bir mö'cüzə göndərin və abunəliyə bu mö'cüzəni yoxlayın
    • ümumi <T> bildiriş sxemlərini istifadə edin, burada T obyektlərin bir nüsxəsi (ya da sadəlik üçün bu bir ViewModel növü ola bilər).
  • Layihə üçün təkrarlamanın qarşısını almaq üçün bildiriş siniflərinin istifadəsi haqqında razılığa gəlinir.
  • Çox böyük layihələr üçün, Module Controller , pəncərə üsulları ilə çox yüklənə bilər. Bu halda, onu bir neçə modul bölmək daha yaxşıdır.

PS Bu yanaşmanı bir müddət istifadə edirəm və şərh etmək hüququmuzu müdafiə etməyə hazıram və zərurət yarandıqda bəzi nümunələr verəcəyəm.

1
22 нояб. 22 noyabr tarixində Aleks Klausun verdiyi cavab. 2013-11-22 02:28 '13 'da 2:28' də 2013-11-22 02:28