2012年4月29日 星期日

Dispatcher.BeginInvoke 取代方案

在舊一點的 Windows 框架上例如 Win32 API、MFC 上面…
如果在背景執行序中想做 UI 的改變…常常會用 PostMessage 等方式…
在 WPF、Silverlight 上是用 Dispatcher.BeginInvoke 等方式

如果在撰寫 Windows 8 Metro Style 的 App 時有這類的需求…
可能會發現以前常用的下列兩種主要 Dispatcher 不見了…

App.Current.RootVisual.Dispatcher
Deployment.Current.Dispatcher

每種專案的流程不太一樣…每個人寫程式的習慣也不太一樣…
我個人在某些專案中是極依賴 Dispatcher 幫忙調度 UI Thread 做一些事…
例如利用 HttpWebRequest 從遠端將資料取回來時…
若中途發生什麼 Exception 而要顯示 MessageBox 時就會用到…
找了一些 MSDN forums 上面的說法…微軟似乎希望在 Win RT 上面…
開發者能盡量使用新的 await、async 結構來撰寫程式…
使用 async 的寫法的確可以保證還在 UI Thread 裡面…
但這樣我必須將以前寫好的一些 model 全部重寫…
所以找到取代之前 Dispatcher 的類別…對目前的我來說 CP 值會高上十倍…

有找到一些比較特殊的作法…例如利用 DispatcherTimer 定期去更新 UI
但這樣有點奇怪…直覺上對系統資源也會造成負擔…

我採用了另一種方法…在 UIElement 中其實有一個 Dispatcher Property
以我的撰寫習慣一定會將 Page 的 DataContext 指定為一個相似名稱的 Class
該 Class 繼承自 INotifyPropertyChanged 以達到 Data Binding 的目的…
所以在 Win RT 以往的 Page.DataContext = ViewModel; 後必須再加上一行…

private PageModel _viewModel = new PageModel();
public PageModel ViewMode
{
    get
    {
        return _viewModel;
    }
}

public void MyPage()
{
    DataContext = ViewModel;
    ViewModel.UIDispatcher = this.Dispatcher;
}

而原本的 ViewModel 則必須加入一個屬性如下…

private CoreDispatcher _uiDispatcher;
public CoreDispatcher UIDispatcher
{
    get
    {
        return _uiDispatcher;
    }
    set
    {
        _uiDispatcher = value;
    }
}

接下來在此 ViewModel 中如果要執行會造成 UI 異動的指令時…
就由以前的 Dispatcher.BeginInvoke(()=>{foo();}); 改為

public void OnGetResponseCompleted(String strResult)
{
    if(String.IsNullOrEmpty(strResult))
    {
        // 提示訊息網路發生錯誤
        UIDispatcher.InvokAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, (obj1, obj2) => { foo(); }, this, null);
    }
    else
    {
        // 後續處理
    }
}


2012/05/15 後記…在 Release Preview 版中…
Invok 改成 RunAsync 嘍…且 callback function 是無參數的…


沒有留言:

張貼留言