2014-03-12 32 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並加載服務。 –