Cory Petosky's website

Edit 11/17/2010:

While this article's XNA+WPF solution worked when I wrote it, in mid 2009, it no longer functions. This solution might get you halfway there, but I have not researched the other half as I am no longer regularly programming in XNA. Judging from the comments I've been getting for the last couple years, this might not even be possible.


We're writing Kung-Fu Kingdom using a platform called XNA. I've worked with a lot of game frameworks, and this particular one has a lot of advantages, but the two big ones are:

  1. You can write code in any .NET language, which means you get to use C#, a very nice programming language with a lot of great features.
  2. You can compile your project for the XBox 360 Community Games platform.

I'm compelled to note that the primary disadvantage of XNA is that it's currently (and will be for the foreseeable future) Windows only.

Now, XNA is great, and it's based on DirectX, the Microsoft graphics layer that Windows has used for ages. But it's new, and like everything else Microsoft does, when stuff is new, it doesn't work well with others. In particular, they've also recently released a new GUI framework called WPF. It's desireable in a lot of cases to mix your game framework with your GUI framework, so you can, say, make a nice looking set of tools to build your game with.

XNA and WPF don't play together nicely yet. They want to, they intend to, and Microsoft is working on making them friends, but currently it requires a set of tightly-coded leg irons to keep them together. Here's my technique for getting one inside the other.

Step 1: Make a game

This is probably the hardest step, but I know you can do it! It's beyond the scope of this article though. If you just feel like fooling with this, you can make a simple game that just redraws the screen background to a random color every frame.

Step 2: Make a WPF Window

Make a new WPF project. As far as I can tell, there's no way to directly inject XNA into WPF, so we're going to use an intermediate layer. We'll add a WinForms host to our WPF window, and then inject our XNA game into that WinForms host. Here's the basic code for your WPF window:

<Window x:Class="EditorWPF.Editor"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:wf="clr-namespace:System.Windows.Forms;assembly=System.Windows.Forms"
Title="Editor" Height="Auto" Width="Auto">
<DockPanel>
<Menu Name="MenuBar" DockPanel.Dock="Top"/>
<StatusBar Name="StatusBar" DockPanel.Dock="Bottom">
<StatusBarItem>
<TextBlock Name="statusText">Load or create a project to begin.</TextBlock>
</StatusBarItem>
</StatusBar>
<WindowsFormsHost DockPanel.Dock="Bottom" Width="800" Height="600">
<wf:Panel x:Name="RenderPanel"/>
</WindowsFormsHost>
</DockPanel>
</Window>

Be sure you've referenced the System.Windows.Forms assembly for your project, or this won't work.

The menu and status bar are just to illustrate why we're doing this -- if we just put the XNA game by itself in the window, there would be no point to this technique. Anyway, now we have a WinForms Panel available to inject our XNA game into.

Step 3: Setup your game to accept Panel reference

Go into your Game class. We're going to edit the constructor to accept a single IntPtr argument called handle. The handle parameter is the internal memory handle of the panel we created above. However, we don't muck with the display heirarchy right in the constructor -- we add an event listener to the game's GraphicsDeviceManager for PreparingDeviceSettings, and muck with it then. Here's what your code should look like:

private IntPtr handle;
private GraphicsDeviceManager graphics; public EditorGame(IntPtr handle) {
this.handle = handle;
graphics = new GraphicsDeviceManager(this);
graphics.PreparingDeviceSettings += OnPreparingDeviceSettings; this.IsMouseVisible = true;
} private void OnPreparingDeviceSettings(object sender, PreparingDeviceSettingsEventArgs args) {
args.GraphicsDeviceInformation.PresentationParameters.DeviceWindowHandle = handle;
}

Step 4: Set up your WPF Window to instantiate your Game

Usually, when you create an XNA project, it generates a simple Main function that just instantiates and runs your game for you. In this project, we're not going to use this. Instead, we're going to manually instantiate and run our game from our WPF window.

This isn't quite as simple as you might think. If we just call game.Run() somewhere, our window will stop responding until we end the game. Since we don't intend on ending the game until the WPF window is closed, this won't work. Instead, we have to spawn a second thread and run the game there. This is much easier than it sounds.

Open the code file underneath your XAML file and add these two lines to the bottom of your constructor:

IntPtr handle = RenderPanel.Handle;
new Thread(new ThreadStart(() => { game = new EditorGame(handle); game.Run(); })).Start();

And, of course, add a private game instance variable to your class:

private EditorGame game;

...and that's it! Run your WPF project and you should see your game between the status bar and the menu bar.

Optional: Breaking it down

In case you've never used threads, lambda expressions, or anonymous objects before, let me break down that weird line above.

new Thread(
new ThreadStart(
() => {
game = new EditorGame(handle);
game.Run();
}
)
).Start();

Working from the inside out:

() => {
game = new EditorGame(handle);
game.Run();
}

This is a lambda expression, a C# 3.0 language feature. It's a shorthand way of defining a function. You can do exactly the same thing with C# 2.0's anonymous function feature, but lambda expressions are shorter and more elegant. You could also, of course, define a normal instance method instead of using an anonymous thing at all, but I like anonymous functions when the scope of that function is small and restricted to a single method. The following method is entirely equivalent to the above lambda expression:

private void MyFunction() {
game = new EditorGame(handle);
game.Run();
}

The ThreadStart constructor takes a delegate as an argument. A delegate is basically a way to treat a function or method as an object. By providing the lambda expression directly as an argument, the compiler treats it as a "function object" and passes it in to the ThreadStart constructor. If you're still confused or curious, search for C# delegates, and then search for C# lambda expressions.

Our statement now looks like this:

