作者:马宁

    相信未来一段的业余时间,我都要和XNA为伍了。本来想向3D开发的纵深发展,但是遇到了一个实际的问题,就是如何在XNA下显示MessageBox和Software Input Panel。干脆先写出来吧,省得大家遇到这问题时抓狂。

    按照为数不多的公开文档描述,XNA和Silverlight for Windows Phone应该是基于同一个.NET Compact Framework的CLR。但是,XNA并没有提供任何用户控件、MessageBox和软键盘等,也不能直接调用Silverlight for Windows Phone的类库。这样势必为XNA制造了很多人为的障碍。但调用MessageBox和Software Input Panel的后门,XNA还是给我们留下了,这就是Microsoft.Xna.Framework.GamerServices命名空间下的Guide类,类描述如下:

http://msdn.microsoft.com/en-us/library/microsoft.xna.framework.gamerservices.guide.aspx

    该类不但可以调用MessageBox和软键盘,还能够调用Marketplace、XBox Live等窗体。不过,值得注意的是,Guide类提供的方法都是异步调用,而非同步调用,这也好理解,游戏的处理过程是以时间驱动的,所以任何操作不应该阻塞住游戏主线程。

    Guide类调用MessageBox和软键盘的描述在这里:

http://msdn.microsoft.com/en-us/library/ff827869.aspx

http://msdn.microsoft.com/en-us/library/ff827868.aspx

    但MSDN文档还是有一些瑕疵,按上面提供的方法会产生Exception,所以我在下面给出修改后可以运行的方法。运行环境基于VS 2010 + Windows Phone 7 SDK RTW版。

调用MessageBox

    创建Windows Phone 7中XNA 4.0的工程,然后,我们在Update方法里添加对于MessageBox的调用。当然,大家请不要认为把MessageBox加到Update里正确的,这样会造成MessageBox不断弹出。我只是为了简化代码,才这么做的。


  1. protected override void Update(GameTime gameTime)  
  2.         {  
  3.             // Allows the game to exit  
  4.             if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)  
  5.                 this.Exit();  
  6.             // TODO: Add your update logic here  
  7.             List<string> MBOPTIONS = new List<string>();   
  8.             MBOPTIONS.Add("OK");  
  9.             MBOPTIONS.Add("CANCEL");  
  10.             if (!Guide.IsVisible)  
  11.                 Guide.BeginShowMessageBox("test", "hello, XNA", MBOPTIONS, 0, MessageBoxIcon.Alert, new AsyncCallback(RespCallback), null);  
  12.             base.Update(gameTime);  
  13.         } 

    由于Microsoft.Xna.Framework.GamerServices是默认添加的组件,所以,我们可以直接使用Guide类。首先创建一个string类型的List,用于保存MessageBox中按钮的Text;然后通过List<string>的Add方法将需要显示的Button Text添加进去。

    接下来是Guide.IsVisible方法,由于MessageBox、SIP软键盘等公用一个绘制表面,必须保证没有其他UI显示时,才能够显示指定组件,如果不添加这句Guide.IsVisible的判断,将触发下面的异常。

马宁的Windows Phone 7开发教程(3)——XNA下使用MessageBox和软键盘

    接下来就是调用的主体Guide.BeginShowMessageBox了,这是一个异步方法,调用后立刻返回。参数比较好理解,下面是函数的声明:


  1. public static IAsyncResult BeginShowMessageBox (  
  2.          string title,  
  3.          string text,  
  4.          IEnumerable<string> buttons,  
  5.          int focusButton,  
  6.          MessageBoxIcon icon,  
  7.          AsyncCallback callback,  
  8.          Object state  

    第一个参数是标题,第二个参数是对话框内容,第三个是button上文字的列表,也表示有几个Button出现,第四个是焦点在第几个Button上,第五个是图标,我们设置为null,第六个是结束时调用的Callback函数对象,最后一个是用户自定义状态对象,可以传递自定义信息。其他参数都容易理解,AsyncCallback对象需要一个Callback函数RespCallback,我们实现如下:


  1. private static void RespCallback(IAsyncResult asynchronousResult)  
  2.         {  
  3.             int? b = Guide.EndShowMessageBox(asynchronousResult);  
  4.             if (b > 0)  
  5.                 Debug.WriteLine("Cancel");  
  6.             else 
  7.                 Debug.WriteLine("OK");  
  8.         } 

    Callback函数中最重要的工作是调用Guide.EndShowMessageBox函数,来关闭MessageBox。EndShowMessageBox需要传入一个IAsyncResult对象,来自Callback函数的参数。返回值是一个可为空的int,如果为空则表示没有返回值,如果不为空,返回值是Button的Index值,返回0表示点击了第一个按钮OK,返回1则表示点击了第二个按钮Cancel,以此类推。

    显示MessageBox的效果如下:

马宁的Windows Phone 7开发教程(3)——XNA下使用MessageBox和软键盘

调用Software Input Panel

    接下来是调用SIP软键盘的代码,仍旧放到Update方法里,Callback函数也一并给出。


  1. protected override void Update(GameTime gameTime)  
  2.         {  
  3.             // Allows the game to exit  
  4.             if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)  
  5.                 this.Exit();  
  6.             if (!Guide.IsVisible)  
  7.                 Guide.BeginShowKeyboardInput(PlayerIndex.One,  
  8.                         "Here's your Keyboard", "Type something...",  
  9.                         "abc",  
  10.                         new AsyncCallback(GetTypedChars),  
  11.                         null);  
  12.             base.Update(gameTime);  
  13.         }  
  14.         private static void GetTypedChars(IAsyncResult asynchronousResult)  
  15.         {  
  16.             string output = Guide.EndShowKeyboardInput(asynchronousResult);  
  17.             Debug.WriteLine(output);  
  18.         } 

    前边都解释过了,直接来看Guide.BeginShowKeyboardInput,第一个参数要传PlayerIndex进去,这个是针对Xbox的,在Windows和Windows Phone 7上只支持一个用户,所以直接传PlayerIndex.One就好了。接下来的三个参数是标题、描述和默认字符,然后是异步调用方法和自定义状态。还有最后一个可选参数,表示是否用Password方式显示字符。


  1. public static IAsyncResult BeginShowKeyboardInput (  
  2.          PlayerIndex player,  
  3.          string title,  
  4.          string description,  
  5.          string defaultText,  
  6.          AsyncCallback callback,  
  7.          Object state,  
  8.          bool usePasswordMode  

    在异步调用方法中,Guide.EndShowKeyboardInput会返回一个字符串,该字符串为用户输入的字符串。为什么显示的是字符串呢,这和SIP的显示方式有关。在调用SIP函数后,会首先弹出第一个对话框,询问用户是否输入字符,如果用户点Cancel则关闭SIP,如果点OK则进入第二个界面,用户才能够使用SIP软键盘进行输入。

    下面就是SIP显示的状态:

马宁的Windows Phone 7开发教程(3)——XNA下使用MessageBox和软键盘

马宁的Windows Phone 7开发教程(3)——XNA下使用MessageBox和软键盘

写到最后

    今天的主角Guide类,还有很多有趣的函数调用,有兴趣的朋友按照这个方法调用就可以了。这次的代码量不多,所以就不给出单独Sample Code的下载了。再有就是,虚心接受批评,将文章里代码的格式弄好了。

    我最近还是很勤快的,Windows Phone 7的开发都写了三篇了,这是之前文章的链接:

马宁的Windows Phone 7开发教程(1)——Windows Phone开发工具初体验

马宁的Windows Phone 7开发教程(2)——Windows Phone XNA 4.0 3D游戏开发