I general, you should do your cleanup login in Unloaded event.
I solved this by calling Activate and Deactivate methods on my ViewModel from View's Loaded resp Unloaded event.
interface IViewModelLifeCycle
{
void Activate();
void Deactivate();
}
public class MyComponentViewModel: BindableBase, IViewModelLifeCycle {
public void Activate(){}
public void Deactivate()
}
This is basically the same principle as in Brian's answer, which I like and upvoted. This is just more generic and you are not tied to RegionManager (I don't like RegionManager)
[Optional]
in order to make it more comfortable, I have created behavior attached to view, instead of writing some code behind code:
<local:MyComponentView DataContext="{Binding MyComponentViewModel}"
local:ViewModelLifeCycleBehavior.ActivateOnLoad="True" />
<Style x:Key="PageStyle" TargetType="Page">
<Setter Property="local:ViewModelLifeCycleBehavior.ActivateOnLoad" Value="True" />
</Style>
The behavior implementation is a bit chatty, but it's actually very simple pattern. In PropertyChanged callback, attach to FrameworkElements events.
public static class ViewModelLifeCycleBehavior
{
public static readonly DependencyProperty ActivateOnLoadProperty = DependencyProperty.RegisterAttached("ActivateOnLoad", typeof (bool), typeof (ViewModelLifeCycleBehavior),
new PropertyMetadata(ActivateOnLoadPropertyChanged));
public static void SetActivateOnLoad(FrameworkElement element, bool value)
{
element.SetValue(ActivateOnLoadProperty, value);
}
public static bool GetActivateOnLoad(FrameworkElement element)
{
return (bool)element.GetValue(ActivateOnLoadProperty);
}
private static void ActivateOnLoadPropertyChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
{
if (DesignerProperties.GetIsInDesignMode(obj)) return;
var element = (FrameworkElement)obj;
element.Loaded -= ElementLoaded;
element.Unloaded -= ElementUnloaded;
if ((bool) args.NewValue == true)
{
element.Loaded += ElementLoaded;
element.Unloaded += ElementUnloaded;
}
}
static void ElementLoaded(object sender, RoutedEventArgs e)
{
var element = (FrameworkElement) sender;
var viewModel = (IViewModelLifeCycle) element.DataContext;
if (viewModel == null)
{
DependencyPropertyChangedEventHandler dataContextChanged = null;
dataContextChanged = (o, _e) =>
{
ElementLoaded(sender, e);
element.DataContextChanged -= dataContextChanged;
};
element.DataContextChanged += dataContextChanged;
}
else if (element.ActualHeight > 0 && element.ActualWidth > 0) //to avoid activating twice since loaded event is called twice on TabItems' subtrees
{
viewModel.Activate(null);
}
}
private static void ElementUnloaded(object sender, RoutedEventArgs e)
{
var element = (FrameworkElement)sender;
var viewModel = (IViewModelLifeCycle)element.DataContext;
viewModel.Deactivate();
}
}