2013年12月27日 星期五

[W8App][C#] Task Cancellation

若想要讓產生的Task具有可取消的機制,則必需透過以下static methods取得task handle與傳入cancel token來達成
Task.Run(Action, CancellationToken)
Task.Run(Func<Task>, CancellationToken)
Task.Run<TResult>(Func<Task<TResult>>, CancellationToken)
Task.Run<TResult>(Func<TResult>, CancellationToken)

這個範例是我設計的一個對話盒控制項,當對話盒產生時呼叫InitItemsAsync以非同步的方式產生圖片對載入UI的list控制項中,使用者可以不必等待所有的圖片都產生即按下OK(或Cancel)按來關閉這個對話盒。因此我必需讓InitItemsAsync具有可被取消的機制。

   1: public sealed partial class MyDialog : UserControl
   2: {
   3:     private CancellationTokenSource _cts;
   4:     private CancellationToken _token; 
   5:     private Task _opInit;

#3-5 我們的物件必需儲存CancellationTokenSource、CancellationToken與Task方便在動作的對應函式中取消



   1: private async Task InitItemsAsync()
   2: {
   3:     for(int i=0 ; i<24 ; i++)
   4:     {
   5:         if (_token.IsCancellationRequested) break;
   6:  
   7:         await LoadImageAsync(i);
   8:     }
   9: }

#5 非同步的InitItemsAsync函式,當_token收到取消的要求時(IsCancellationRequested)提前結束迴圈



   1: _cts = new CancellationTokenSource();
   2: _token = _cts.Token;
   3: _opInit = Task.Run(async () =>
   4: {
   5:     await InitItemsAsync();
   6: },
   7: _token);

透過Task.Run來執行非同步的函式,並載入cancel token。
#5 InitItemAsync是非同步的函式,因此必需有async / await來載明


PS: 我嘗試過不用Lambda的寫法,而是直接以函式指標的方式來使用Task.Run,但在VS2013編譯時期會有函式型別模糊(error CS0121: The call is ambiguous between the following methods or properties)的錯誤,目前還不知道解決的方式。



   1: private bool TryCancelInitItems()
   2: {
   3:     if (_opInit.IsCompleted == false)
   4:     {
   5:         try
   6:         {
   7:             _cts.Cancel();
   8:  
   9:             _opInit.Wait(_token);
  10:  
  11:             return true;
  12:         }
  13:         catch(System.OperationCanceledException)
  14:         {
  15:             return true;
  16:         }
  17:         catch (Exception e)
  18:         {
  19:             Debug.WriteLine(@"{0}", e);
  20:  
  21:             return false;
  22:         }
  23:     }
  24:  
  25:     return true;
  26: }
當使用者按下結束對話盒的按鈕時,呼叫我的TryCancelInitItems函式來取消InitItemsAsync。

#3 先檢查task是否已經完成,若尚未完成才需使用取消機制
#7 呼叫取消並等待_token
#13 若InitItemsAsync被取消了,會丟出System.OperationCanceledException

2013年10月25日 星期五

[W8App] 8.0 “Snapped” to 8.1– with LayoutAwarePage

Windows 8.1的一個德政是取消了原本的Snappped View限制,App除了FullScreen外還可以有任意的大小(只是有最小限制)。不過對於已經開發好的8.0 App,就要花時間重新調整不同視窗大小時Layout。

我的懶人改法是,除了FullScreen外一律視為Snapped。依這樣的邏輯其實只要改動一個function – LayoutAwarePage的DetermineVisualState:

   1: protected virtual string DetermineVisualState(ApplicationView appView)
   2: {
   3:     if(appView.IsFullScreen)
   4:     {
   5:         return @"FullScreen" + appView.Orientation.ToString();
   6:     }
   7:     else
   8:     {
   9:         return @"Snapped";
  10:     }
  11: }

1. 原本參數為ApplicationViewState (編譯時有warning),改為ApplicationView
3-10. 原本view state可以直接吐回字串的部份用邏輯修正


當然呼叫端也要調整為



   1: string visualState = DetermineVisualState(ApplicationView.GetForCurrentView());
1.  原本呼叫端是傳入ApplicationView.Value (編譯時有warning),改為ApplicationView.GetForCurrentView()

2013年10月22日 星期二

[W8App] In W8.1, DirectWrite must create font with IDWriteFactory2

升上8.1第一天後, 我的App發生DirectWrite繪製文字有時會繪製不出來的問題。最後才發現改IDWriteFactory改為IDWriteFactory2後就一切正常,不過IDWriteFactory2只支援8.1以上,所以結論是App必做二個版本了。。。

2013年10月14日 星期一

[W8App] D2DERR_WRONG_FACTORY (0x88990012)

最近在學習W8App的Direct2D與DirectWrite時發生了一個例外的錯誤,當呼叫D2D的EndDraw來更新繪圖結果是產生了0x88990012 (D2DERR_WRONG_FACTORY)的例外錯誤。

這個錯誤發生的原因是我的DirectWrite Render物件預先儲存了D2D factory,而透過SurfaceImageSource來連結W8App與Direct2D時,D2DContext所使用的D2D factory似乎有自已獨立的產生方式。

目前的解決方式是透過GetFactory直接從D2DContext物件中取得D2DFactory,至於是否有更好的作法,就需要再研究。

   1: IFACEMETHODIMP CustomTextRenderer::DrawGlyphRun(
   2:     _In_opt_ void* clientDrawingContext,
   3:     FLOAT baselineOriginX,
   4:     FLOAT baselineOriginY,
   5:     DWRITE_MEASURING_MODE measuringMode,
   6:     _In_ DWRITE_GLYPH_RUN const* glyphRun,
   7:     _In_ DWRITE_GLYPH_RUN_DESCRIPTION const* glyphRunDescription,
   8:     IUnknown* clientDrawingEffect
   9:     )
  10: {
  11:     HRESULT hr = S_OK;
  12:  
  13:     Microsoft::WRL::ComPtr<ID2D1Factory> d2dFactory;
  14:     m_d2dContext->GetFactory(&d2dFactory);
  15:  
  16:     // Create the path geometry.
  17:     Microsoft::WRL::ComPtr<ID2D1PathGeometry> pathGeometry;
  18:     hr = d2dFactory->CreatePathGeometry(&pathGeometry);
  19:  
  20:     // Write to the path geometry using the geometry sink.
  21:  
  22:     Microsoft::WRL::ComPtr<ID2D1GeometrySink> sink;
  23:     if (SUCCEEDED(hr))
  24:     {
  25:         hr = pathGeometry->Open(&sink);
  26:     }
  27:  
  28:     // Get the glyph run outline geometries back from DirectWrite and place them within the
  29:     // geometry sink.
  30:     if (SUCCEEDED(hr))
  31:     {
  32:         hr = glyphRun->fontFace->GetGlyphRunOutline(
  33:             glyphRun->fontEmSize,
  34:             glyphRun->glyphIndices,
  35:             glyphRun->glyphAdvances,
  36:             glyphRun->glyphOffsets,
  37:             glyphRun->glyphCount,
  38:             glyphRun->isSideways,
  39:             glyphRun->bidiLevel %2,
  40:             sink.Get()
  41:             );
  42:     }
  43:  
  44:     // Close the geometry sink
  45:     if (SUCCEEDED(hr))
  46:     {
  47:         hr = sink.Get()->Close();
  48:     }
  49:  
  50:     // Initialize a matrix to translate the origin of the glyph run.
  51:     D2D1::Matrix3x2F const matrix = D2D1::Matrix3x2F(
  52:         1.0f, 0.0f,
  53:         0.0f, 1.0f,
  54:         baselineOriginX, baselineOriginY
  55:         );
  56:  
  57:     // Create the transformed geometry
  58:     Microsoft::WRL::ComPtr<ID2D1TransformedGeometry> transformedGeometry;
  59:     if (SUCCEEDED(hr))
  60:     {
  61:         hr = d2dFactory->CreateTransformedGeometry(
  62:             pathGeometry.Get(),
  63:             &matrix,
  64:             &transformedGeometry
  65:             );
  66:     }
  67:  
  68:     // Draw the outline of the glyph run
  69:     if(SUCCEEDED(hr))
  70:     {
  71:         m_d2dContext->DrawGeometry(transformedGeometry.Get(),m_brushOutline.Get());
  72:         m_d2dContext->FillGeometry(transformedGeometry.Get(),m_brushFill.Get());
  73:     }
  74:  
  75:     return hr;
  76: }

13-14 透過目前的D2DContext來取得D2DFactory

2013年10月11日 星期五

[W8App] Hide / Show cursor

隱藏滑鼠指標

   1: if (Window.Current.CoreWindow.PointerCursor != null) Window.Current.CoreWindow.PointerCursor = null;

顯示滑鼠指標



   1: if (Window.Current.CoreWindow.PointerCursor == null)
   2: {
   3:     Window.Current.CoreWindow.PointerCursor = new CoreCursor(CoreCursorType.Arrow, 1);
   4: }


CoreCursor的id可以自已隨意的指派並不一定是1。


Reference:



  1. CoreCursorType enumeration

2013年8月27日 星期二

如何取消訂閱行事曆

有網友問已訂閱的行事曆如何取消,所以就多了這一篇。和訂閱行事曆一樣必需透過瀏覽器進入SkyDrive的行事曆設定。進入SkyDrive的行事曆後,點選右邊的小齒輪,進入選項頁
選擇想要取消訂閱的行事曆
 刪除

相關文章
整合Windows 8與google行事曆

2013年8月22日 星期四

[W8App] XAML/C# Hex string

XAML的Hex string表示是以&#x開頭,如:

   1: <Button Content="&#xE114;" FontFamily="Segoe UI Symbol"/>

但C#的Hex string是以\x開頭,如:



   1: btn.Content = "\xE114";

2013年8月19日 星期一

[W8App] Custom Control – Access template component

Step 1. XAML,給予要存取元件名稱

   1: <Style TargetType="local:WaitReadyButton">
   2:     <Setter Property="Template">
   3:         <Setter.Value>
   4:             <ControlTemplate TargetType="local:WaitReadyButton">
   5:                 <Grid>
   6:                     <TextBlock x:Name="txtSymbol" Text=""/>
   7:                 </Grid>
   8:             </ControlTemplate>
   9:         </Setter.Value>
  10:     </Setter>
  11: </Style>

Step 2. C#,宣告存取元件



   1: public sealed class WaitReadyButton : Control
   2: {
   3:     private TextBlock _txtSymbol;

Step 3. C#,在OnApplyTemplate()中取得元件



   1: protected override void OnApplyTemplate()
   2: {
   3:     base.OnApplyTemplate();
   4:  
   5:     _txtSymbol = GetTemplateChild(@"txtSymbol") as TextBlock;
   6: }

成功取得元件後即可在程式碼中使用該元件。


Reference


1. Creating a WPF Custom Control