我正在Visual C++
通过Direct2D
开发一个绘图应用程序。
我有一个演示应用程序,其中:
// create the ID2D1Factory
D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, &m_pDirect2dFactory);
// create the main window
HWND m_hwnd = CreateWindow(...);
// set the render target of type ID2D1HwndRenderTarget
m_pDirect2dFactory->CreateHwndRenderTarget(
D2D1::RenderTargetProperties(),
D2D1::HwndRenderTargetProperties(m_hwnd, size),
&m_pRenderTarget
);
当我收到WM_PAINT
消息时,我会画出我的形状。
现在我需要开发一个WPF控件(一种Panel)来表示我的新渲染目标(因此它会替换主窗口m_hwnd
),这样我就可以创建一个新的( C#)WPF项目,主窗口包含自定义面板的子窗口,而渲染部分保留在本机C ++ / CLI DLL项目中。
我该怎么做?我必须设置什么作为我的新渲染目标?
我想使用WPF窗口的句柄:
IntPtr windowHandle = new WindowInteropHelper(MyMainWindow).Handle;
但是我需要在我的面板上画画而不是在我的窗户上画画。
请注意,我不想将任何WPF类用于渲染部分(Shapes
,DrawingVisuals
...)
答案 0 :(得分:3)
您必须实现一个托管Win32窗口m_hwnd
的类。该类继承自HwndHost。
此外,您必须覆盖HwndHost.BuildWindowCore
和HwndHost.DestroyWindowCore
方法:
HandleRef BuildWindowCore(HandleRef hwndParent)
{
HWND parent = reinterpret_cast<HWND>(hwndParent.Handle.ToPointer());
// here create your Window and set in the CreateWindow function
// its parent by passing the parent variable defined above
m_hwnd = CreateWindow(...,
parent,
...);
return HandleRef(this, IntPtr(m_hwnd));
}
void DestroyWindowCore(HandleRef hwnd)
{
DestroyWindow(m_hwnd); // hwnd.Handle
}
答案 1 :(得分:3)
我会尝试尽可能地根据WPF 4.5 Unleashed第19章回答这个问题。如果你想查找它,你可以在子部分找到所有信息&#34;混合DirectX内容与WPF内容&#34;。
你的C ++ DLL应该有3个公开的方法Initialize(),Cleanup()和Render()。 有趣的方法是Initialize()和InitD3D(),它由Initialize()调用:
extern "C" __declspec(dllexport) IDirect3DSurface9* WINAPI Initialize(HWND hwnd, int width, int height)
{
// Initialize Direct3D
if( SUCCEEDED( InitD3D( hwnd ) ) )
{
// Create the scene geometry
if( SUCCEEDED( InitGeometry() ) )
{
if (FAILED(g_pd3dDevice->CreateRenderTarget(width, height,
D3DFMT_A8R8G8B8, D3DMULTISAMPLE_NONE, 0,
true, // lockable (true for compatibility with Windows XP. False is preferred for Windows Vista or later)
&g_pd3dSurface, NULL)))
{
MessageBox(NULL, L"NULL!", L"Missing File", 0);
return NULL;
}
g_pd3dDevice->SetRenderTarget(0, g_pd3dSurface);
}
}
return g_pd3dSurface;
}
HRESULT InitD3D( HWND hWnd )
{
// For Windows Vista or later, this would be better if it used Direct3DCreate9Ex:
if( NULL == ( g_pD3D = Direct3DCreate9( D3D_SDK_VERSION ) ) )
return E_FAIL;
// Set up the structure used to create the D3DDevice. Since we are now
// using more complex geometry, we will create a device with a zbuffer.
D3DPRESENT_PARAMETERS d3dpp;
ZeroMemory( &d3dpp, sizeof( d3dpp ) );
d3dpp.Windowed = TRUE;
d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
d3dpp.BackBufferFormat = D3DFMT_UNKNOWN;
d3dpp.EnableAutoDepthStencil = TRUE;
d3dpp.AutoDepthStencilFormat = D3DFMT_D16;
// Create the D3DDevice
if( FAILED( g_pD3D->CreateDevice( D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hWnd,
D3DCREATE_SOFTWARE_VERTEXPROCESSING,
&d3dpp, &g_pd3dDevice ) ) )
{
return E_FAIL;
}
// Turn on the zbuffer
g_pd3dDevice->SetRenderState( D3DRS_ZENABLE, TRUE );
// Turn on ambient lighting
g_pd3dDevice->SetRenderState( D3DRS_AMBIENT, 0xffffffff );
return S_OK;
}
让我们转到XAML代码:
xmlns:interop="clr-namespace:System.Windows.Interop;assembly=PresentationCore"
<Button.Background>
<ImageBrush>
<ImageBrush.ImageSource>
<interop:D3DImage x:Name="d3dImage" />
</ImageBrush.ImageSource>
</ImageBrush>
</Button.Background>
我已经使用ImageBrush将其设置为按钮的背景。我认为将其添加为背景是显示DirectX内容的好方法。但是,您可以以任何您喜欢的方式使用图像。
初始化渲染获取当前窗口的句柄并用它调用DLL的Initialize()方法:
private void initialize()
{
IntPtr surface = DLL.Initialize(new WindowInteropHelper(this).Handle,
(int)button.ActualWidth, (int)button.ActualHeight);
if (surface != IntPtr.Zero)
{
d3dImage.Lock();
d3dImage.SetBackBuffer(D3DResourceType.IDirect3DSurface9, surface);
d3dImage.Unlock();
CompositionTarget.Rendering += CompositionTarget_Rendering;
}
}
在呈现UI之前触发CompositionTarget.Rendering事件。您应该在那里呈现您的DirectX内容:
private void CompositionTarget_Rendering(object sender, EventArgs e)
{
if (d3dImage.IsFrontBufferAvailable)
{
d3dImage.Lock();
DLL.Render();
// Invalidate the whole area:
d3dImage.AddDirtyRect(new Int32Rect(0, 0, d3dImage.PixelWidth, d3dImage.PixelHeight));
d3dImage.Unlock();
}
}
基本上就是这样,我希望它有所帮助。现在只是几个重要的旁注:
应该处理事件IsFrontBufferAvailableChanged,因为有时前缓冲区可能变得不可用(例如,当用户进入锁定屏幕时)。您应该根据缓冲区可用性释放或获取资源。
private void d3dImage_IsFrontBufferAvailableChanged(object sender, DependencyPropertyChangedEventArgs e)
{
if (d3dImage.IsFrontBufferAvailable)
{
initialize();
}
else
{
// Cleanup:
CompositionTarget.Rendering -= CompositionTarget_Rendering;
DLL.Cleanup();
}
}
答案 2 :(得分:0)
可能使用WINFORMS,但不能使用WPF。 这是一篇关于WPF如何使用HWND的文档:
WPF如何使用Hwnds
要充分利用WPF“HWND互操作”,您需要了解WPF的方式 使用HWND。对于任何HWND,您不能将WPF渲染与DirectX混合使用 渲染或GDI / GDI +渲染。这有很多含义。 首先,为了完全混合这些渲染模型,你必须这样做 创建一个互操作解决方案,并使用指定的段 您选择使用的每个渲染模型的互操作。也, 渲染行为为你的东西创造了一个“空域”限制 互操作解决方案可以实现。 “空域”概念是 在“技术区域概述”主题中进行了更详细的解释。 屏幕上的所有WPF元素最终都由HWND支持。什么时候 你创建一个WPF窗口,WPF创建一个顶级HWND,并使用 HwndSource将Window及其WPF内容放在HWND中。该 应用程序中的其余WPF内容共享单个HWND。 菜单,组合框下拉菜单和其他弹出窗口是一个例外。这些 元素创建自己的顶级窗口,这就是WPF菜单的原因 可能会超过包含它的窗口HWND的边缘。 当您使用HwndHost在WPF中放置HWND时,WPF会告知Win32如何 将新子HWND相对于WPF窗口HWND定位。一个 HWND的相关概念是每个HWND内部和之间的透明度。 这也在技术区域概述主题中讨论。
从https://msdn.microsoft.com/en-us/library/ms742522%28v=vs.110%29.aspx
复制我建议你研究一种跟踪渲染区域的方法,并在它前面创建一个“可怕的”子窗口。 您可以做的其他研究是尝试查找/获取WPF图形缓冲区,并使用指针和一些高级内存编程将渲染的场景直接注入其中。
答案 3 :(得分:-2)
https://github.com/SonyWWS/ATF/可能有帮助
这是一个包含direct2d视图的关卡编辑器