之前在写化学分子模型制作程序的时候,使用一种方法,将WINFORM控件嵌入到XNA窗体中,从而实现了即使用WINFORM窗体控件又使用XNA。最近在写另一个物理运动学课件制作程序,同样使用XNA,但从另一个角度实现了WINFORM控件和XNA共存,并且在编码上更简单一些。

一、创建XNA工程并添加窗体

向工程添加窗体MainForm,并修改GAME1为MainGame。我们把XNA内容绘制到MainForm上,其实绘制到任何有句柄的控件都可以,即使我们绘制到桌面也未尝不可。但更少的控件能够使我们的思路更清晰和明确也不失全面:改变窗体大小时、移动窗口是如何处理XNA的显示。那么现在我们有了一个窗体,在解决首要问题:如何让XNA的内容显示在MainForm上之前,先考虑一下我们前述的MainForm移动等等问题,在MainGame.VB和MainForm.VB中互相调用难以避免,所以我们先公开这两个类。

二、公开类的相互调用和让MainForm先显示

先显示MainForm要比先显示MainGame看起来更清晰。至少我是这样认为的,因为我们有一个Program类作为程序入口控制MainGame的运行。所以,我们可以在这个类中公开它们并控制加载顺序,修改之后的类看起来是这样的:

#If WINDOWS Or XBOX Then

