2014-03-12 34 views
0

我在C#中有一个虚拟机,它有两个构造函数重载,五个事件处理函数和两个公共属性。我已经将这个虚拟机集成到了WinForms和Silverlight应用程序中,甚至可以在客户端/服务器设置中将其用作Web应用程序。在WinRT中集成虚拟机

界面如下:

构造

public Engine(Stream gameFile) { .. } 
public Engine(Stream gameFile, Stream saveFile) { .. } 

活动

public OutputReady(object sender, OutputReadyEventArgs e); 
public LineWanted(object sender, LineWantedEventArgs e); 
public KeyWanted(object sender, KeyWantedEventArgs e); 
public SaveRequested(object sender, SaveRestoreEventArgs e); 
public LoadRequested(object sender, SaveRestoreEventArgs e); 

属性

public Dictionary<string, string> ChanneData { get; set; } 
public byte[] SaveData { get; set; } 

当WinForms和Silverlight中实现这一点,我不得不使用Invoke或类似的方法写入UI线程。

在WinRT中,UI和后台线程之间的切换隐藏在异步/等待和任务处理的后面,并且我还没有真正能够以我想要的方式移植我的虚拟机....作为服务。

我可以在我的应用程序的MainForm中嵌入虚拟机,就像我在Silverlight中完成的那样,但是我想要更多的MVC/Service实现。我想将虚拟机移除到一个具有简单界面的服务,用于启动游戏,发送新命令,保存游戏和加载游戏。 MainForm不应该知道有关vm实现的任何信息。

我已经解决了这个问题超过了几个小时,但仍然没有足够清楚的理解async/await和Tasks来完成它,以便它能正常工作。我可以让引擎启动并发送命令,但保存和加载失败,我认为这是因为异步的东西无法正常工作。

的WinForms简单的实现:

using System; 
using System.Collections.Generic; 
using System.ComponentModel; 
using System.Data; 
using System.Drawing; 
using System.Linq; 
using System.Text; 
using System.Threading.Tasks; 
using System.Windows.Forms; 
using System.IO; 
using System.Reflection; 
using System.Threading; 

using FyreVM; 

namespace FyreVMTest 
{ 
    public partial class MainForm : Form 
    { 
     private Thread vmThread = null; 
     private Engine vm = null; 
     private AutoResetEvent inputReadyEvent = new AutoResetEvent(false); 
     private string command = ""; 

     public MainForm() 
     { 
      InitializeComponent(); 
     } 

     private void MainForm_Load(object sender, EventArgs e) 
     { 
      Assembly assembly = Assembly.GetExecutingAssembly(); 
      Stream stream = 
       assembly.GetManifestResourceStream("FyreVMTest.Game.shadow-w8.ulx"); 
      vm = new Engine(stream); 

      vm.KeyWanted += vm_KeyWanted; 
      vm.LineWanted += vm_LineWanted; 
      vm.LoadRequested += vm_LoadRequested; 
      vm.OutputReady += vm_OutputReady; 
      vm.SaveRequested += vm_SaveRequested; 

      vmThread = new Thread(VMThreadProc); 
      vmThread.IsBackground = true; 
      vmThread.Start(); 
     } 

     private void VMThreadProc(object dummy) 
     { 
      try 
      { 
       vm.Run(); 
       this.Invoke(new Action(GameFinished)); 
      } 
      catch (Exception ex) 
      { 
       MessageBox.Show(ex.ToString()); 
      } 
     } 

     private void GameFinished() 
     { 
      this.Close(); 
     } 

     private void RequestLine() 
     { 
      CommandLine.Focus(); 
     } 

     private void vm_LineWanted(object sender, LineWantedEventArgs e) 
     { 
      this.Invoke(new Action(RequestLine)); 
      inputReadyEvent.WaitOne(); 
      e.Line = command; 
     } 

     private void vm_KeyWanted(object sender, KeyWantedEventArgs e) 
     { 
      //this.Invoke(new Action(RequestKey)); 
      //inputReadyEvent.WaitOne(); 
      //e.Char = entry[0]; 
     } 

     private void vm_OutputReady(object sender, OutputReadyEventArgs e) 
     { 
      this.Invoke(new Action<Dictionary<string,string>>(HandleOutput), e.Package); 
     } 

