Ehsan Akbar
Ehsan Akbar

Reputation: 7301

Passing window handle to view model in WPF with MVVM

I am using an external library which requires a window handle. My application architecture is MVVM, but the external library doesn't exactly fit into this architecture. I have decided that the view model is the most appropriate place to call an initialization function which requires a window handle. How can I get the window handle from my view to my view model?

Upvotes: 3

Views: 5142

Answers (2)

pdm
pdm

Reputation: 21

This can also be achieved without the View having access to the ViewModel by using a command.

In the View.xaml.cs

private void View_Loaded(object sender, RoutedEventArgs e)
{
    // Get the Window
    var win = WinTools.FindParentWindow(this);
    if (win != null)   // stops designer errors
    {
        var winHan = new WindowInteropHelper(win).Handle;
        // Execute the command with Window handle as parameter
        WinInfo?.Execute(winHan);   
    }
}

// Command Property
public ICommand WinInfo
{
    get => (ICommand)GetValue(WinInfoProperty);
    set => SetValue(WinInfoProperty, value);
}

public static readonly DependencyProperty WinInfoProperty =
            DependencyProperty.RegisterAttached("WinInfo",
            typeof(ICommand),
            typeof(View));

In the xaml

<View WinInfo="{Binding WinInfoCmd}" />

And finally in the ViewModel.cs

private IntPtr _winHandle;

// setup to receive command
private RelayCommand _winInfoCmd;
public ICommand WinInfoCmd { get { return _winInfoCmd ??= new RelayCommand(o => SetWin((IntPtr)o), o => true); } }

private void SetWin(IntPtr han)
{
    _winHandle = han;
}

Upvotes: 2

Michael Gunter
Michael Gunter

Reputation: 12811

Typically, your view model shouldn't know about the implementation details of your view (such as it's HWND). However, as your question indicates, the external library you are using requires you to initialize it, and you can only do that in one place. Assuming that your view model is the most appropriate place for it (it might even belong in the model), you can do something like the following.

This implementation provides the window handle to your view model as soon as all the pieces are available. Note that the view model implementation that you provided in your previous question requires the HWND in the view model's constructor. You're going to have to change your view model so that the initialization happens via an explicitly called method or property. In the code below, I assume that there is a method in your view model called OnWindowHandleAvailable. You could certainly call that method Initialize instead, or you could put a Handle property on your view model which you explicitly set.

public partial class View
{
    public View()
    {
        InitializeComponent();
        this.Loaded += View_Loaded;
        this.DataContextChanged += View_DataContextChanged;
    }

    private void View_Loaded(object sender, RoutedEventArgs e)
    {
        GiveWindowHandleToViewModel();
    }

    private void View_DataContextChanged(object sender, DependencyPropertyChangedEventArgs e)
    {
        GiveWindowHandleToViewModel();
    }

    private void GiveWindowHandleToViewModel()
    {
        // get view model
        var viewModel = this.DataContext as ViewModel;
        if (viewModel == null)
            return;

        // get window handle
        var windowHandle = this.GetWindowHandle();
        if (windowHandle == IntPtr.Zero)
            return;

        // signal view model
        viewModel.OnWindowHandleAvailable(windowHandle);
    }

    private IntPtr GetWindowHandle()
    {
        // get window
        var window = Window.GetWindow(this);
        if (window == null)
            return IntPtr.Zero;

        // get window handle
        return new WindowInteropHelper(window).Handle;
    }
}

Upvotes: 5

Related Questions