如何在取消订阅活动时告诉我的观点?

时间:2017-12-12 01:40:52

标签: c# wpf mvvm

我使用MVVM创建一个在多个视图之间切换的应用程序。视图通过ContentControl实例化,如下所示:

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

底层ViewModel看起来像这样:

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

问题在于,在我的一些观看中,我需要在各自的ViewModel上订阅PropertyChanged通知,以便我可以在视图中更改无法轻松绑定的内容。这导致内存泄漏&#34; (因为C#中可能存在内存泄漏),因为ContentControl每次创建一个新的视图,并且由于那些事件处理程序仍然在ViewModel中有引用,它永远不会被清除。

我尝试在View更改时清理ViewModel的所有事件订阅者,但除了它导致ViewModel中的视图清理代码之外,它还会产生意想不到的后果,并使我的某些功能停止工作。

有没有办法告诉我的意见停止订阅活动?或者,我应该找到一种绑定它们的方法(例如使用可以绑定的DependencyProperties创建自定义控件)。

1 个答案:

答案 0 :(得分:1)

我找到了答案,比我想象的要快。大多数WPF控件执行的方式都是Weak Event Pattern。此模式允许您订阅具有弱引用的事件。解决方案是更改这样的行:

model.PropertyChanged += Model_PropertyChanged;

更像这样的事情:

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

这样,即使ViewModel的生命周期比视图长,任何引用都只是弱引用,允许垃圾收集器出现并清理对象,即使它的事件订阅尚未清除起来。