0

I want to implement some functions when UserControl is changing to another by using MVVM. So far, I have tried:

<i:Interaction.Triggers>
    <i:EventTrigger EventName="Closing">
        <i:InvokeCommandAction Command="{Binding ClosingCommand}" />
    </i:EventTrigger>
</i:Interaction.Triggers>

ViewModel:

private ICommand _closingCommand;
public ICommand ClosingCommand
{
    get
    {
        _closingCommand = new RelayCommand(new Action<object>(OnClosingCommand));
        return _closingCommand;
    }
    set { SetProperty(ref _closingCommand, value); }
}
private void OnClosingCommand(object parameter)
{
    //My functions here
}

It works for LoadedCommand but does not work on Closing command.

thatguy
  • 21,059
  • 6
  • 30
  • 40
  • Do any of the answers here: https://stackoverflow.com/questions/502761/disposing-wpf-user-controls help? closing doesn't fire in alot of circumstances and i'm guessing you want to do some finalizing actions in that command – T.Schwarz Jun 30 '21 at 11:21

1 Answers1

1

The Loaded event is defined on FrameworkElement and UserControl is derived from it. That is why this event is raised on the user control. However, Closing is defined on Window. Since a UserControl does not inherit from Window this event will never be raised, as it is not even defined.

There is no event in UserControl that you can reliably use to detect that your view has been replaced. Even the Unloaded event can be ambiguous here.

Loaded and Unloaded might both be raised on controls as a result of user-initiated system theme changes. A theme change causes an invalidation of the control template and the contained visual tree, which in turn causes the entire control to unload and reload. Therefore Unloaded cannot be assumed to occur only on navigation away from the page.

Note that the Unloaded event is not raised after an application begins shutting down.

A safer way is to define an interface for your view models that provides a method like:

public interface IDisposableViewModel
{
   void DisposeViewModel();
}

Implement this interface in your view model to call the logic of your ClosingCommand. Then in your main window or whereever the navigation logic is implemented either access the view model directly via this interface and call DisposeViewModel() explicitly before, after or while navigating. If this logic is implemented in the view, e.g. the code behind, you can access the DataContext of the view your are replacing, check if it implements IDisposableViewModel and do the same, e.g.:

private void Navigate(UserControl currentView, UserControl nextView)
{
   // ...this is just an example, your code definitely differs.

   if (currrentView is IDisposableViewModel disposableViewModel)
      disposableViewModel.DisposeViewModel();

   RemoveView(currentView);
   SetView(nextView);
}

The point of it all is that you trigger navigation somewhere in your code and that is the point where you can explicitly clean up or execute any code necessary without any other events.

thatguy
  • 21,059
  • 6
  • 30
  • 40
  • Not working for me.. I just switch between different viewmodel(UserControl) in MainWindow contentControl and not actually close the MainWindow. – Nicholas Leong Jun 30 '21 at 11:55
  • @NicholasLeong Ok, they the question is a completely different one. The question is not about handling the `Closing` event of a window from a user control, but invoking a command when the user control is replaced in a view. Am I right? – thatguy Jun 30 '21 at 11:57
  • Yes, I just want to add some methods in UserControl A when switching to UserControl B in the same Window. – Nicholas Leong Jun 30 '21 at 12:03
  • @NicholasLeong Adapted the answer. – thatguy Jun 30 '21 at 12:23