Hands-On Lab
XNA 3D 模型浏览器
Lab version: 1.0.0
Last updated: 2/29/2012
内容
任务 1 – 使用background transfer 服务... 5
任务 2 – 将 3D XNA模型嵌入到 Silverlight 应用程序中... 11
WindowsPhone提供了两种不同的用户界面编程模型:Silverlight和 XNA。Silverlight是最常用的应用程序开发方式,应用程序可以使用控件、显示很多数据。XNA是2D和3D游戏开发方式。当年您也可以使用Silverlight来写游戏,或者使用XNA来编写一个以数据为中心的应用程序。每个应用程序框架都有其自身的优势;选择合适的编程模型,可以使您的生产率大幅提高,并且缩短应用上市时间。
在Windows Phone 7版本中,您不得不选择一个应用程序框架来开发您的应用程序或游戏。而Windows® Phone Mango中允许您同时使用两个编程框架,Silverlight应用程序中可以嵌入XNA模块,XNA应用程序中也可以嵌入Silverlight元素。这样的话,可以保持两个应用程序框架的优势。例如,在Windows Phone Mango之前,应用程序开发者在XNA中展现非拉丁字母的文字时会比较困难。UIElementRenderer提供了一个强制方法来解决这个问题。您可以使用UIElementRenderer来展现SilverlightTextBlock的元素(或者其他可以显示字体的颜色)。通过Silverlight的高级功能和矢量图,文本将会以XNA材质的方式进行显示。
WindowsPhone Mango支持新的background transfer服务,可以独立运行您的应用程序。当您的应用程序处于隐藏状态或者用户导航到其他页面时,Transfers可以继续执行。
本次实验将会演示如何使用background transfer service来下载嵌入的XNA模型。
提示: 这次动手实验是基于Windows Phone Mango的beta版,因此RTM在API方面可能会有一些变化。
课程目标
本次实验将帮助您完成下列目标:
· 编写应用程序使用background transfer service服务
· 在Silverlight应用程序中嵌入3D XNA 模型
前提条件
您在开始本次动手实验前,请先确认达到下列前提条件:
· Microsoft Visual Studio 2010 或者 Microsoft Visual C# Express2010, 和 Windows® Phone 7 Codenamed “Mango” Developer Tools,下载地址:http://go.microsoft.com/?linkid=9772716
· 如何创建 Windows® Phone 7应用程序的支持
(如果您是新的Windows® Phone 7开发者,您可以首先阅读: http://msdn.microsoft.com/en-us/gg266499).
实验 提纲
这个动手实验将包括一个完整的实验,包括下列的任务:
1. 编写应用程序使用background transfer service服务
2. 在Silverlight应用程序中嵌入3D XNA 模型
预计完成时间
完成该实验预计花费15到30分钟的时间。
本次课程的起始点在实验安装目录的Source\Begin文件夹中,打开里边的解决方案。
任务 1 – 使用 background transfer service服务
该应用程序从远程下载位置上,下载XNA模型。预先下载的模型将会出现在LocalModels列表中,而能够被下载的模型将出现在RemoteModels列表中。
本次任务将演示如何使用BackgroundTransferService类开始一个新的transfer或者管理已有的transfer。当用户点击Download按钮,应用程序开始下载并且监控其进度,当下载完成时,将该项目移动到Local Models列表中。
提示: 使用BackgroundTransferService 类和相关的API 类,需要添加Microsoft.Phone程序集的引用。该引用在Windows Phone 7应用程序工程中被默认添加。
1. 创建一个新的background transfer非常简单。您只需要创建新的 BackgroundTransferRequest 对象,并将其添加到BackgroundTransferService队列中。 BackgroundTransferRequest将信息存储在一个单独的传输队列中。
打开ModelViewer\Downloads工程文件夹下的Download.cs 文件并用下列代码替换空的Start方法:
C#
public void Start()
{
//Create a new backgroundtransfer request
BackgroundTransferRequest request = new BackgroundTransferRequest(requestUri,downloadUri);
requestId =request.RequestId;
//Add new request to the queue
BackgroundTransferService.Add(request);
}
requestUri 变量指定文件从哪里下载,而downloadLocation变量指定本地文件的存储位置。requestId变量将在稍后使用。
当应用程序添加一个传输请求到BackgroundTransferService队列时,Windows Phone操作系统在一个后台任务中执行该请求。用户如果从应用程序中推出,传输仍然将继续。
提示: 您的传输请求也许不会被立即执行。后台传输服务使用特殊的调度程序来管理传输请求。例如,当电量不足时,为了节省电池,它也许会暂停当前活动的传输。
2. 为了在Local Models列表中添加下载完成的模型,应用程序必须监听BackgroundTransferRequest的TransferStatusChanged事件。该事件会传递BackgroundTransferEventArgs对象, 其中会包含触发事件的请求的引用。通过检查请求的TransferStatus属性,你可以检测下载是否完成和响应是否正确。在这个例子中,您需要移动下载文件到本地XNA模型存储的位置。
在Start方法中的请求启动之前,监听TransferStatusChanged事件:
C#
public void Start()
{
//Create a new backgroundtransfer request
BackgroundTransferRequest request = new BackgroundTransferRequest(requestUri,downloadUri);
requestId =request.RequestId;
//Subscribe to the events
request.TransferStatusChanged+= request_TransferStatusChanged;
//Add new requestto the queue
BackgroundTransferService.Add(request);
}
一旦下载完成,事件处理函数将移动文件到正确位置,通知应用程序,然后将BackgroundTransferRequest对象从队列中移除。WindowsPhone 7不会从队列中自动移除完成的传输,所以开发者需要自己删除。因此应用程序不需要监视传输请求,事件处理方法会从TransferStatusChanged事件中清除掉。添加下列的方法:
C#
private void request_TransferStatusChanged(object sender, BackgroundTransferEventArgse)
{
BackgroundTransferRequest request = e.Request;
if(request.TransferStatus == TransferStatus.Completed)
{
request.TransferStatusChanged -= request_TransferStatusChanged;
OnDownloadFinished(request);
}
}
private void OnDownloadFinished(BackgroundTransferRequest request)
{
//Move downloadedfile to its final location
Storage.MoveFile(downloadPath, targetPath);
//Notify listeners(UI) about download complete
if(DownloadFinished != null)
DownloadFinished(this, EventArgs.Empty);
//Remove the requestfrom the queue
BackgroundTransferService.Remove(request);
}
3. 同样,您可以通过BackgroundTransferRequest的TransferProgressChanged事件,通过传递的BackgroundTransferEventArgs对象来监听处理过程。Download对象会向应用程序报告下载的进度,ModelMetadata会跟踪进度并更新UI上的蓝色进度条。
在Start方法中添加事件处理函数:
C#
public void Start()
{
request.TransferStatusChanged += request_TransferStatusChanged;
request.TransferProgressChanged+= request_TransferProgressChanged;
…
}
事件处理函数将会检查传输是否激活。如果需要,可以从BackgroundTransferEventArgs对象中的Request属性中获取进度信息。最终将触发DownloadProgress事件:
C#
private void request_TransferProgressChanged(object sender, BackgroundTransferEventArgse)
{
BackgroundTransferRequest request = e.Request;
//While the transferis still active
if(request.TransferStatus== TransferStatus.Transferring)
{
//Notifyabout progress change
if(DownloadProgress!= null)
DownloadProgress(this, new DownloadProgressEventArgs(request.BytesReceived, request.TotalBytesToReceive));
}
}
最后,但不是最重要的,当下载完成时,你必须清除不用的事件处理函数。修改request_TransferStatusChanged方法:
C#
private void request_TransferStatusChanged(object sender, BackgroundTransferEventArgse)
{
…
request.TransferStatusChanged -= request_TransferStatusChanged;
request.TransferProgressChanged -=request_TransferProgressChanged;
…
}
提示: 当应用程序休眠或关闭时,后台传输服务会继续运行。在这个过程中,服务队列继续所有的处理和状态事件,等待应用程序恢复。当你的应用程序恢复,事件将会被触发,因此你的应用程序可以收到在后台状态下的所有更新。
4. 应用程序允许用户取消进行中的下载。当下载开始时,UI会在RemoteModels列表中显示一个带“X”的按钮。当用户点击这个按钮时,ModelMetadata对象调用Abort方法,为每一个Download对象设置模块对象。
Abort 方法调用BackgroundTransferService的 Find 方法根据特别的requestId,来恢复BackgroundTransferRequest。如果发现了对应的请求,应用程序从队列中删除掉。
C#
public bool Abort()
{
BackgroundTransferRequest request = BackgroundTransferService.Find(requestId);
if (request !=null)
{
isAborted = true;
BackgroundTransferService.Remove(request);
}
return request !=null;
}
提示: 后台传输服务API提供访问自己应用程序的传输请求队列。你不能访问其他应用程序的传输队列。
当BackgroundTransferRequest 从队列中去掉时,BackgroundTransferService触发TransferStatusChanged事件,设置请求的TransferStatus属性为TransferStatus.Completed,就如同请求没有被取消一样。Abort方法设置isAborted标记位为true,因此TransferStatusChanged事件控制要分清这两个情况:
C#
private void request_TransferStatusChanged(object sender,BackgroundTransferEventArgs e)
{
…
if (isAborted)
OnDownloadAborted();
else
OnDownloadFinished(request);
…
}
OnDownloadAborted 方法删除临时下载文件,触发DownloadAborted事件通知应用程序:
C#
private void OnDownloadAborted()
{
//Delete the temporarydownload file
Storage.DeleteFile(downloadPath);
//Notifyabout the aborted download
if(DownloadAborted!= null)
DownloadAborted(this, EventArgs.Empty);
}
5. Windows Phone 7 Mango限制后台传输请求队列最多为5个。添加更多的请求将会导致应用程序抛出异常。因为Remote Models里的每一个项目也许会产生多于一个的传输请求,用户也许会尝试同时下载所有的远程模型,这样会产生过多的同时请求。
应用程序可以在DownloadManager类中实现自己的内存队列,可以解决这个问题。DownloadManager的StartDownload和ProcessPendingDownloads方法演示了如何使用这个队列。
C#
public static voidStartDownload(Download download)
{
if(BackgroundTransferService.Requests.Count()< 5)
{
…
download.Start();
}
else
{
pendingDownloads.Enqueue(download);
}
}
private static voidProcessPendingDownloads()
{
if(pendingDownloads.Count > 0)
{
Download download = pendingDownloads.Dequeue();
StartDownload(download);
}
}
当活动下载数量达到限制,应用程序将请求加入到pendingDownloads队列,而不会加入到BackgroundTransferService队列中。
提示: 在 DownloadManager 类中使用的队列是简单的实现。因为下载等待队列不能持续保留(例如,在应用程序的独立存储中),如果应用程序关闭或启动墓碑机制,他们将不会被恢复。这个实现中没有包括最佳实践,只适合本次动手实验。
任务 2 – 将3D XNA 模型嵌入到 Silverlight 应用程序
当用户选择本地模型,应用程序导航到GamePage页面,用Silverlight内容来显示XNA模型。本次任务将演示如何创建Silverlight/XNA混合应用程序。
实现整合功能的关键组件是SharedGraphicsDeviceManager类,该类让应用程序开发者使用XNA绘制的直接模式来代替Silverlight绘制的保留模式。
当您安装Windows Phone Development Tools,两个工程模板支持Silverlight/XNA混合模式的应用程序,Silverlightfor Windows Phone文件夹下的WindowsPhone 3D Graphics Application模板和XNA Game Studio 4.0文件夹下的Windows Phone Rich Graphics Application (4.0) 模板。
提示: 这些模板在标准的Windows Phone Application模板基础上增加了一些小的改进。首先在App.xaml.cs中声明SharedGraphicsDeviceManager变量;其次,App类实现IServiceProvider接口为了模拟纯粹XNA应用程序的行为。IServiceProvider.GetService方法返回存在于ApplicationLifetimeObjects集合中的对象。
1. 为了在Silverlight页面中,显示3D XNA模型。打开GamePage.xaml.cs文件,添加OnNavigatedTo方法:
C#
protected override void OnNavigatedTo(NavigationEventArgs e)
{
// Set the sharingmode of the graphics device to turn on XNA rendering
SharedGraphicsDeviceManager.Current.GraphicsDevice.SetSharingMode(true);
base.OnNavigatedTo(e);
}
2. 只有这一页需要XNA渲染,因此你需要在离开该页面前关闭XNA渲染。添加OnNavigatedFrom方法:
C#
protected override voidOnNavigatedFrom(NavigationEventArgs e)
{
// Set the sharingmode of the graphics device to turn off XNA rendering
SharedGraphicsDeviceManager.Current.GraphicsDevice.SetSharingMode(false);
base.OnNavigatedFrom(e);
}
3. 通过SharedGraphicsDeviceManager的操作打开XNA渲染模式,让操作系统使用XNA渲染取代Silverlight渲染,但是不需要实际打开XNA游戏循环。WindowsPhone Mango使用GameTimer类,用来运行XNA引擎并将一些标准的Microsoft.Xna.Framework.Game生命周期方法作为事件暴露出来,使你的应用程序可以被控制。
在绘制任何XNA内容之前,你不得不创建GameTimer类型,并启动游戏引擎。在GamePage类中,声明GameTimer的变量timer。
C#
public partial class GamePage : PhoneApplicationPage
{
GameTimer timer;
…
}
找到GamePage构造函数,创建实际的对象,注册Update和Draw事件。设置GameTimer的UpdateInterval属性,指定触发Update事件的时间间隔,设备触发Draw事件的时间间隔与此相同。
C#
public GamePage()
{
InitializeComponent();
// Create a timer for this page
timer = new GameTimer();
timer.UpdateInterval = TimeSpan.FromTicks(333333);
timer.Update += timer_Update;
timer.Draw += timer_Draw;
}
private void timer_Update(object sender, GameTimerEventArgs e)
{
}
private void timer_Draw(object sender, GameTimerEventArgs e)
{
SharedGraphicsDeviceManager.Current.GraphicsDevice.Clear(Color.CornflowerBlue);
}
4. 为了启动和关闭XNA引擎,在OnNavigatedTo方法中调用GameTimer的Start方法,在OnNavigatedFrom方法中调用Stop方法。
C#
protected override void OnNavigatedTo(NavigationEventArgse)
{
// Set the sharingmode of the graphics device to turn on XNA rendering
SharedGraphicsDeviceManager.Current.GraphicsDevice.SetSharingMode(true);
// Start the game timer
timer.Start();
base.OnNavigatedTo(e);
}
protected override void OnNavigatedFrom(NavigationEventArgse)
{
// Stop the game timer
timer.Stop();
// Set thesharing mode of the graphics device to turn off XNA rendering
SharedGraphicsDeviceManager.Current.GraphicsDevice.SetSharingMode(false);
base.OnNavigatedFrom(e);
}
5. 现在XNA绘制的引擎已经开始运行了,在实际绘制内容之前,您需要一个最终的对象。 SpriteBatch类负责绘制多个基于可视化元素的精灵。在接下来的几步中,您将使用SpriteBatch实例来绘制背景图片和Silverlight内容。但是,首先您需要初始化它。
声明 SpriteBatch 变量叫做spriteBatch:
C#
public partial class GamePage : PhoneApplicationPage
{
GameTimer timer;
SpriteBatch spriteBatch;
…
}
6. 在 OnNavigatedTo方法中实现该对象:
C#
protected override void OnNavigatedTo(NavigationEventArgse)
{
// Set the sharingmode of the graphics device to turn on XNA rendering
SharedGraphicsDeviceManager.Current.GraphicsDevice.SetSharingMode(true);
//Initialize SpriteBatch
spriteBatch = new SpriteBatch(SharedGraphicsDeviceManager.Current.GraphicsDevice);
// Start the gametimer
timer.Start();
base.OnNavigatedTo(e);
}
7. 下一步来绘制位于ModelViewerLibraryContent工程的背景图片background.jpg。 为了绘制图片,通过ContentManager进行加载,然后通过spriteBatch进行绘制。
在GamePage类中,声明 Texture2D 类型的变量叫做background ,来加载背景图片。这是一个简单的材质,用来在XNA绘制引擎里进行绘制。
C#
public partial class GamePage : PhoneApplicationPage
{
…
SpriteBatch spriteBatch;
Texture2D background;
…
}
8. ModelViewerLibraryContent工程中包含预编译的资源,当ModelViewer工程编译时,并将其嵌入到Silverlight程序集里。应用程序需要ContentManager在运行时加载这些资源。
根据XNA的需求,在App类中实现IServiceProvider接口。现在你可以使用它在OnNavigatedTo方法中来创建ContentManager。使用ContentManager将预编译的背景图片,加载为材质,并且设置到background变量中去。
C#
protectedoverride void OnNavigatedTo(NavigationEventArgse)
{
…
//Initialize SpriteBatch
spriteBatch = new SpriteBatch(SharedGraphicsDeviceManager.Current.GraphicsDevice);
App app = (App)Application.Current;
ContentManager appContentManager = new ContentManager(app,"Content");
background = appContentManager.Load<Texture2D>("background");
// Startthe game timer
timer.Start();
base.OnNavigatedTo(e);
}
9. 最后,在timer_Draw事件处理函数中使用SpriteBatch对象绘制图片。
C#
private void timer_Draw(object sender, GameTimerEventArgs e)
{
…
spriteBatch.Begin();
spriteBatch.Draw(background, Vector2.Zero,Color.White);
spriteBatch.End();
}
运行程序确认 XNA可以正常显示背景图片。
10. 绘制3D模型比绘制背景图片略微苦难一些。首先需要替代预编译资源,需要从独立存储中加载3D模型资源。首先要声明嵌入3D模型和其元数据的成员变量:
C#
public partial class GamePage : PhoneApplicationPage
{
…
XnaModelWrapper model;
ModelMetadatamodelMetadata;
…
}
11. 使用content manager来加载模型,就像加载背景图片一样。因为这些模型通过查询字符串,被传递到GamePage页面,而不是预编译的,你不得不使用Model\CustomContentManager.cs位于的自定义内容管理器,从独立存储中加载模型。在OnNavigatedTo方法中加载模型和元数据:
C#
protected override void OnNavigatedTo(NavigationEventArgse)
{
…
background = appContentManager.Load<Texture2D>("background");
//Get query string parameter andinitialize local metadata variable
IDictionary<string, string>data = NavigationContext.QueryString;
modelMetadata = app.ModelsStore[data["ID"]];
//Initialize the model
model = new XnaModelWrapper();
model.Lights = new bool[] { true, true, true };
ContentManager contentManager = modelMetadata.IsContent? appContentManager : new CustomContentManager();
model.Load(contentManager,modelMetadata.Assets[0]);
// Startthe game timer
timer.Start();
base.OnNavigatedTo(e);
}
提示: 下面的代码中包含XNA和3D模型,这些超出了本次实验的范围。为了学习更多关于XNA的知识,可以参考XNA的文档。
12. SpriteBatch只能绘制Texture2D对象,因此你不能使用它来绘制3D模型。为了绘制模型,调用XnaModelWrapper类的Draw方法。编辑GamePage类中timer_Draw事件处理函数来绘制模型:
C#
private void timer_Draw(object sender, GameTimerEventArgs e)
{
spriteBatch.Begin();
spriteBatch.Draw(background, Vector2.Zero, Color.White);
spriteBatch.End();
// Set render states.
SharedGraphicsDeviceManager.Current.GraphicsDevice.DepthStencilState= DepthStencilState.Default;
SharedGraphicsDeviceManager.Current.GraphicsDevice.BlendState= BlendState.Opaque;
SharedGraphicsDeviceManager.Current.GraphicsDevice.RasterizerState= RasterizerState.CullCounterClockwise;
SharedGraphicsDeviceManager.Current.GraphicsDevice.SamplerStates[0]= SamplerState.LinearWrap;
// Draw the model
model.Draw();
}
13. 您也会更新模型的属性,在3D空间中正确的显示。当GameTimer触发Draw事件时,模型会被绘制;当触发Update事件时,执行应用程序的逻辑,并计算模型位置。
在timer_Update事件处理函数中更新模型:
C#
private void timer_Update(object sender, GameTimerEventArgs e)
{
float yaw = MathHelper.Pi + MathHelper.PiOver2; //rotation around the y-axis
float pitch = 0; // rotation around the x-axis
floatfieldOfView = MathHelper.ToRadians(45f) /modelMetadata.FieldOfViewDivisor; // zoom
model.Rotation = modelMetadata.World * Matrix.CreateFromYawPitchRoll(yaw,pitch, 0);
model.Projection = Matrix.CreatePerspectiveFieldOfView(fieldOfView,modelMetadata.AspectRatio, modelMetadata.NearPlaneDistance,modelMetadata.FarPlaneDistance);
model.View = modelMetadata.ViewMatrix;
model.IsTextureEnabled = true;
model.IsPerPixelLightingEnabled = true;
}
14. 因为要在Update事件处理函数中进行的计算在不同调用中不会发生变化,每一帧看起来都是相同的,并且绘制模型会出现在3D空间相同的位置上。为了响应手势识别使XNA绘制能够缩放、旋转3D模型,首先需要在GamePage构造函数里激活手势识别的句柄:
C#
public GamePage()
{
InitializeComponent();
…
//Initialize gestures support -Pinch for Zoom and horizontal drag for rotate
TouchPanel.EnabledGestures= GestureType.FreeDrag | GestureType.Pinch | GestureType.PinchComplete;
}
声明声明下列的变量,在timer_Update事件处理函数中,我们会使用这些变量来旋转和缩放这些模型:
C#
public partial class GamePage : PhoneApplicationPage
{
…
floatxRotation = 0.0f;
floatyRotation = 0.0f;
float? prevLength;
floatcameraFOV = 45; // Initial camera FOV (servesas a zoom level)
…
}
15. 在OnNavigatedTo方法中根据初始化xRotation和yRotation变量:
C#
protected override void OnNavigatedTo(NavigationEventArgse)
{
…
modelMetadata = app.ModelsStore[data["ID"]];
xRotation = modelMetadata.DefaultXRotation;
yRotation = modelMetadata.DefaultYRotation;
…
}
16. 添加HandleInput方法读取用户输入到xRotation, yRotation, cameraFOV和prevLength变量中:
C#
private void HandleInput()
{
while(TouchPanel.IsGestureAvailable)
{
GestureSamplegestureSample = TouchPanel.ReadGesture();
switch(gestureSample.GestureType)
{
caseGestureType.FreeDrag:
xRotation += gestureSample.Delta.X;
yRotation -= gestureSample.Delta.Y;
break;
caseGestureType.Pinch:
float gestureValue = 0;
float minFOV = 80;
float maxFOV = 20;
float gestureLengthToZoomScale = 10;
Vector2 gestureDiff = gestureSample.Position - gestureSample.Position2;
gestureValue = gestureDiff.Length()/ gestureLengthToZoomScale;
if(null != prevLength) // Skipthe first pinch event
cameraFOV -= gestureValue- prevLength.Value;
cameraFOV = MathHelper.Clamp(cameraFOV, maxFOV, minFOV);
prevLength = gestureValue;
break;
case GestureType.PinchComplete:
prevLength = null;
break;
default:
break;
}
}
}
17. 编辑timer_Update方法来获取用户输入,并更新3D模型:
C#
private void timer_Update(object sender, GameTimerEventArgs e)
{
HandleInput();
float yaw = MathHelper.Pi + MathHelper.PiOver2+ xRotation / 100; // rotation around the y-axis
float pitch = yRotation / 100; // rotation around the x-axis
floatfieldOfView = MathHelper.ToRadians(cameraFOV)/ modelMetadata.FieldOfViewDivisor; // zoom
…
}
18. 在同一时间只有一个Readerer能够在屏幕上进行绘制。所以如果在你的应用程序中使用3D模型和其他XNA功能,那么XNA Renderer不会触发正规的Silverlight页面生命周期时间。Silverlight内容只是存在,但不会显示出来,因为XNA不会绘制它们。
XNA框架中使用一个新的对象来解决这个问题:UIElementRenderer。UIElementRenderer将Silverlight内容作为2D材质在XNA中显示,并且触发Silverlight事件。例如,当我们的用户触摸由UIElementRenderer生成的Silverlight按钮图片时,UIElementRenderer将会把输入信息传递给真实(不可见)的Silverlight按钮。
为了显示模型的名称和描述,使用UIElementRenderer来绘制Silverlight控件树。开始声明uiRenderer变量:
C#
public partial class GamePage : PhoneApplicationPage
{
…
UIElementRenderer uiRenderer;
…
}
19. 为了展现整个控件树,当控件树改变时,你需要更新uiRenderer变量。在GamePage的LayoutUpdated事件中添加处理函数:
C#
public GamePage()
{
…
this.LayoutUpdated += GamePage_LayoutUpdated;
}
private void GamePage_LayoutUpdated(object sender, EventArgs e)
{
if(uiRenderer == null || LayoutRoot.ActualWidth > 0 && LayoutRoot.ActualHeight> 0)
uiRenderer = newUIElementRenderer(LayoutRoot, (int)LayoutRoot.ActualWidth, (int)LayoutRoot.ActualHeight);
}
20. XNA renderer只能在应用程序触发Draw事件之后,更新绘制。调用uiRenderer的Render方法来更新Silverlight UI,并在材质中进行重华。使用spriteBatch将材质绘制到屏幕上。
C#
private void timer_Draw(object sender, GameTimerEventArgs e)
{
…
// Update the Silverlight UI
uiRenderer.Render();
// Draw the sprite
spriteBatch.Begin();
spriteBatch.Draw(uiRenderer.Texture, Vector2.Zero,Color.White);
spriteBatch.End();
}
21. 最后,设置页面的DataContext,并添加GamePanel XAML可以绑定的所依赖的属性:
C#
public GamePage()
{
…
this.DataContext = this;
}
public string ModelName
{
get { return (string)GetValue(ModelNameProperty);}
set { SetValue(ModelNameProperty, value); }
}
// Using aDependencyProperty as the backing store for ModelName. This enables animation, styling, binding,etc...
public static readonlyDependencyProperty ModelNameProperty =
DependencyProperty.Register("ModelName", typeof(string), typeof(GamePage), null);
public string ModelDesc
{
get { return(string)GetValue(ModelDescProperty); }
set { SetValue(ModelDescProperty, value); }
}
// Using aDependencyProperty as the backing store for ModelDesc. This enables animation, styling, binding,etc...
public static readonlyDependencyProperty ModelDescProperty =
DependencyProperty.Register("ModelDesc", typeof(string), typeof(GamePage), null);
22. 在OnNavigatedTo方法中,更新依赖属性值:
C#
protected override void OnNavigatedTo(NavigationEventArgse)
{
…
modelMetadata = app.ModelsStore[data["ID"]];
ModelName = modelMetadata.Name;
ModelDesc = modelMetadata.Description;
…
}
23. 我们完成了本次动手实验。
本次实验中,您学习了如何使用两个Windows Phone Mango的新功能。您浏览了BackgroundTransferService,解释了如何在您的应用程序总集成一个下载管理器,使其能够不依赖于您的应用程序而独立运行。你也看到了如何将XNA内容嵌入到一个Silverlight页面中,创建一个Silverlight/XNA混合应用程序,来演示如何在同一个页面中显示两个完全不同的。程序框架。