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