# 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=""
/>
```
那麼點c#要這樣寫,不能直接`PauseButton.Content = ""`
```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)