2017-12-12 7 views
-1

MVVMを使用して、複数のビューを切り替えるアプリケーションを作成しています。ビューは、このようなContentControlによってインスタンス化されていますイベントから退会する必要があるときにビューをどのように伝えますか?

基礎となるViewModelには、このようなものになります
<ContentControl Name="DynamicViewControl" Content="{Binding }"> 
    <ContentControl.Resources> 
     <DataTemplate x:Key="PageOneTemplate"> 
      <pages:PageOne DataContext="{Binding PageOneViewModel}"/> 
     </DataTemplate> 
     <DataTemplate x:Key="PageTwoTemplate"> 
      <pages:PageTwo DataContext="{Binding PageTwoViewModel}"/> 
     </DataTemplate> 
     <!-- And so on... --> 
    </ContentControl.Resources> 
    <ContentControl.Style> 
     <Style TargetType="{x:Type ContentControl}"> 
      <Style.Triggers> 
       <DataTrigger Binding="{Binding CurrentPage}" Value="{x:Static model:Pages.PageOne}"> 
         <Setter Property="ContentTemplate" Value="{StaticResource PageOneTemplate}"/> 
       </DataTrigger> 
       <DataTrigger Binding="{Binding CurrentPage}" Value="{x:Static model:Pages.PageTwo}"> 
        <Setter Property="ContentTemplate" Value="{StaticResource PageTwoTemplate}"/> 
       </DataTrigger>   
       <!-- And so on... -->  
      </Style.Triggers> 
     </Style> 
    </ContentControl.Style> 
</ContentControl> 

public enum Pages { 
    PageOne, 
    PageTwo, 
    // etc... 
} 

public class PageViewModel : ObservableObject { 
    private Pages currentPage = Pages.PageOne; 

    public PageViewModel() { 
     PageOneViewModel= new PageOneViewModel(Model); 
     PageTwoViewModel= new PageTwoViewModel(Model); 
     // And so on... 

     NavButtonCommand = new RelayCommand(NavButton); 
     PreviousButtonCommand = new RelayCommand(PreviousButton); 
    } 

    public PageModel Model { get; } = new PageModel(); 

    /// <summary>Gets or sets the current page.</summary> 
    public Pages CurrentPage { 
     get => currentPage; 
     set { 
      currentPage = value; 
      NotifyPropertyChanged(); 
     } 
    } 

    public DataSelectionViewModel PageOneViewModel { get; } 

    public ProfileSelectionViewModel PageTwoViewModel { get; } 

    public ICommand NavButtonCommand { get; } 

    public ICommand PreviousButtonCommand { get; } 

    // This isn't my actual page change logic, just some code with a 
    // similar effect to get my point across 
    private void NavButton(object param) { 
     int next = (int)CurrentPage + 1; 
     if Enum.IsDefined(typeof(Pages), next) { 
      CurrentPage = (Pages)next; 
     } 
    } 

    private void PreviousButton(object param) { 
     int previous = (int)CurrentPage - 1; 
     if Enum.IsDefined(typeof(Pages), previous) { 
      CurrentPage = (Pages)previous; 
     } 
    } 
} 

問題がある私の見解の一部では、私はPropertyChanged通知をサブスクライブする必要があることをそれぞれのViewModelで簡単にバインドできないView内のものを変更できるようにします。 ContentControlが毎回新しいViewを作成し、ViewModelで参照を保持しているイベントハンドラのために決してクリーンアップされないため、メモリリークが発生します(C#でメモリリークが存在する限り)。

ビューの変更時にViewModelのすべてのイベントサブスクライバをクリーンアップしようとしましたが、ビューのクリーンアップコードがViewModel内にあり、意図しない結果が生じ、一部の機能が動作しなくなった。

イベントに登録を停止するように私の意見を伝える方法はありますか?または、それらをバインドする方法(バインドできるDependencyPropertiesでカスタムコントロールを作成するなど)を見つける必要があります。

+0

あなたのビューモデルはどのようなイベントでも購読していますか? –

+0

ビューはビューモデルの 'PropertyChanged'通知を購読しています。 – Lauraducky

+0

質問に同じ質問があるという同じ質問があるからといって、必ずしもそうではないと思うが、将来の訪問者が同様の質問を参照するのに役立つかもしれない。 – Lauraducky

答えて

1

私は答えを見つけました、私は思ったよりも速いです。ほとんどのWPFコントロールが行う方法は、Weak Event Patternであることが判明しています。このパターンを使用すると、弱い参照でイベントを購読することができます。 ViewModelには、ビューよりも長い寿命を持っている場合でも、すべての参照のみが弱くなりの方法、

PropertyChangedEventManager.AddHandler(model, Model_PropertyChanged, "MyProperty"); 

:もっとこのような何かに

model.PropertyChanged += Model_PropertyChanged; 

:ソリューションは、このような行を変更しましたイベントのサブスクリプションがクリーンアップされていない場合でも、ガベージコレクタがオブジェクトをクリーンアップできるようにします。

+0

この記事をご覧ください。多分役立つかもしれません:https://csharpvault.com/blog/weak-event-pattern/ – Arnel

関連する問題