WPF – Bring the Loaded Event to MVVM

In the spirit of bringing the “Closing” event to your view model, I also decided that I wanted an easy way to bring the “Loaded” event to my view model. This one I don’t like as much as many frameworks have some kind of navigation that can take care of this for you. But if you have a simple app and want to initialize your view model when your window is loaded, you might find this helpful.

Just like in the previous article, you could wire into the “Loaded” event on each window and then delegate to the view model. This does work, but does tend to repeat the same code over and over again. Instead of this I went in and did a solution that is similar to the “Closing” event.

First up is create an interface that defines an Action that will be called when the window is loaded.

public interface ILoadedAction
{
    void WindowLoaded();
}

And a delegate based implementation of the ILoadedAction interface. I like this because it probably takes care of 99% of my cases.

public class DelegateLoadedAction : ILoadedAction
{
    public Action LoadedActionDelegate { get; set; }

    public DelegateLoadedAction()
    {
    }

    public DelegateLoadedAction(Action action)
    {
        LoadedActionDelegate = action;
    }

    public void WindowLoaded()
    {
        LoadedActionDelegate?.Invoke();
    }
}

And finally, some attached properties for our Window.

public class LoadedBindings
{
    public static readonly DependencyProperty LoadedEnabledProperty =
        DependencyProperty.RegisterAttached(
            "LoadedEnabled",
            typeof(bool),
            typeof(LoadedBindings),
            new PropertyMetadata(false, new PropertyChangedCallback(OnLoadedEnabledPropertyChanged)));

    public static bool GetLoadedEnabled(DependencyObject sender) => (bool) sender.GetValue(LoadedEnabledProperty);
    public static void SetLoadedEnabled(DependencyObject sender, bool value) => sender.SetValue(LoadedEnabledProperty, value);

    private static void OnLoadedEnabledPropertyChanged(object sender, DependencyPropertyChangedEventArgs e)
    {
        if (sender is Window w)
        {
            bool newEnabled = (bool) e.NewValue;
            bool oldEnabled = (bool) e.OldValue;

            if (oldEnabled && !newEnabled)
                w.Loaded -= MyWindowLoaded;
            else if (!oldEnabled && newEnabled)
                w.Loaded += MyWindowLoaded;
        }
    }

    private static void MyWindowLoaded(object sender, RoutedEventArgs e)
    {
        ILoadedAction loadedAction = GetLoadedAction((Window) sender);
        loadedAction?.WindowLoaded();
    }


    public static readonly DependencyProperty LoadedActionProperty =
        DependencyProperty.RegisterAttached(
            "LoadedAction",
            typeof(ILoadedAction),
            typeof(LoadedBindings),
            new PropertyMetadata(null));

    public static ILoadedAction GetLoadedAction(DependencyObject sender) => (ILoadedAction) sender.GetValue(LoadedActionProperty);
    public static void SetLoadedAction(DependencyObject sender, ILoadedAction value) => sender.SetValue(LoadedActionProperty, value);
}

I created two attached properties, one to enable or disable this feature and one to bind to the interface that contains the action to execute when the window is loaded. You could make this even simpler by removing the enabled/disabled property, but I like the flexibility of being able to turn it on and off. If you did want to do that, you would have to define a DependencyPropertyChangedEvent for the LoadedActionProperty and subscribe and unsubscribe to the Loaded event there.

To use these properties, open up your window XAML file, add in the namespace declaration and define your properties.

<Window x:Class="SampleApp.MainWindow"
    xmlns:local="clr-namespace:SampleApp"

    local:LoadedBindings.LoadedEnabled="True"
    local:LoadedBindings.LoadedAction="{Binding LoadAction}">
</Window>

In the sample above I removed some of the other declarations and assumed that our attached properties were in the same namespace as our MainWindow class.

Next, in the view model class, we simply do this:

public class MainWindowViewModel : BaseViewModel
{
    /// constructor, properties etc ...
    /// ...

    private DelegateLoadedAction _loadAction = null;
    public DelegateLoadedAction LoadAction
    {
        get
        {
            return _loadAction ??
                (_loadAction = new DelegateLoadedAction(
                    () =>
                    {
                        /// do your window initialization here
                    }));
        }
    }
}

And that is about it. Now you don’t have to subscribe the Loaded event explicitly any longer and you can consolidate your code into the view model. You can also move any of those longer running operations that were in the constructor of the view model out.

Perhaps the next step will be to turn this and the Closing event code from the previous post into a nuget package that can be just installed into a project.