MVVM Light: как отменить регистрацию Messenger - PullRequest
42 голосов
/ 10 марта 2011

Мне нравится Messenger MVVM Light и его гибкость, однако у меня возникают утечки памяти, когда я забываю явно отменить регистрацию получателей (в Silverlight 4).

Причина объяснена здесь , но я в порядке с этим, так как считаю, что хорошей практикой является явная отмена регистрации получателей в любом случае, вместо того, чтобы полагаться на использование мессенджером слабых ссылок. Проблема в том, что легче сказать, чем сделать.

  • ViewModels просты: вы обычно имеете полный контроль над их жизненным циклом и можете просто Cleanup() их, когда они больше не нужны.

  • С другой стороны, представления сложнее, потому что они создаются и уничтожаются с помощью DataTemplates. Например вы можете представить ItemsControl с MyView как DataTemplate, связанный с ObservableCollection<MyViewModel>. MyView элементы управления создаются / собираются механизмом привязки, и у вас нет хорошего способа вручную вызвать Cleanup () для них.

Я имею в виду решение, но хотел бы знать, является ли это приличным шаблоном или есть лучшие альтернативы. Идея состоит в том, чтобы отправить определенное сообщение из ViewModel, чтобы сообщить связанным представлениям удалить:

public class MyViewModel : ViewModelBase
{
    ...

    public override void Cleanup()
    {
        // unregisters its own messages, so that we risk no leak
        Messenger.Default.Unregister<...>(this);

        // sends a message telling that this ViewModel is being cleaned
        Messenger.Default.Send(new ViewModelDisposingMessage(this));

        base.Cleanup();
    }
}

public class MyView : UserControl, ICleanup
{
    public MyView()
    {
         // registers to messages it actually needs
         Messenger.Default.Register<...>(this, DoSomething);

         // registers to the ViewModelDisposing message
         Messenger.Default.Register<ViewModelDisposingMessage>(this, m =>
             {
                 if (m.SenderViewModel == this.DataContext)
                     this.Cleanup();
             });
    }

    public void Cleanup()
    {
        Messenger.Default.Unregister<...>(this);
        Messenger.Default.Unregister<ViewModelDisposingMessage>(this);
    }
}

Поэтому, когда вы вызываете Cleanup () для viewModel, все представления, которые используют его в качестве DataContext, будут также выполнять локальную очистку ().

Что ты думаешь? Я что-то упускаю из виду?

Ответы [ 3 ]

6 голосов
/ 25 июня 2011

Класс ViewModelLocator помогает сохранить централизованное хранилище для ваших моделей представления. Вы можете использовать этот класс для управления новыми версиями и очистки старых. Я всегда ссылаюсь на мою viewmodel из вида через локатор, поэтому у меня всегда есть код, который я могу запустить, чтобы управлять этими вещами. Вы можете попробовать это.

Также я использую метод Cleanup для вызова Messenger.Unregister(this), который очищает все ссылки из мессенджера для этого объекта. Вы должны каждый раз вызывать .Cleanup (), но такова жизнь :)

1 голос
/ 08 апреля 2011

Я не использовал MVVM Light (хотя я слышал замечательные вещи), но если вы хотите реализацию Messenger, которая использует WeakReferences, посмотрите Messenger, включенный здесь http://mvvmfoundation.codeplex.com/.

0 голосов
/ 01 ноября 2011

MVVM Light Messenger использует WeakAction (WeakReference).Так что вам не нужно явно отменять регистрацию.

...