proper way to use BuildWindowCore params?
In the following code, I'm attempting to make my own .NET 2.0 form act as a FrameworkElement. I'm having trouble in the BuildWindowCore function. As I understand it, I'm supposed to take my existing form and make it a child of the input HWND. I don't know how to take the hwndParent.Handle and turn that into a System.Windows.Forms.Control. I've stepped through the code a number of times trying different things includeing WINAPI SetParent all with no luck. It's too bad Wrapper is null. That would at least give me an idea as to what type of object it is.
publicclassAppWindow :HwndHost
{
privateAppForm _appForm;// AppForm inherits from System.Windows.Forms.Form
public AppWindow(AppForm appForm) {
if (appForm ==null
) thrownew
ArgumentNullException("appForm"
); _appForm = appForm;
}
protectedoverride
System.Runtime.InteropServices.HandleRef BuildWindowCore(System.Runtime.InteropServices.HandleRef hwndParent) {
// hwndParent.Wrapper == null but hwndParent.Handle == some number_appForm.Parent = System.Windows.Forms.
Control.FromHandle(hwndParent.Handle); // BUG: _appForm.Parent still nullreturnnew
System.Runtime.InteropServices.HandleRef(_appForm, _appForm.Handle);}
protectedoverridevoid
DestroyWindowCore(System.Runtime.InteropServices.HandleRef hwnd){
_appForm.Dispose();
}
}
[3896 byte] By [
Brannon] at [2007-12-30]
The following code can do the trick here:
[System.Windows.Markup.ContentProperty("Child")]public class FormHost : HwndHost{ private System.Windows.Forms.Form child; public System.Windows.Forms.Form Child { get { return child; } set { child = value; } } public FormHost(System.Windows.Forms.Form child) { this.child = child; } public FormHost() : this(null) { } protected override HandleRef BuildWindowCore(HandleRef hwndParent) { HandleRef childHwnd = new HandleRef(this, child.Handle); System.Windows.Forms.Integration.WindowsFormsHost.EnableWindowsFormsInterop(); if (child != null) { SetWindowLong(Child.Handle, GWL_STYLE, WS_CHILD); SetParent(childHwnd.Handle, hwndParent.Handle); } return childHwnd; } protected override void DestroyWindowCore(HandleRef hwnd) { child.Dispose(); } public static readonly Int32 GWL_STYLE = -16; public static readonly Int32 WS_CHILD = 0x40000000; [DllImport("User32", CharSet = CharSet.Auto, ExactSpelling = true)] internal static extern IntPtr SetParent(IntPtr hWnd, IntPtr hWndParent); [DllImport("user32.dll")] static extern Int32 SetWindowLong(IntPtr hWnd, Int32 nIndex, Int32 dwNewLong); }Sheva
Thank you for the reply. I have several questions on it.
1. Would this code work on Vista? I get nervous when I see user32.dll calls.
2. Can we get these two calls into the .NET framework or the Interop in an upcoming version? They seem quite useful. Either that or add a FromHandle function to the Interop.
3. The sample code creates childHwnd without checking the child == null. That seems bad to me.
4. The childHwnd is created with a wrapper of FormHost rather than the owner of the handle. This seems to contradict the help on HandleRef to me.
The code below works on WinXP. I set this to a Window.Content. It displays, but what I want is for this to be the top level window. (Or hide the top level window and make this look like it is the top-level window.) To move a window I want to grab the title bar in this AppWindow.Child, not the title bar in the Parent of this.
[System.Windows.Markup.
ContentProperty("Child")] public class AppWindow : HwndHost {
private System.Windows.Forms.Form _child; // AppForm inherits from System.Windows.Forms.Form public AppWindow(System.Windows.Forms.Form child) : base() {
if (child == null) throw new ArgumentNullException("child"); _child = child;
}
public System.Windows.Forms.Form Child {
get { return _child; } set { _child = value; } }
public AppWindow() : this(null) { } protected override System.Runtime.InteropServices.HandleRef BuildWindowCore(System.Runtime.InteropServices.HandleRef hwndParent) {
// hwndParent.Wrapper == null but hwndParent.Handle == some number System.Windows.Forms.Integration.
WindowsFormsHost.EnableWindowsFormsInterop(); SetWindowLong(_child.Handle, GWL_STYLE, WS_CHILD);
SetParent(_child.Handle, hwndParent.Handle);
// BUG: _appForm.Parent still null return new System.Runtime.InteropServices.HandleRef(_child, _child.Handle); }
protected override void DestroyWindowCore(System.Runtime.InteropServices.HandleRef hwnd) {
_child.Dispose();
}
public static readonly Int32 GWL_STYLE = -16; public static readonly Int32 WS_CHILD = 0x40000000; [
DllImport("User32", CharSet = CharSet.Auto)] internal static extern IntPtr SetParent(IntPtr hWnd, IntPtr hWndParent); [
DllImport("User32", CharSet = CharSet.Auto)] static extern Int32 SetWindowLong(IntPtr hWnd, Int32 nIndex, Int32 dwNewLong); }
Thanks for the article on the topic, but apparently I'm still missing something on the usage. Putting an instance your FormHost class into the Content member of a Window object does some interesting things. Straight out I see two title bars: one from my existing form and one from the new Window instance. I can set WindowStyle to None, but that still does not allow me to click on the hosted title bar and drag the window around, etc. Also, I couldn't see a way to remove the border from the Window object.