19

I need to host a Win32 window in a Windows Form control. I had the same problem with WPF and I solved this problem by using the HwndHost control.

I followed this tutorial:

Walkthrough: Hosting a Win32 Control in WPF

Is there any equivalent control in Windows Form?

I have a Panel and its Handle property, I use this handle as parent of my Direct2D render target window:

// Register the window class.
        WNDCLASSEX wcex = { sizeof(WNDCLASSEX) };

        // Redraws the entire window if a movement or size adjustment changes the height 
        // or the width of the client area.
        wcex.style         = CS_HREDRAW | CS_VREDRAW;
        wcex.lpfnWndProc   = Core::WndProc;
        wcex.cbClsExtra    = 0;
        wcex.cbWndExtra    = sizeof(LONG_PTR);
        wcex.hInstance     = HINST_THISCOMPONENT;
        wcex.hbrBackground = nullptr;
        wcex.lpszMenuName  = nullptr;
        wcex.hCursor       = LoadCursor(nullptr, IDI_APPLICATION);
        wcex.lpszClassName = L"SVGCoreClassName";

        RegisterClassEx(&wcex);

hwnd = CreateWindow(
            L"SVGCoreClassName",        // class name
            L"",                        // window name
            WS_CHILD | WS_VISIBLE,      // style
            CW_USEDEFAULT,              // x
            CW_USEDEFAULT,              // y
            CW_USEDEFAULT,              // width
            CW_USEDEFAULT,              // height
            parent,                     // parent window
            nullptr,                    // window menu
            HINST_THISCOMPONENT,        // instance of the module to be associated with the window
            this);                      // pointer passed to the WM_CREATE message

...

hr = d2dFactory->CreateHwndRenderTarget(
        D2D1::RenderTargetProperties(),
        D2D1::HwndRenderTargetProperties(hwnd, size, D2D1_PRESENT_OPTIONS_IMMEDIATELY),
        &renderTarget);

The code works if I use the HwndHost parent handle with WPF. But it does not render anything if I use the System.Windows.Forms.Panel Handle.

Nick
  • 10,309
  • 21
  • 97
  • 201
  • You may be interested in [`NativeWindow`](https://msdn.microsoft.com/en-us/library/system.windows.forms.nativewindow.aspx). – Lucas Trzesniewski Apr 16 '15 at 16:17
  • All Winforms controls are already Win32 windows so "hosting" doesn't make much sense. You'll need to do a better job describing the nature of this window. Who owns it? How do you create it? – Hans Passant Apr 16 '15 at 19:08
  • It there any error checking in this code? You need if (hwnd == IntPtr.Zero) throw new System.ComponentModel.Win32Exception(); Now you'll know why it doesn't work. – Hans Passant Apr 20 '15 at 08:52
  • @HansPassant My hwnd is a valid pointer, it is not null. All the previous checks I made with WPF still remain the same, and all the tests are passed. – Nick Apr 20 '15 at 08:54

2 Answers2

8

What you had to do in WPF to create a valid D2D1 target window is not necessary in Winforms. It was necessary in WPF because controls are not windows themselves and don't have a Handle property, the one that CreateHwndRenderTarget() needs.

In Winforms the Panel class is already a perfectly good render target, you can use its Handle property to tell D2D1 where to render. You just need to tell it to stop painting itself. Add a new class to your project and paste this code:

using System;
using System.Windows.Forms;

class D2D1RenderTarget : Control {
    protected override void OnHandleCreated(EventArgs e) {
        base.OnHandleCreated(e);
        if (!this.DesignMode) {
            this.SetStyle(ControlStyles.UserPaint, false);
            // Initialize D2D1 here, use this.Handle
            //...
        }
    }
}

Compile. Drop the new control onto your form, replacing the existing panel.

Hans Passant
  • 922,412
  • 146
  • 1,693
  • 2,536
  • I did not understand what handle I have to pass as the parent argument of the CreateWindow function. I tried by using the Control.Handle property, in the OnHandleCreated method, but it does not work. Please see my edit too. – Nick Apr 20 '15 at 12:09
  • Moreover I noticed that the CreateParams property is not called (when I set a breakpoint in it). – Nick Apr 20 '15 at 12:20
  • Don't, it is entirely automatic when you drop the control on the panel. If you do it in code then you must use the form's Controls.Add() method to add the control to the panel. After you added some code, this looks **completely** unnecessary. All that CreateHwndRenderTarget() needs is a valid window handle. The panel's Handle property is already quite good enough, there is no point at all in helping more. What that C++ code did is the same that Panel does. Shame that you posted such a sucky snippet. – Hans Passant Apr 20 '15 at 12:27
  • I am sorry but I still do not understand. I have The C++ code I posted where I create a window and set its parent. Then I use the window handle to create my Direct2D render target. With WPF I used the HwndHost control and the BuildWindowCore method to get the handle (the parent argument in the CreateWindow function), it worked. Now with the Forms.Control class I'm unable to do the same thing, that is I don't know where, when and what handle I have to pass to my C++ code as I did previously with WPF. Could you be clearer? – Nick Apr 20 '15 at 12:33
  • You needed to jump through those hoops in WPF because controls don't have a Handle property. It is not necessary in Winforms. – Hans Passant Apr 20 '15 at 12:34
  • Ok, so could you add in your code where, when and what handle I have to pCass to my c++ CreateWindow function? Thanks. Or have I to remove the CreateWindow function completely and use the Control.Handle? – Nick Apr 20 '15 at 12:37
  • You don't get it. Do **not** create a window, you already have one. The panel is a window. – Hans Passant Apr 20 '15 at 12:40
  • It does not work properly but I'm able to see something now... When I used the RegisterClassEx function I setted the lpfnWndProc to a static c++ class function. Now that I'm using the Forms.Control class how can I set the procedure to the same Core::WndProc function? – Nick Apr 20 '15 at 12:58
  • I rewrote the answer. Not much I can do about you doggedly persisting in doing this wrong, good luck with it. – Hans Passant Apr 20 '15 at 13:03
  • Hmm, everything has a limit and my free time to help other programmers is certainly not limitless. We all have a family and a job to attend to as well. Your welcome anyway. [Read this](http://meta.stackexchange.com/questions/66377/what-is-the-xy-problem) to avoid falling in this trap in the future. – Hans Passant Apr 23 '15 at 10:28
1

Sounds like an MDI application. Something like this?

[DllImport("user32.dll")]
private static extern IntPtr SetParent(IntPtr hWndChild, IntPtr hWndNewParent);

[DllImport("user32.dll")]
internal static extern bool MoveWindow(IntPtr hWnd, int X, int Y, int nWidth, int nHeight, bool bRepaint);

Form f = new Form();
Button b1 = new Button { Text = "Notepad" };
b1.Click += delegate {
    using (var p = Process.Start("notepad.exe")) {
        p.WaitForInputIdle();
        SetParent(p.MainWindowHandle, f.Handle);
        MoveWindow(p.MainWindowHandle, 50, 50, 300, 300, true);
    }
};
f.Controls.Add(b1);
Application.Run(f);
Loathing
  • 5,109
  • 3
  • 24
  • 35