代码之家  ›  专栏  ›  技术社区  ›  Johannes

避免短期视图模型上的weakEventManager和内存泄漏

  •  1
  • Johannes  · 技术社区  · 6 年前

    我有长寿命的模型和使用视图显示的属性。 我视图中的DataContext是一个使用寿命短的ViewModel。

    示例包括列表中的行视图模型。

    为了避免内存泄漏,ViewModels使用 System.Windows.WeakEventManager .

    如果我正常订阅,长寿模型将保持viewModel的活动状态。

    在每个视图模型中使用weakEventManager似乎非常麻烦。 这个用例看起来像是WPF的标准用例。我是否遗漏了WPF或C的基本概念,这将有助于我在这里编写更好的代码?

    下面是一个最小的例子,说明了我现在所做的。

    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            //building would take place in a factory method
            DataContext = new ShortLivedViewModel(new LongLivingModel());
        }
    }
    
    public class ShortLivedViewModel : INotifyPropertyChanged
    {
        private string _myText;
    
        public ShortLivedViewModel(LongLivingModel model)
        {
            model.SomeEvent += SomeEventHandler;
            WeakEventManager<LongLivingModel, EventArgs>.AddHandler(model, nameof(LongLivingModel.SomeEvent),
                SomeEventHandler);
        }
    
        public string MyText
        {
            get => _myText;
            set
            {
                _myText = value;
                PropertyChanged(this, new PropertyChangedEventArgs(nameof(MyText)));
            }
        }
    
        public event PropertyChangedEventHandler PropertyChanged = delegate { };
    
        private void SomeEventHandler(object sender, EventArgs e)
        {
            //The actual update content would come from the event args
            MyText = Guid.NewGuid().ToString("N");
        }
    }
    
    public class LongLivingModel
    {
        //the event does not matter too much so I omit the implementation that causes it
        public EventHandler<EventArgs> SomeEvent = delegate { };
    }
    

    我的问题是,是否有一种简单的方法可以从一个短的生命体订阅一个长的生命体。或者如果在WPF中有一些设施我不知道。

    我突然想到这是标准情况。 我玩的是添加 IDisposable 接口,但这只留下了我跟踪什么时候调用Dispose,以便我可以取消订阅。

    我要寻找的可能是告诉GC订阅不计入ViewModel的生存期,以及取消订阅销毁,或者是更好的解决方案。

    1 回复  |  直到 6 年前
        1
  •  1
  •   bic    6 年前

    我认为父视图模型应该负责去掉引用。

    这是一个将更新推送到每个子视图模型的简单示例。当需要清理子级时,它会通知父级删除其引用。

    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            //building would take place in a factory method
            DataContext = new LongLivingModel();
            LongLivingModel.AddChild();
            LongLivingModel.AddChild();
        }
    }
    
    public class ShortLivedViewModel : INotifyPropertyChanged
    {
        private readonly LongLivingModel longLivingModel;
    
        public ShortLivedViewModel(LongLivingModel longLivingModel){
            this.longLivingModel = longLivingModel;
        }
    
        private string _myText;
    
        public string MyText
        {
            get => _myText;
            set
            {
                _myText = value;
                PropertyChanged(this, new PropertyChangedEventArgs(nameof(MyText)));
            }
        }
    
        public void Remove(){
            longLivingModel.Remove(this);
        }
    
        // INotifyPropertyChanged implementation
    }
    
    public class LongLivingModel
    {
        public ObservableCollection<ShortLivedViewModel> ChildViewModels { get; } = new ObservableCollection<ShortLivedViewModel>();
    
        public void AddChild(){
            ChildViewModels.Add(new ShortLivedViewModel(this));
        }
    
        public void RemoveChild(ShortLivedViewModel shortLivedViewModel) {
            ChildViewModels.Remove(shortLivedViewModel);
        }
    
        public void PushToChildren(){
            foreach(ShortLivedViewModel shortLivedViewModel in ChildViewModels){
                shortLivedViewModel.MyText = Guid.NewGuid().ToString("N");
            }
        }
    }
    
    推荐文章