2008-11-16 22 views
2

我剛剛編寫了一個小型XBox 360無線控制器託管界面,基本上 包裝在低槓桿SlimDX包裝庫中,併爲XBOX 360控制器提供了一個易於管理的API。定時器,UI框架和不良耦合 - 任何想法?

在內部,班級每隔N ms輪詢一次遊戲板,並在探測到控制器底層狀態發生變化時拍攝事件。

我遇到一些什麼死衚衕與計時器被basiclly迫使兩害之間進行選擇:

  • 要麼讓我XBox360GamePad類UI框架的具體(即支持WPF/WinForms的會在類中硬編碼,並且類必須引用這些框架......)

  • 使類完全與框架無關,但強制用戶使用Dispatcher.Invoke/Invoke()調用將其代碼能夠根據生成的事件更新UI。

如果我選擇後者(使得代碼UI不可知的),然後我基本上使用的「通用」 System.Timers.Timer或沒有UI依賴任何計時器。 在這種情況下,我最終會產生/不能直接更新UI的線程生成/調用事件,即在WPF中,我必須通過Dispatcher的(醜陋)使用來發布源自360控制器類的每個更新。調用。另一方面,如果我在XBox 360控制器類中使用DispatcherTimer,我有一個可以直接更新UI的工作組件,但現在我的整個控制器類都與WPF耦合,並且它不能沒有(在純控制檯應用程序,IE)依賴於WPF使用

什麼我是一種看起來是某種解決辦法,讓我既框架無關,而不必訴諸於所有同時更新UI各種Dispatcher.Invoke()技術... 如果例如有一個共享基類的所有計時器,我可以以某種方式注入計時器作爲依賴關係根據相關的情況。 有沒有人曾經成功地處理過這種問題?

回答

0

因此... 看來信息/代碼@http://geekswithblogs.net/robp/archive/2008/03/28/why-doesnt-dispatcher-implement-isynchronizeinvoke.aspx確實提供了一個工作解決方案。 代碼完全獨立於任何UI,而僅依賴於ISynchronizeInvoke進行正確的UI /線程集成。

剛剛使用過這個,我仍然不太願意保持原樣。

我的問題的要點是,每一個事件調用函數看起來是這樣的:

protected virtual void OnLeftThumbStickMove(ThumbStickEventArgs e) 
{ 
    if (LeftThumbStickMove == null) return; 

    if (_syncObj == null || !_syncObj.InvokeRequired) 
    LeftThumbStickMove(this, e); 
    else 
    _syncObj.BeginInvoke(LeftThumbStickMove, new object[] { this, e }); 
} 

這是非常惱人的和混亂編寫代碼這種方式,它看起來對我來說,像這樣太過計較周圍只是讓該死的東西去工作。基本上我不喜歡用這麼多的邏輯來包裝每個事件調用(!)

因此,我選擇了一個不同的/額外的策略: 基本上,XBox360GamePad類的構造函數現在看起來像這樣:

public XBox360GamePad(UserIndex controllerIndex, Func<int, Action, object> timerSetupAction) 
{ 
    CurrentController = new Controller(controllerIndex); 
    _timerState = timerSetupAction(10, UpdateState); 
} 

正如您所看到的,它接受一個負責創建計時器並掛鉤它的Func。

這意味着,在默認情況下,裏面的360個控制器類,我不需要使用任何 UI具體定時器......從本質上說,我的「默認」構造函數控制器看起來是這樣的:

public XBox360GamePad(UserIndex controllerIndex) : 
    this(controllerIndex, (i,f) => new Timer(delegate { f(); }, null, i, i)) {} 

我使用lambda函數來編寫控制器類在定時器「service」上的依賴關係。

從WPF,我用的是更普遍的構造,以確保控制將使用DispatcherTimer這樣的:

_gamePad = new XBox360GamePad(UserIndex.One, (i, f) => { 
    var t = new DispatcherTimer(DispatcherPriority.Render) {Interval = new TimeSpan(0, 0, 0, 0, i) }; 
    t.Tick += delegate { f(); }; 
    t.Start(); 
    return t; 
    }); 

這樣一來,我基本上把它上升到「用戶」提供計時器實現控件,其中默認實現不必使用任何WPF或WinForms特定的代碼。

我個人發現這比使用ISynchronizeInvoke更有用/更好的設計。

我很想聽到,像這樣的人反饋/要改善這種/被厭惡這等

1

輪詢體系結構是唯一的選擇嗎?

在任何情況下,我都會重組系統,以便外部世界可以訂閱從控制器類發起的事件。

如果您希望控制器在正確的線程上下文中觸發事件,那麼我會爲ISynchronizeInvoke接口添加一個屬性,並且如果此屬性在控制器內部爲非null,請使用該接口,否則只需調用事件直接在控制器運行的線程上。

例如,這可以讓您將整個輪詢事件變成一個線程,每N毫秒喚醒一次就可以完成任務,甚至可以使用事件對象來等待事件(如果360控制器體系結構具有類似情況) 。

0

360控制器只能報告它的當前狀態。 沒有其他方法可以在沒有投票的情況下獲得狀態。 使用System.Threading.Timer或打開一個Thread.Sleep()的新線程在我的視圖中是相同的,它們都實現了無UI定時器類的功能。

感謝提ISynchronizeInvoke,我GOOGLE多一些,發現有人共享我的痛苦,甚至可能有一個解決辦法: http://geekswithblogs.net/robp/archive/2008/03/28/why-doesnt-dispatcher-implement-isynchronizeinvoke.aspx

我會嘗試他的代碼,並保持這個線程張貼...謝謝提示!

0

你也可以看看併發與協調運行時,這是微軟機器人的一部分Developers Studio(但不久將成爲獨立產品)。

CCR是線程協調器,它允許MRDS完全支持駕駛機器人周圍的操縱桿等操作,並在消息傳遞模型上運行。您可以設置定時器,從XBox遊戲杆請求數據,然後將數據發送到端口(隊列)。然後,您將數據發佈到操縱桿端口以處理操作時,設置被調用(接收方)的方法。提供適用於WinForms的適配器,並且爲WPF編寫適配器並不困難。

下面是一篇非常棒的文章,演示瞭如何在沒有成熟的MRDS的情況下使用CCR。 http://msdn.microsoft.com/en-us/magazine/cc163556.aspx

根據我的經驗,一旦您掌握了CCR的基礎知識,那麼執行多線程併發異步編程就變得很容易了。

0

@MattValerio:我知道CCR,但這並不符合這裏的法案,因爲我打算把控制權放在googlecode上,而CCR不是框架或開源的一部分。