Home Arrow Icon Knowledge base Arrow Icon Global Arrow Icon Can you provide an example of using WeakSubscribe in MvvmCross


Can you provide an example of using WeakSubscribe in MvvmCross


Using `WeakSubscribe` in MvvmCross is a way to avoid strong references between ViewModels and Views, which can lead to memory leaks. Here's a detailed example of how to use `WeakSubscribe` in a typical MVVM scenario.

Background

In MVVM, the ViewModel should not hold a strong reference to the View. This is because if the ViewModel holds a strong reference to the View, it can prevent the View from being garbage collected, even when it's no longer needed. MvvmCross provides the `WeakSubscribe` method to handle this situation by allowing weak subscriptions to events.

Example Scenario

Let's say you have a ViewModel that needs to notify the View when some data is loaded. Traditionally, you might use an event like this:

csharp
public class MyViewModel
{
    public event Action Loaded;

    // When data is loaded
    private void OnDataLoaded()
    {
        if (Loaded != null) Loaded();
    }
}

And in your View:

csharp
public class MyView
{
    protected override void ViewJustLoaded()
    {
        this.ViewModel.Loaded += LoadControls;
    }

    private void LoadControls()
    {
        // Load controls when ViewModel is populated
    }
}

However, this creates a strong reference from the ViewModel to the View, which can cause memory leaks.

Using WeakSubscribe

To avoid this, you can use `WeakSubscribe`. However, `WeakSubscribe` is typically used with `INotifyPropertyChanged` events. If you want to use it with a custom event like `Loaded`, you might need to adapt your approach.

One way to use weak subscriptions in this scenario is to change your event to use `EventHandler` instead of `Action`, and then use `WeakSubscribe` on the event info:

csharp
public class MyViewModel
{
    public event EventHandler Loaded;

    // When data is loaded
    private void OnDataLoaded()
    {
        Loaded?.Invoke(this, EventArgs.Empty);
    }
}

And in your View:

csharp
public class MyView
{
    protected override void ViewJustLoaded()
    {
        var eventInfo = typeof(MyViewModel).GetEvent("Loaded");
        this.ViewModel.WeakSubscribe(eventInfo, (sender, args) => LoadControls());
    }

    private void LoadControls()
    {
        // Load controls when ViewModel is populated
    }
}

However, this approach requires implementing a custom `WeakSubscribe` extension method for `EventHandler` types without `EventArgs`, as MvvmCross's built-in `WeakSubscribe` primarily targets `INotifyPropertyChanged`.

Alternative Approach

A more straightforward approach might be to use `INotifyPropertyChanged` directly in your ViewModel and bind to a property change in the View. This allows you to use MvvmCross's built-in weak subscription mechanisms more easily:

csharp
public class MyViewModel : INotifyPropertyChanged
{
    private bool _isLoaded;
    public bool IsLoaded
    {
        get => _isLoaded;
        set
        {
            _isLoaded = value;
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(IsLoaded)));
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;

    // When data is loaded
    private void OnDataLoaded()
    {
        IsLoaded = true;
    }
}

And in your View:

csharp
public class MyView
{
    protected override void ViewJustLoaded()
    {
        this.ViewModel.PropertyChanged += ViewModel_PropertyChanged;
    }

    private void ViewModel_PropertyChanged(object sender, PropertyChangedEventArgs e)
    {
        if (e.PropertyName == nameof(MyViewModel.IsLoaded))
        {
            LoadControls();
        }
    }

    private void LoadControls()
    {
        // Load controls when ViewModel is populated
    }
}

You can then use `WeakSubscribe` on the `PropertyChanged` event:

csharp
protected override void ViewJustLoaded()
{
    this.ViewModel.WeakSubscribe(
        (sender, e) => ViewModel_PropertyChanged(sender, e));
}

Conclusion

Using `WeakSubscribe` in MvvmCross helps prevent memory leaks by avoiding strong references between ViewModels and Views. While it's primarily designed for `INotifyPropertyChanged`, you can adapt it for custom events by changing your approach slightly. Always ensure that you're holding onto the subscription tokens to manage and dispose of them properly when needed.

Citations:
[1] https://stackoverflow.com/questions/19378470/weak-referencing-in-mvvmcross-with-actiont
[2] https://stackoverflow.com/questions/23648384/weaksubscribe-in-mvvmcross-doesnt-work-correct
[3] https://www.mvvmcross.com/documentation/fundamentals/data-binding
[4] https://stackoverflow.com/a/19379912
[5] https://github.com/MvvmCross/MvvmCross/issues/1402
[6] https://github.com/MvvmCross/MvvmCross-Samples
[7] https://gregshackles.com/weak-subscription-gotcha-in-mvvmcross/
[8] https://www.mvvmcross.com/documentation/upgrading/upgrade-to-mvvmcross-60
[9] https://www.mvvmcross.com/documentation/advanced/mvxinteraction
[10] https://www.mvvmcross.com/documentation/fundamentals/linking
[11] https://www.mvvmcross.com/documentation/fundamentals/viewmodel-lifecycle