2
votes

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?

2
@MichaelGunter i call my view inside the constructor of my viewmodelEhsan Akbar
@MichaelGunter as you know about the previous question i just want to get the handler of my windows ,and you said i have to create a function inside my view to return the valueEhsan Akbar
May I edit your question in order to make it more precise? I can then provide a direct answer.Michael Gunter
@MichaelGunter yes why not ,i will be appreciatedEhsan Akbar
@MichaelGunter thank you MichaelEhsan Akbar

2 Answers

4
votes

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;
    }
}
1
votes

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;
}