顯示具有 C# 標籤的文章。 顯示所有文章
顯示具有 C# 標籤的文章。 顯示所有文章

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月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月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

2013年6月1日 星期六

[W8App] ListBox Binding data (5) – notify count changed

要如何得知ListBox繫結的資料數量有變化呢?由於資料來源是透過繫結,所以要得知資料量有有更動時必需加入聆聽ListBox.Items.VectorChanged事件。

   1: myListBox.Items.VectorChanged += VectorChangedEventHandler;


當數量改變時,會呼叫指定的VectorChangedEventHander函式:



   1: public void VectorChangedEventHandler(IObservableVector<object> sender, IVectorChangedEventArgs e)
   2: {
   3:     myListBox.Visibility = (sender.Count > 0) ? Visibility.Visible : Visibility.Collapsed;
   4:  
   5:     String strValue = String.Format(@"({0})", sender.Count);
   6:  
   7:     Debug.WriteLine(strValue);
   8: }

2013年5月29日 星期三

[W8App] ListBox Binding data (4) – Update data asynchronous

W8App的非同步(Async)與傳統桌面系統的多緒(multi-thread)在操作上有一點點的不太一樣,傳統的多緒系統CreateThread通常都會對應一個WaitForSingleObject,等待該工作的完成。但非同步的機制確不需要再額外coding一個等待機制隨時的檢查資料是否有更新(或是檢查資料是否全部更新完)

Async

以ListBox為例,我們可以把與ListBox繫結的資料,以非同步的方式一筆筆的加到ObservableCollection集合中。這對於大量或是遠端的資料的使用上可避免UI長時間的占用請形有很大的幫助。

不過當ListBox繫結非同步資料時有一點必需注意,新增與移除資料必需是在UI元件同樣的執行緒上執行,

   1: await Window.Current.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
   2: {
   3:     // read items collection from file or network ...
   4:     foreach (var item in items)
   5:     {
   6:         SampleDataGroup group = SampleDataSource.GetGroup("AllGroups");
   7:         group.Items.Add(new SampleDataItem("","","","","","",group));
   8:     }
   9: });

Window.Current會取得目前的window,但是要注意的是若新增(移除)的非同步機制是在另一個頁面或是Runtime dll中時,可能會取到不正確的window,這時就必需先記錄ListBox所在的windows資料。


參考資料:

1. Async Made Simple with C++ PPL, Rahul V. Patil, Microsoft Corporation

2013年5月27日 星期一

[筆記] XmlElement can not call SetAttribute in foreach?

C#對我而言只是會用而已,有些語法還不是很了解,今天遇到一個問題不清楚是C#的限制還是Bug?

我使用XmlDocument讀取xml文件,並檢查文件中的Bookmark元素們的Bookmarkname屬性,當屬性不存在或為空字元(“”)時視為舊版本,從Filename屬性讀出資料更新Bookmarkname屬性。

原本是使用C#的foreach來檢查每個Bookmark元素

   1: var elements = _xmlProject.GetElementsByTagName("Bookmark");
   2: foreach(var element in elements)
   3: {
   4:     String bookmarkname = element.GetAttribute(@"Bookmarkname");
   5:     if (bookmarkname == null || bookmarkname == "")
   6:     {
   7:         bookmarkname = element.GetAttribute(@"Filename");
   8:         element.SetAttribute(@"Bookmarkname", bookmarkname);
   9:     }
  10: }

不過當發現元素為舊版格式不存在Bookmarkname屬性而呼叫SetAttribute後,下一筆element讀取時一定會發生Invalidate exception,若改為for語法就一切正常,更改後為



   1: var elements = _xmlProject.GetElementsByTagName("Bookmark");
   2: int count = elements.Count;
   3: for(int i=0 ; i<count ; i++)
   4: {
   5:     var element = elements[i] as XmlElement;
   6:  
   7:     String bookmarkname = element.GetAttribute(@"Bookmarkname");
   8:     if (bookmarkname == null || bookmarkname == "")
   9:     {
  10:         bookmarkname = element.GetAttribute(@"Filename");
  11:         element.SetAttribute(@"Bookmarkname", bookmarkname);
  12:     }
  13: }

所以是C#的foreach並不是list的形式所以資料集合不能有記憶體配置的變化?還只是單純的bug?

2013年5月24日 星期五

[W8App] ListBox Binding data (3) – Binding Dynamic Data

XAML的Data Binding方式可隨開發者的需求而有許多不同的設計方式,我想要的需求是一個ListBox元件能隨時的更換繫結的資料集合,在程式中必需要Binding資料時呼叫以下的程式碼來建立繫結ListBox (myListBox)與資料集合(myList),若要更換資料集合時,只需要重新assing另一個物件即可

   1: myListBox.ItemsSource = myList;

myList是使用List<T>或是ObservableCollection<T>模版的考量方式可以從何時指定ItemsSource來看。


如果是先把所有的item加入到myList後才進行Binding,那麼用List<T>就可以了



   1: myList.Add(item1);
   2: myList.Add(item2);
   3: ...
   4: myListBox.ItemsSource = myList

如果是先進行Binding,之後才“慢慢”的加入item,那麼就必需用ObservableCollection<T>



   1: myListBox.ItemsSource = myList
   2: ...
   3: myList.Add(item1);
   4: myList.Add(item2);
   5: ...




如果我想要產生每個Item控制項包含一張Icon圖與文字的ListBox


ListBox_Binding


XAML程式碼應該會長的像這樣:



   1: <ListBox x:Name="myListBox">
   2:     <ListBox.ItemTemplate>
   3:         <DataTemplate>
   4:             <Grid>
   5:                 <Image Source="{Binding Image}" />
   6:                 <TextBlock Text="{Binding Title}"/>
   7:             </Grid>
   8:         </DataTemplate>
   9:     </ListBox.ItemTemplate>
  10: </ListBox>

ListBox的ItemsSource與資料集合Binding是在程式中完成,所以XAML中不特意指明Binding的對像,但是DataTemplate中的元件與資料集合就必需指明與元件Binding的路徑名稱,如上面例子的#5, #6所示。