     private void vm_SaveRequested(object sender, SaveRestoreEventArgs e) 
     { 
      e.Stream = (Stream)this.Invoke(new Func<Stream>(RequestSaveStream)); 
     } 

     private void vm_LoadRequested(object sender, SaveRestoreEventArgs e) 
     { 
      e.Stream = (Stream)this.Invoke(new Func<Stream>(RequestLoadStream)); 
     } 

     private Stream RequestSaveStream() 
     { 
      using (SaveFileDialog dlg = new SaveFileDialog()) 
      { 
       dlg.Title = "Select Save File"; 
       dlg.Filter = "Textfyre save files (*.tfq)|*.tfq"; 

       if (dlg.ShowDialog() == DialogResult.Cancel) 
        return null; 
       else 
       { 
        return new FileStream(dlg.FileName, 
            FileMode.Create,FileAccess.Write); 
       } 
      } 
     } 

     private Stream RequestLoadStream() 
     { 
      using (OpenFileDialog dlg = new OpenFileDialog()) 
      { 
       dlg.Title = "Load a Saved Game"; 
       dlg.Filter = "Textfyre save files (*.tfq)|*.tfq"; 

       if (dlg.ShowDialog() == DialogResult.Cancel) 
        return null; 
       else 
       { 
        return new FileStream(dlg.FileName, FileMode.Open, FileAccess.Read); 
       } 
      } 
     } 

     private void HandleOutput(Dictionary<string, string> package) 
     { 
      MainOutput.Clear(); 
      foreach (string key in package.Keys) 
      { 
       MainOutput.AppendText(key + ": " + package[key]); 
      } 

     } 

     private void GoButton_Click(object sender, EventArgs e) 
     { 
      command = CommandLine.Text; 
      CommandLine.Text = ""; 
      inputReadyEvent.Set(); 
     } 

    } 
} 

所以我正在寻找指导如何实现这个在WinRT中。接口是一个问题,还是我需要将其更改为WinRT更友好?如果没关系,在一个类中实现它的最好方法是什么,然后如何从MainForm调用该类?

任何指导非常感谢。

+0

如果有帮助,一个示例非工作项目在这里:https://github.com/ChicagoDave/FyreXaml/ –

回答

0

在WinRT中调用UI线程与使用Dispatcher的Silverlight类似。在这种情况下,它是一个CoreDispatcher

有访问它的一些方法,但最好的可能:

Windows.ApplicationModel.Core.CoreApplication.MainView.CoreWindow.Dispatcher 

注意:您只能在启动应用程序的访问后,这样的说法。如果您执行任何VM初始化预启动,您可能会收到空引用。

我建议做的是创建一个具有虚拟Invoke方法的自定义Dispatcher类。创建一个接受CoreDispatcher属性的子类。在Invoke方法中,如果CoreDispatchernull,只是InvokeAction(或Function),否则Invoke它在CoreDispatcher

然后,您可以在PCL或其他组件库中创建便携式Dispatcher访问器,并让您的应用程序向其中注入新的Dispatcher对象(最佳控制反转)。

为了让你开始:

// In your PCL 
public abstract class PortableDispatcher 
{ 
    public abstract void Invoke(Action action); 
} 

// In your app 
public class WinRTDispatcher : PortableDispatcher 
{ 
    public override void Invoke(Action action) 
    { 
     Windows.ApplicationModel.Core.CoreApplication.MainView.CoreWindow.Dispatcher. 
      RunAsync(CoreDispatcherPriority.Normal, new DispatchedHandler(action); 
    } 
} 

然后在你的PCL,你有一个static ViewModelLocator。给它一个PortableDispatcher类型的财产。启动应用程序时,请将定位器中的属性设置为新的WinRTDispatcher。任何想从PCL发送到UI线程的人都可以通过ViewModelLocatorPortableDispatcher。由于注入的对象,他们可以访问WinRT CoreDispatcher,而无需知道任何事情!

+0

是的,我还在努力与依赖注入。我主要得到它,但是我必须在几天内摸索你的建议。但是,谢谢...我_思考_我知道你的意思。 –

+0

新增了一个例子来帮助您入门。 –

+0

看看github项目,告诉我我是否在正确的轨道上。我将我的虚拟机代码与PortableDispatcher类一起移动到PCL。 MainForm在服务上设置WinRTDispatcher并加载服务。 –