Module Program
Friend XnaGame As MainGame
Friend WinForm As New MainForm
''' <summary>
''' The main entry point for the application.
''' </summary>
Sub Main(ByVal args As String())
WinForm.Show()
Using Game As New MainGame()
XnaGame = Game
Game.Run()
End Using
End Sub
End Module #End If

这样MainForm的实例将先被初始化,而后运行MainGame的实例。

三、让XNA内容显示在MainForm上

确切的说是一个叫做MinWorld的Panel上,现在,我们必须拿出杀手锏来了,指定XNA的设备用指定的句柄来创建。这可以在graphics.PreparingDeviceSettings事件中实现,将句柄指定:

    Private Sub Graphics_PreparingDeviceSettings(sender As Object, e As PreparingDeviceSettingsEventArgs)
e.GraphicsDeviceInformation.PresentationParameters.DeviceWindowHandle = MinWorld.Handle
e.GraphicsDeviceInformation.PresentationParameters.BackBufferWidth = MinWorld.Width
e.GraphicsDeviceInformation.PresentationParameters.BackBufferHeight = MinWorld.Height
End Sub

在代码中,同时设置了背景缓冲区大小,使得两个窗体的大小互相匹配。当然,还需要在MainGame.VB的构造函数添加这个事件处理过程。

四、两窗体的同步过程

这包含很多问题,不仅仅是前面我所提到的窗体大小和位置同步,还包括最小化,XNA窗体在WINDOWS任务栏的显示,重置摄影机等等。为了便于编码,首先在MainGame.vb的构造函数中,初始化一个全局变量:

Friend WithEvents XNAfrm As System.Windows.Forms.Form
XNAfrm= CType(System.Windows.Forms.Form.FromHandle(Me.Window.Handle), System.Windows.Forms.Form)

XNAfrm就是MainGame启动的窗体了。这样我们就可以像操作普通窗体一样操作它。当然,如果你愿意可以不用WithEvents关键字而在构造函数中使用AddHandle来关联事件。接下来就是处理剩下的“很多问题”,假设你熟悉窗体事件,那么很容易能够看出,需要处理的问题集中在窗体大小改变和可见性变化上。

    Private Sub MainGame_VisibleChanged(sender As Object, e As EventArgs) Handles XNAfrm.VisibleChanged
If XNAfrm.Validate Then
XNAfrm.FormBorderStyle = Windows.Forms.FormBorderStyle.None
XNAfrm.Location = minWorld.PointToScreen(Drawing.Point.Empty)
XNAfrm.Size = minWorld.Size
XNAfrm.Visible = False
XNAfrm.ShowInTaskbar = False
End If
End Sub Private Sub MainGame_SizeChanged(sender As Object, e As EventArgs) Handles XNAfrm.SizeChanged
graphics.PreferredBackBufferWidth = minWorld.Width
graphics.PreferredBackBufferHeight = minWorld.Height
graphics.ApplyChanges()
Camera = New Camera2D(GraphicsDevice)
End Sub

在窗体显示时,VisibleChanged事件将会被调用,在这里为了我做了一些工作:

1、去掉窗体边框,这样窗体大小就和其窗体客户区大小匹配,有利于使窗体大小与我们偷梁换柱的控件大小保持一致。

2、指定窗体位置与实际显示控件位置一致,这可能看起来没有什么必要。但实际上不这样做在使用Mouse.GetState时出现偏移。与其后面矫正,不如现在对齐。

3、匹配窗体和实际控件大小,这可以避免画面显示不正常。

4、5、隐藏窗体和任务栏显示,注意两句的顺序。这样可以使得窗体消失——它彻底去了幕后,我们的界面就看起来就完美了。别担心,即使窗体不可见,获取鼠标键盘也不受影响。还记得用DX来偷窥其他程序的输入的代码吗?

在SizeCanged事件中,重新指定了背景缓冲区的大小,注意ApplyChanges,否则上面两两句无效。同时,我重新初始化了我的摄影机。

OK,剩下的问题就是把这两个事件利用起来。VisibleChanged没有什么好说的,是第一次显示的时候起作用。SizeCanged事件也很好引发,只需要在WinForm中重新指定XNAFrm大小就可以了。我们为了保持窗体的大小、位置等同步——成为我们实际显示控件的影子,所以需要处理两个事件:

    Private Sub MainForm_Resize(sender As Object, e As EventArgs) Handles Me.Resize
If Program.XnaGame IsNot Nothing Then
If WindowState = FormWindowState.Normal OrElse WindowState = FormWindowState.Maximized Then
Program.XnaGame.XNAFrm.Size = MinWorld.Size
Program.XnaGame.XNAFrm.Location = MinWorld.PointToScreen(Drawing.Point.Empty)
End If
If WindowState = FormWindowState.Normal OrElse WindowState = FormWindowState.Minimized Then
Program.XnaGame.XNAFrm.WindowState = WindowState
End If
End If
End Sub Private Sub MainForm_Move(sender As Object, e As EventArgs) Handles Me.Move
If Program.XnaGame IsNot Nothing Then
Program.XnaGame.XNAFrm.Location = MinWorld.PointToScreen(Drawing.Point.Empty)
End If
End Sub

需要注意的是Resize事件中的处理,在窗体最大化和恢复的时候,显示控件的位置也会移动。在窗体最小化和恢复的时候,也应该对XNAFrm进行同样的操作,否则它继续或不再接收输入将会引发以外的问题。

至此,看起来问题都解决了,原来的XNA窗体彻底成了我们显示控件的影子。但实际上,并非如此,回顾最初一段代码和XNA的架构可以预见(哦,好吧,我也是关了窗体发现进程还在发现的。可见调试的必要性,至少我这脑子漏洞很多。):XnaGame还在运行。那么,退出窗体时干掉它就万事大吉了:

    Private Sub MainForm_FormClosed(sender As Object, e As FormClosedEventArgs) Handles Me.FormClosed
If Program.XnaGame IsNot Nothing Then
Program.XnaGame.Exit()
End If
End Sub

最后,给这个小小半的半成品上个玉照:

另一种在WINFORM中使用XNA的方法的更多相关文章

  1. 一种Cortex-M内核中的精确延时方法

    本文介绍一种Cortex-M内核中的精确延时方法 前言 为什么要学习这种延时的方法? 很多时候我们跑操作系统,就一般会占用一个硬件定时器--SysTick,而我们一般操作系统的时钟节拍一般是设置100 ...

  2. C# WinForm 中Label自动换行 解决方法

    在TableLayoutPannel中放着一些Label如果把Label的AutoSize属性设成True的话,文字超过label长度时就会自动增加,直到后面的字出窗体以外设置成False时,一旦到达 ...

  3. 教你几种在SQLServer中删除重复数据方法(转)

    转载地址:http://www.jb51.net/article/22980.htm 方法一 复制代码 代码如下: declare @max integer,@id integer declare c ...

  4. js-一种去掉数组中重复元素的方法

    思路来源于某个同学的博客 function norepeat(arr){ return arr.filter(function(val,index,array) { return array.inde ...

  5. winform中按钮透明的方法

    把Button设为透明的方法:1.修改 FlatAppearance属性下的BorderSize 为0  修改 FlatStyle 的属性为 Flat 2. /// <summary>// ...

  6. asp.net中导出Excel的方法

    一.asp.net中导出Excel的方法: 本文转载 在asp.net中导出Excel有两种方法,一种是将导出的文件存放在服务器某个文件夹下面,然后将文件地址输出在浏览器上:一种是将文件直接将文件输出 ...

  7. asp.net中导出Execl的方法

    一.asp.net中导出Execl的方法: 在 asp.net中导出Execl有两种方法,一种是将导出的文件存放在服务器某个文件夹下面,然后将文件地址 输出在浏览器上:一种是将文件直接将文件输出流写给 ...

  8. 使用Opencv中matchTemplate模板匹配方法跟踪移动目标

    模板匹配是一种在图像中定位目标的方法,通过把输入图像在实际图像上逐像素点滑动,计算特征相似性,以此来判断当前滑块图像所在位置是目标图像的概率. 在Opencv中,模板匹配定义了6种相似性对比方式: C ...

  9. 转载:WinForm中播放声音的三种方法

    转载:WinForm中播放声音的三种方法 金刚 winForm 播放声音 本文是转载的文章.原文出处:http://blog.csdn.net/jijunwu/article/details/4753 ...

随机推荐

  1. Oracle生成指定表的列名,并前后添加select from

    表的列名比较多的时候,手工一个个的写列名比较麻烦,这个函数可以让人偷偷懒 create or replace function f_GetCols(p_TableName in varchar2/*获 ...

  2. 【python】类(资料+疑惑)

    1.http://python-china.org/t/77 有关method binding的理解 2.[Python] dir() 与 __dict__,__slots__ 的区别 3.Descr ...

  3. [译]SSAS下玩转PowerShell

    操作SSAS数据库的方法有很多,是否有一种可以方法可以通过脚本自动去做这些事呢,比如处理分区,创建备份以及监视SSAS的运行状况. 原文地址: http://www.mssqltips.com/sql ...

  4. UVA 10780 Again Prime No Time.(数学)

    给定两个整数m和n,求最大的k使得m^k是n!的约数 对m质因子分解,然后使用勒让德定理求得n!包含的质数p的阶数,min(b[i] / a[i])即为结果k, 若为0无解 #include<c ...

  5. git学习(一):建立本地仓库和基本命令

    前沿 最近一直在做目标跟踪,开始一直是通过文件按日期命名的方式来区分版本的,实在是太麻烦了,现在下定决心学习一下git命令 基本概念 集中式:有一台中央服务器,每个人把需要改的部分拿回去改完再送回来 ...

  6. 系统吞吐量(TPS)、用户并发量

    PS:下面是性能测试的主要概念和计算公式,记录下: 一.系统吞度量要素: 一个系统的吞度量(承压能力)与request对CPU的消耗.外部接口.IO等等紧密关联. 单个reqeust 对CPU消耗越高 ...

  7. Linux下xampp集成环境安装配置方法 、部署bugfree及部署禅道

    XAMPP(Apache+MySQL+PHP+PERL)是一个功能强大的建站集成软件包.XAMPP 是一个易于安装且包含 MySQL.PHP 和 Perl 的 Apache 发行版.XAMPP 的确非 ...

  8. JavaScript预解析1

    var 不一定是用来定义局部变量的 jscript的全局变量和局部变量的分界是这样的:                  过程体(包括方法function,对象Object o ={})外的所有变量不 ...

  9. Jquery AJAX ASP.NET IIS 跨域 超简单解决办法

    第一种: 在IIS添加如下标头即可 Access-Control-Allow-Headers:Content-Type, api_key, AuthorizationAccess-Control-Al ...

  10. JS仿淘宝星星评价

    //直接复制过去就可以了(你也可以吧css和js封装成css和js文件导入). <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transit ...