# 6/15 學習進度 ###### tags: `binding` :::info ### :notebook_with_decorative_cover: 資源整理 #### C# * [C# 程式設計手冊](https://docs.microsoft.com/zh-tw/dotnet/csharp/programming-guide/) #### UWP * [UWP youtube tutorial](https://www.youtube.com/watch?v=SiaUzO72tqE&list=PLi2hbezQRVS0cPMeW3uDlUHnO_rPvJCV9) * [實作範例(紫外線偵測)](https://blogs.msdn.microsoft.com/hermanwu/2015/08/03/windows-10-uwa-uwp-app/) * [victor推薦的查詢教學](https://developer.microsoft.com/en-us/windows/samples) * [UI_Event溝通](https://docs.microsoft.com/en-us/windows/uwp/xaml-platform/events-and-routed-events-overview) * [LifeCycle](https://github.com/victorfu/AppLifeCycleDemo/blob/master/AppLifeCycleDemo/App.xaml.cs) * [常用UI](https://github.com/Microsoft/Windows-universal-samples/tree/master/Samples/XamlUIBasics) ::: * 作業 : [實作 gallery](https://github.com/wendeehsu/gallery) * 資源 : [tutorial](https://www.youtube.com/watch?v=zuJZrLkKGUw&t=0s&index=41&list=PLi2hbezQRVS0cPMeW3uDlUHnO_rPvJCV9) ## 問題整理 :::warning ### 已解決 * 如何讀取 JSON 檔 * **[Error]** CS0120 需要有物件參考,才可使用非靜態欄位、方法或屬性 * **[Error]** Cannot resolve DataType * 想在做UWP時也能有 Console.WriteLine()相似的功能除錯 ::: :::success ### 找到答案但還沒試成功 * **[Error]** Synchronous operations should not be performed on the UI thread. * **[Error]** The application called an interface that was marshalled for a different thread. * 抓資料用非同步的thread,結果資料還沒load完ui就先呈現了 ::: :::danger ### 未解決(完全看不懂><) * **[Error]** Access to the path is denied ::: > Q: bug是公的還是母的? > A: 母的,因為它會生出更多更多的bug QQQQ > [name=遠志][time=Mon, Jun 11, 2018 11:12 PM] --- :::warning 以下問題已解決 ::: ## 如何讀取JSON檔 [JSON.NET 教學](https://weblog.west-wind.com/posts/2012/Aug/30/Using-JSONNET-for-dynamic-JSON-parsing) ```csharp= <!--from local file--> using System.IO; string text = System.IO.File.ReadAllText(@"C:\Users\徐遠志\C#\active_players.json"); ``` ```cpp= <!--from url--> using Newtonsoft.Json.Linq; using System.Net; var text = new WebClient().DownloadString("http://127.0.0.1:8887/active_players.json"); ``` 上面兩個在console的專案裡都可以,但換成UWP只有下面這個可以成功 >還不清楚原因>< ```csharp= <!--work on UWP--> using System.Net.Http; using Newtonsoft.Json.Linq; using (var httpClient = new HttpClient()) { var text = await httpClient.GetStringAsync("http://127.0.0.1:8887/active_players.json"); // Now parse with JSON.Net } ``` 讀進來都是一整條string,接著可以這樣針對每一個物件做處理: ```csharp= JArray jsonVal = JArray.Parse(text) as JArray; dynamic albums = jsonVal; foreach (dynamic album in albums) { //json長相 //[{ "team":"aaa"},{"team":"bbb"}] Console.WriteLine(Convert.ToString(album.team));  } //output:aaa // bbb ``` #### 補充~ dynamic `dynamic` 是不會檢查資料型態的型別,行為與 `var` 類型類似,只是編譯時期不會對 `dynamic` 做類型檢查 (會在執行階段解決或跳Error ) > 如果在 `var` 中設定不存在的成員時,編譯器會擲回編譯錯誤,而且用 `var` 宣告的變數,其成員也會由 Intellisense 來列舉,可見 `var` 是可以在編譯時期決定型別的資料型態。 ```csharp dynamic d = 3; d = d / 1.5; //2 var v = 3; //v = (local variable) int v = v / 1.5; //編譯時就會顯示錯誤 ``` [官方文件](https://docs.microsoft.com/zh-tw/dotnet/csharp/language-reference/keywords/dynamic) ## Static 物件參考 **發生** ```cpp= public static List<Player> GetPlayer() { //當SetPlayers()是public async Task await SetPlayers(); return playerList; } ``` **解法** 如果錯誤訊息是`CS0120 需要有物件參考,才可使用非靜態欄位、方法或屬性`,表示該物件或函式少了`static`類別,像我上面的程式改成這樣就可以了 ```cpp public static async Task SetPlayers() ``` #### 補充~static [官方文件](https://msdn.microsoft.com/zh-tw/library/79b3xss3(VS.80).aspx) 未與特定物件關聯的方法可以使用 `static` 類別當做組織單位。此外,靜態類別也使實作時,不需要建立物件,即可呼叫方法。 ```csharp=0 //remind: static有共用的特性,所以不能做到個體化 public class People { private static int _age; public int Age { get => _age; set => _age = value; } } People a = new People(); People b = new People(); a.Age = 0; Console.WriteLine($"{a.Age},{b.Age}"); //0,0 a.Age += 1; Console.WriteLine($"{a.Age},{b.Age}"); //1,1 ``` ## Cannot resolve DataType **發生** 做 `x:bind` 的時候這樣寫: ```html <Datatemplate DataType = "{x:bind MyClassName}"> <!--已經在cs裡定義Class MyClassName了--> ``` **解法** 要乖乖照著[範例](https://docs.microsoft.com/en-us/windows/uwp/design/controls-and-patterns/item-templates-listview)做(不過還沒研究出差別:P) ###### step 1 把關於 `MyClassName` 的定義寫進`view.cs` ###### step 2 在 `xaml`的 `<Page>` 裡加上 ```html xmlns:data="using:Gallery_NBA.Models" ``` ###### step 3 ```html <DataTemplate x:DataType="data:MyClassName"> ``` ## Debug.WriteLine() ```cpp using System.Diagnostics; Debug.WriteLine(ln); //東西會顯示在"輸出"那個視窗 ``` --- :::success 以下問題找到解法但還在研究中 ::: ## Synchronous operations should not be performed on the UI thread **發生情形** 在MainPage.xaml.cs的 ```cpp= public MainPage(){ this.InitializeComponent(); Setup(); //Setup()用來讀json檔(public void),他是出錯的原因 } ``` 應該改成: ```csharp= public MainPage() { this.InitializeComponent(); var t = Task.Run(() => Setup()); t.Wait(); } public static async Task SetPlayers() { using (var httpClient = new HttpClient()) { var text = await httpClient.GetStringAsync("http://127.0.0.1:8887/active_players.json"); } // parse with JSON.Net ... } ``` > http request 是一種網路連線獲得資料的方法, 實際上他就是一個 IO 行為, 在 GUI programming 的基本guildline, IO 的工作是被禁止在 UI thread (which is main thread) 執行的, 有些語言會比較激烈禁止, 也就是用 exception 禁止你做這件事情 >  >這道理其實不難懂, UI畫面的呈現都是 computing 的工作, 如果把 UI thread 拿去執行 IO 工作, UI thread 就會卡住, 因為他在等 IO, 然後你就會發現畫面卡卡的 ... > [name=Victor] #### 補充~Task.Run 假設要做的事很耗時,就會開分支來處理 ```csharp private void Setup(){ for(int i =0; i < 1000000000; i++){;} } ``` > 時間延遲也可以用`Task.Delay(ms)` ,UWP不支援`Thread.Sleep(ms)` ><[color=#d2d85d] ##### 方法1. Wait() 這樣會凍結整個 UI 等到 `Task` 結束才繼續執行 ```csharp= private void BtnClick1(object sender, Windows.UI.Xaml.RoutedEventArgs e) { var t = Task.Run(() => Setup()); //Button按下去後,畫面就沒辦法做其他的事 //等到Setup()結束才能繼續 t.Wait(); btn1.Content = "finish"; } ``` ##### 方法2. async await 這樣UI可以繼續做其他的事而不會被耗時的分支影響 ```csharp= private async void BtnClick2(object sender, Windows.UI.Xaml.RoutedEventArgs e) { var t = Task.Run(() => Setup()); var result = await t; btn2.Content = "finish2"; } ``` * [官方文件~ Task.Run()](https://msdn.microsoft.com/zh-tw/library/hh195051(v=vs.110).aspx) * [官方文件~ async](https://docs.microsoft.com/zh-tw/dotnet/csharp/language-reference/keywords/async) * [StackOverflow](https://stackoverflow.com/questions/9519414/whats-the-difference-between-task-start-wait-and-async-await) ## The application called an interface that was marshalled for a different thread **中譯** 應用程式所呼叫了整理給不同執行緒的介面 **發生** 用 `Setup()` 這個Task做json的讀取時,嘗試每讀一個值就直接渲染進 `xaml` 的`<ListView x:Name = "debugList">` ```csharp= public static async Task SetPlayers() { using (var httpClient = new HttpClient()) { var text = await httpClient.GetStringAsync("http://127.0.0.1:8887/active_players.json"); JArray jsonVal = JArray.Parse(text) as JArray; dynamic albums = jsonVal; foreach (dynamic album in albums) { Player p = new Player(ln, fn, img); //這行會出錯,拔掉就沒事了 debugList.Items.Add(p.LastName); } } } ``` #### 原因 * [官方解釋](https://msdn.microsoft.com/zh-tw/library/fd85b3df.aspx#BKMK_A_statement_in_a_foreach_For_Each_in_Visual_Basic_block_changes_the_collection_it_is_iterating)  * [其他參考](https://dotblogs.com.tw/shinli/2015/04/16/151076) > 在非UI thread直接存取UI elements `debugList.Items.Add(p.LastName);` 因為你是這樣使用的 `var t = Task.Run(() => Setup());`時, Task.Run() 的參數是一個 function () => Setup(), 這個function會被執行在一個新的thread 所以你必須在UI thead update: >>await this.Dispatcher.RunAsync(CoreDispatcherPriority.High, () =>{}); ## 畫面比資料早load出來 **發生** 抓資料用非同步的thread,結果資料還沒load完ui就先呈現了 ```csharp= public sealed partial class MainPage : Page { List<Player> showPlayer; public MainPage() { this.InitializeComponent(); showPlayer = PlayerManager.GetPlayer(); } } ``` ```csharp= public class PlayerManager { public static List<Player> playerList = new List<Player>(); public static async Task SetPlayers() { //read json from url and update to playerList } public static List<Player> GetPlayer() { SetPlayers(); Debug.WriteLine(playerList.Count); //0 //應該是這邊不同步,不然長度不可能是0 return playerList; } } ``` #### 解法 >`INotifyPropertyChanged` 就是拿來做這件事情的 只是你的狀況是他在another thread load data [name=victor] #### 解法一(硬解) > 關鍵是 `await this.Dispatcher.RunAsync(CoreDispatcherPriority.High, () =>{});` 必須要在UI thread which is also main thread[name=victor] ```cpp= private void LoadData() { this.LoadData(false); } private async void LoadData(Boolean onlyToday) { await Task.Run(async () => { // get data from database var tmp = onlyToday ? UserDataService.GetUserItemsByToday() : UserDataService.GetUserItems(); await this.Dispatcher.RunAsync(CoreDispatcherPriority.High, () => { // save to collection and notify UI this.UserItems = tmp; IsLoading = false; }); }); } ``` #### 解法二(優雅解) [可以參考victor的github](https://github.com/victorfu/MVVMApp/blob/master/MVVMApp/ViewModels/ImageGalleryViewModel.cs) ```cpp public ImageGalleryViewModel() { Source = new NotifyTaskCompletion<ObservableCollection<SampleImage>>(SampleDataService.GetGallerySampleData()); } ``` ##### 補充~ NotifyTaskCompletion --- :::danger 以下問題未解決 ::: ## Access to the path is denied **發生** 嘗試用UWP讀絕對路徑`(C:/Users/.../data.json)`下的json > UWP的檔案權限設定很嚴格阿,直接access任意地方資料應該會有問題,可以放在一些default的路徑裡面,例如系統文件/照片/影片 資料匣,或是程式預設路徑裡 > [name=TY] > windows 10不能直接存取 > [name=victor] 有两个方式: 1. 使用 [file picker](https://docs.microsoft.com/en-us/windows/uwp/files/quickstart-using-file-and-folder-pickers) 这样你可以存取任何地方 但是必须经过使用者自己点取 (也就是授权) ``` StorageFolder localFolder = ApplicationData.Current.LocalFolder; ``` >想知道上面那行會存到哪可以看[stackoverFlow](https://stackoverflow.com/questions/24095230/where-can-i-find-files-saved-in-applicationdata-current-localfolder-while-develo?utm_medium=organic&utm_source=google_rich_qa&utm_campaign=google_rich_qa)然後教我>< 3. [使用 app data 的 storage]( https://docs.microsoft.com/en-us/windows/uwp/design/app-settings/store-and-retrieve-app-data) > 研究中。。。 > 我目前暫時把json丟到 `localhost` 然後透過`127.0.0.1`的URL叫它 > (有試過用`Console`專案能順利讀寫,但同樣方式`UWP`會跳Error) > [name=遠志] --- # Notes ## GridView [官方參考](https://docs.microsoft.com/en-us/uwp/api/windows.ui.xaml.controls.gridview) >* GridView is an `ItemsControl`, containing a collection of items of any type. >* `ItemsSource` data source >* `ItemTemplate` The appearance of individual items in each group. >* `DataTemplate` define the layout of controls used to display an individual item. ```htmlmixed=\ <GridView ItemsSource="dataSource"> <GridView.ItemTemplate> <DataTemplate> </DataTemplate> </GridView.ItemTemplate> </GridView> ``` ##### 延伸 >> [Item templates for grid view](https://docs.microsoft.com/zh-tw/windows/uwp/design/controls-and-patterns/item-templates-gridview) ## Data binding [官方參考~起手式](https://docs.microsoft.com/en-us/windows/uwp/data-binding/data-binding-quickstart) [官方參考~深入版](https://docs.microsoft.com/en-us/windows/uwp/data-binding/data-binding-in-depth) * **`binding source`** 想呈現的資料 * **`binding target`** 想更動的部分ex. `TextBlock` 的 `Text` * **`binding object`** `x:bind` 或 `Binding` 指向的東西 #### Binding Mode [官方參考](https://docs.microsoft.com/en-us/uwp/api/windows.ui.xaml.data.bindingmode) |OneTime| OneWay|TwoWay| | -------- | -------- |---- | | 為`x:bind`的`default` ,僅在 binding 建立當下更新值 | 為`Binding`的`default` ,除了binding建立的當下,Changes to the source object can also propagate to the target.|Updates either the target or the source object when either changes. When the binding is created, the target property is updated from the source.| #### [x:bind v.s. Binding](https://stackoverflow.com/questions/37398038/difference-between-binding-and-xbind?utm_medium=organic&utm_source=google_rich_qa&utm_campaign=google_rich_qa) * `x:Bind` 是 `Binding` 的替代項目(Windows10 的新功能)。 `{x:Bind}` 沒有 `{Binding}` 的部分功能,但在執行時所需的時間和記憶體都比`{Binding}`少,並且支援較強的偵錯功能。 * **`{x:Bind}`** 會執行它在編譯階段產生的特殊用途程式碼 * **`{Binding}`** 會使用一般用途的執行階段物件檢查。 >關於 [{x:bind}](https://chenrensong.com/uwp-xbind) > >要用 `INotifyPropertyChanged` 通知 UI有變化,因為他不支持UpdateSourceTrigger > >[INotifyPropertyChanged 簡單參考 ](https://stackoverflow.com/questions/48290663/textblock-with-binding-not-updated-on-property-change) >[name=TY] ```csharp public event PropertyChangedEventHandler PropertyChanged; private void OnPropertyChanged(string propertyName) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); 加這兩個宣告,然後改完title以後加上: Title = "Wonderful"; OnPropertyChanged(null); 以及 Mode=OneWay ``` ##### 如果出現這種Error ```cpp CS1503 引數 1: 無法從 'System.Collections.Generic.List<gallery.Models.Book>' 轉換成 'System.Collections.ObjectModel.ObservableCollection<gallery.Models.Book>' ``` 就把`xaml`裡的程式剪下貼上即可 >`x:bind`造成的吧 ( ?[color=#188bf7] ## ImageBrush [參考](https://docs.microsoft.com/en-us/uwp/api/windows.ui.xaml.media.imagebrush) 可以讓物件以圖片為背景 ```htmlmixed <StackPanel.Background> <ImageBrush ImageSource="{x:Bind BookImg}"/> </StackPanel.Background> ``` >可以設 `opacity` 喔! ## ObservableCollection * [youtube tutorial](https://www.youtube.com/watch?v=dw2oDaQV0Qo&t=5s) * [官方用法介紹](https://msdn.microsoft.com/zh-tw/library/ms668604(v=vs.110).aspx) 跟一般的Collection最大的不同地方在,`ObservableCollection`可以被 **監控**,所以改`dataSource`的時候會同步渲染到畫面上 ```csharp //使用方式和 list 很像 private ObservableCollection<Book> Books; ``` ## KeyDown Event xaml: ```csharp <TextBox x:Name="Search" KeyDown="OnKeyDownHandler"/> ``` xaml.cs: ```csharp private void OnKeyDownHandler(object sender, KeyRoutedEventArgs e) { if(Convert.ToString(e.Key) == "Enter") { //按Enter之後會觸發的事件 } } ``` ## 實作 - Gallery `ItemsSource` 指向資料的集合,這裡的Books是一個list ```htmlmixed= <GridView Grid.Row="2" ItemsSource="{x:Bind Books}"> <GridView.ItemTemplate> <DataTemplate x:DataType="data:Book"> <StackPanel Height="150" Width="150"> <StackPanel.Background> <ImageBrush ImageSource="{x:Bind BookImg}"/> </StackPanel.Background> <Grid Height="Auto" Width="150"> <TextBlock Text="{x:Bind BookName}"/> </Grid> </StackPanel> </DataTemplate> </GridView.ItemTemplate> </GridView> ``` ## 實作 - Carousel ### Universal Character Map的轉換問題 Universal Character Map的轉換問題。 如果要把播放及暫停鍵,在`MediaElementState.Paused`判斷是否要轉換按鍵圖案,轉換會失敗。 ```xml= <Button FontFamily="Segoe MDL2 Assets" FontSize="48" Content="&#xE103;" /> ``` 那麼點c#要這樣寫,不能直接`PauseButton.Content = "&#xE101;"` ```csharp= private void PauseVideo(object sender, RoutedEventArgs e) { if (FirstVideo.CurrentState == MediaElementState.Paused) { FirstVideo.Play(); PauseButton.Content = "\uE102"; } else { FirstVideo.Pause(); PauseButton.Content = "\uE103"; } } ``` ### 轉換MediaElement以及Image的`Source` ```cpp VideoPlace.Source = new Uri(this.BaseUri, "/Assets/Video/second.mp4"); ImageElement.Source = new BitmapImage(new Uri("ms-appx:///Assets/second.jpg")); ``` ### 取得UWP物件 ```cpp // <object Name="Name"> var song = (MediaElement)FindName(Name); ``` ## To-read List - [x] [DataTemplate](https://docs.microsoft.com/en-us/uwp/api/windows.ui.xaml.datatemplate) - [x] [ItemsControl.ItemTemplate](https://docs.microsoft.com/en-us/uwp/api/windows.ui.xaml.controls.itemscontrol.itemtemplate#Windows_UI_Xaml_Controls_ItemsControl_ItemTemplate) - [ ] [UI thread](https://docs.microsoft.com/zh-tw/windows/uwp/debug-test-perf/keep-the-ui-thread-responsive) - [x] [static](https://social.msdn.microsoft.com/Forums/zh-TW/51b6d9b7-699c-4627-8cb5-dcbbf5ff90be/27714211612098629694quot38656352012637729289202142144332771251?forum=233) - [ ] [partial用法](https://dotblogs.com.tw/dc690216/2010/03/13/14008) - [ ] [public, protected, private, internal, protected internal 差別](http://unicorntudo.blogspot.com/2013/07/c3-public-protected-private-internal.html) - [ ] [Inotify](https://docs.microsoft.com/en-us/dotnet/framework/wpf/data/how-to-create-and-bind-to-an-observablecollection)