new Thread(
new ThreadStart(
MyFunction
)
).Start();

The new ThreadStart there just instantiates a new ThreadStart object, just like every other new expression you've ever used. In this case, we're never going to use that ThreadStart object again, so I define it anonymously -- that is, I don't assign it to a variable to be reused again later. This is equivalent to the following:

ThreadStart myThreadStart = new ThreadStart(MyFunction);
new Thread(myThreadStart).Start();

The new Thread call does the same thing, except it then invokes a method on the anonymous object created. Again, this is because I don't care to worry about the thread after I create it. In the end, this whole chunk is equivalent to what I first wrote:

public Editor() {
IntPtr handle = RenderPanel.Handle;
ThreadStart myThreadStart = new ThreadStart(MyFunction);
Thread myThread = new Thread(myThreadStart)
myThread.Start();
} private void MyFunction() {
game = new EditorGame(handle);
game.Run();
}

I prefer mine myself:

IntPtr handle = RenderPanel.Handle;
new Thread(new ThreadStart(() => { game = new EditorGame(handle); game.Run(); })).Start();

But both approaches are "correct." There are many people who would advocate the longer approach, arguing that it's easier to read and maintain. I disagree, but now you have the option to include either based on your personal bias.

Originally posted July 8, 2009.

Copyright 2012 Cory Petosky. Email me: cory@petosky.net

XNA+WPF solution worked的更多相关文章

  1. Display HTML in WPF and CefSharp

    https://www.codeproject.com/articles/881315/display-html-in-wpf-and-cefsharp-tutorial-part Download ...

  2. Sharing Code Between Silverlight and WPF

    一个很好的列子: http://www.codeproject.com/Articles/254506/XAMLFinance-A-Cross-platform-WPF-Silverlight-WP7 ...

  3. Leetcode: Convert sorted list to binary search tree (No. 109)

    Sept. 22, 2015 学一道算法题, 经常回顾一下. 第二次重温, 决定增加一些图片, 帮助自己记忆. 在网上找他人的资料, 不如自己动手. 把从底向上树的算法搞通俗一些. 先做一个例子: 9 ...

  4. [转] --- Error: “A field or property with the name was not found on the selected data source” get only on server

    Error: “A field or property with the name was not found on the selected data source” get only on ser ...

  5. afterTextChanged() callback being called without the text being actually changed

    afterTextChanged() callback being called without the text being actually changed up vote8down votefa ...

  6. WebBrowser的内存释放

    WebBrowser窗口自动滚动: this.webBrowser.Document.Window.ScrollTo(0, webBrowser1.Document.Body.ScrollRectan ...

  7. Creating a CSRF protection with Spring 3.x--reference

    reference from:http://info.michael-simons.eu/2012/01/11/creating-a-csrf-protection-with-spring-3-1/ ...

  8. 编程概念--使用async和await的异步编程

    Asynchronous Programming with Async and Await You can avoid performance bottlenecks and enhance the ...

  9. LiangNa Resum

    LiangNa AnShan Street, YangPu, NY @.com OBJECTIVE: Seeking a position to contribute my skills and ed ...

随机推荐

  1. ZT 接口和实现分离

    什么叫接口和实现分离,如何实现 [问题点数:20分,结帖人heronism] http://bbs.csdn.net/topics/310212385 http://blog.csdn.net/sta ...

  2. 搞定INTEL快速存储技术(用SSD硬盘做缓存加速)

    给朋友买了个联想 ideapad s400超级本,还真是锻炼我的idea啊,原机不带WIN7系统,所以只好自己动手装WIN7,并打开24G SSD硬盘做缓存. 一.用常规方法GHOST了一个WIN7系 ...

  3. CodeIgniter框架学习要点

    以下内容从兄弟连的CI教学视频中摘抄: http://codeigniter.org.cn/tutorials/ ------------------------------------------- ...

  4. 关于项目中的DAL数据接入层架构设计

    摘要:项目中对关系型数据库的接入再寻常不过,也有海量的ORM工具可供选择,一个一般性的DAL数据接入层的结构却大同小异,这里就分享一下使用Hibernate.Spring.Hessian这三大工具对D ...

  5. 使用combineReducers注意事项

    一.从‘redux’包中引入combineReducers方法: import { combineReducers } from 'redux'; 二.针对state的不同属性写不同的reducer, ...

  6. Jenkins传参修改jmeter的报告名称和详细报告地址

    目前已经可以发送邮件了,我已经配置了Jenkins,但是有几个显示问题,待处理1.报告名称地址, 2详细报告地址没有做跳转 更改后为: 修改url 1.打开样式的jmeter-results-deta ...

  7. Executor线程池框架

    Executor线程池框架 new Thread()的缺点 每次new Thread()耗费性能 调用new Thread()创建的线程缺乏管理,被称为野线程,而且可以无限制创建,之间相互竞争,会导致 ...

  8. 在UML系统开发中有三个主要的模型

    http://www.cnblogs.com/Yogurshine/archive/2013/01/14/2859248.html 在UML系统开发中有三个主要的模型: 功能模型: 从用户的角度展示系 ...

  9. BZOJ2427:[HAOI2010]软件安装(树形DP,强连通分量)

    Description 现在我们的手头有N个软件,对于一个软件i,它要占用Wi的磁盘空间,它的价值为Vi.我们希望从中选择一些软件安装到一台磁盘容量为M计算机上,使得这些软件的价值尽可能大(即Vi的和 ...

  10. Python基础 List和Tuple类型

    python 创建list python 内置一种数据类型是列表: 列表是一种有序的集合,可以随时添加和 删除其中的元素,list 中的元素是按照顺序排列的.构建list 直接用 [ ], list ...