UWP(Universal Windows Platform)介紹 — 快速上手
Categories:
- 1. Xaml
- 2. MVVM
- 3. ViewModel 與 Model 的互動
- 4. View 與 ViewModel 的互動
- 5. (View 與 ViewModel) 實作訊息通知 – INotifyPropertyChanged
- 6. (View 與 ViewModel) ListView 繫結到實作了訊息通知的來源 – ObservableCollection
- 7. (View) ListView 的 Item 範本
- 8. (ViewModel 與 Model) 資料轉換 – IValueConverter
- 9. (View) 修改 Control 的樣式
- 10. 使用 Panel
- 11. (View) 調適式 UI (Adaptive UI)
- 12. 版面配置原則
- 13. 在地化
- 14. 命名慣例
- 15. 注意
1. Xaml
1.1. XAML 簡介
- XAML 概觀
- 主要用於建立可視的 UI 元素.
- Xaml 的基本語法基於 XML.
- 宣告一個 namespace 的別名:
xmlns:controls="using:Common.Controls"
- 使用 namespace 中宣告的類別:
<controls:MyControl />
可重複使用的資源,
x:Key
<Style x:Key="TextBlock_Style" />
- 控制項元素的 Name,
x:Name
Xaml:
<MyControl x:Name="myControl" />
C#:
private MyControl myControl;
在地化
x:Uid
<TextBlock x:Uid="sampleText" />
1.2. 最基礎的控制項 – TextBlock, Button
<Page x:Class="MyPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<StackPanel>
<TextBlock Text="UWP Introduction" />
<Button Content="UWP Introduction" Click="Button_Click" />
</StackPanel>
</Page>
2. MVVM

- View 盡量只包含 UI 顯示的內容, View 大部分使用 Xaml 語言完成;
- ViewModel 應盡量不包含業務處理邏輯, 而是透過呼叫 Model 裡的函式完成動作;
- Model 應盡量包含所有的業務資料和邏輯, 並盡量不依賴 View 和 ViewModel;
3. ViewModel 與 Model 的互動
3.1. ViewModel 操作 Model 的資料
ViewModel:
public class ViewModel
{
private Model model;
private ChangeA()
{
this.model.A = "A";
}
}
Model:
public class Model
{
public string A { get; set; }
public string B { get; set; }
}
3.2. Model 通知 ViewModel – event
ViewModel:
public class ViewModel
{
private Model model;
private ChangeA()
{
r.BEventArgs += this.Handler;
}
private void Handler(object sender, EventArgs e)
{
AnyActions();
}
}
Model:
public delegate void BChangedHandler(object sender, EventArgs e);
public class Model
{
public string A { get; set; }
private string _B;
public string B
{
get { return this._B; }
set
{
this._B = value;
if (BEventArgs != null)
{
BEventArgs(this, new EventArgs());
}
}
}
public event BChangedHandler BEventArgs;
}
4. View 與 ViewModel 的互動
4.1. 資料繫結
- 資料繫結概觀
- 只有 DependencyProperty 可以被繫結
- 繫結在 Public 的 Property
- 謹慎重構
4.1.1. 繫結在 ViewModel
View:
<TextBlock Text={Binding SampleText} />
ViewModel:
public string SampleText { get; set; }
4.1.2. 繫結在其它 Control
View:
<TextBlock x:Name="TextBlock1" Text="SampleText" />
<Button Content="{Binding ElementName=TextBlock1, Path= Text}" />
4.1.3. 指定 DataContext
public ViewModelClass ViewModel { get; set; }
...
SpecifiedControl.DataContext = ViewModel;
...
5. (View 與 ViewModel) 實作訊息通知 – INotifyPropertyChanged
當 SampleText 發生變化時, 會通知到繫結在該 property 的 DependencyProperty
public class MyControlViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public void Notify(string propName)
{
if (this.PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propName));
}
}
private string _sampleText;
public string SampleText
{
get
{
return _sampleText;
}
set
{
_sampleText = value;
Notify(nameof(SampleText));
}
}
}
或者繼承 ViewModelBase
public class ViewModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public void Notify(string propName)
{
if (this.PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propName));
}
}
}
6. (View 與 ViewModel) ListView 繫結到實作了訊息通知的來源 – ObservableCollection

在 ListView 中實作繫結到具有訊息通知的 ItemSource
View:
<ListView ItemsSource="{Binding Items}">
ViewModel:
public ObservableCollection<Recording> Items { get; set; }
- ObservableCollection 只在 Item 新增\移除, 整個列表重新整理時才會產生訊息通知;
- 如果需要在 item Recording 的內容發生變化時通知介面, 需要由 Recording 實作 INotifyPropertyChanged.
7. (View) ListView 的 Item 範本
7.1. DataTemplate
View:
<ListView ItemsSource="{Binding Items}">
<ListView.ItemTemplate>
<DataTemplate DataType="local:Recording">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding A}" />
<TextBlock Text="{Binding B}" />
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
ViewModel:
public ObservableCollection<RecordingViewModel> Items { get; set; }
public class RecordingViewModel : INotifyPropertyChanged
{
...
實作 INotifyPropertyChanged
...
private Recording _recording;
public string A
{
get
{
return this._recording.A;
}
set
{
this._recording.A = value;
Notify(nameof(A));
}
}
public string B { get; set; } = this._recording.B;
public RecordingViewModel (Recording recording)
{
this._recording = recording;
}
}
Model:
public class Recording
{
public string A { get; set; }
public string B { get; set; }
public string C { get; set; }
... ...
}
比較:

7.2. UserControl
7.2.1. DependencyProperty
- Dependency Properties Overview
- 只有 DependencyProperty 能繫結到其它 property, 只有繫結能實作訊息通知到 View
- 具有優先順序
View:
<control:MyControl Text="App is on searching" IsSearching="{Binding ViewModel.IsSearching}" />
ViewModel:
public class MyControl
{
...
public static readonly DependencyProperty IsSearchingProperty =
DependencyProperty.Register
(
"IsSearching", typeof(Boolean),
typeof(MyControl), null
);
public bool IsSearching
{
get { return (bool)GetValue(IsSearchingProperty); }
set { SetValue(IsSearchingProperty, value); }
}
...
}
8. (ViewModel 與 Model) 資料轉換 – IValueConverter
可以使用IValueConverter來進行 ViewModel 與 Model 之間的資料轉換.
public class ShowAllButtonVisibilityConverter:IValueConverter
{
public object Convert(object value, Type targetType, object parameter, string language)
{
if (value is IList)
{
int count = (value as IList).Count;
if (count > 3)
{
return Windows.UI.Xaml.Visibility.Visible;
}
}
return Windows.UI.Xaml.Visibility.Collapsed;
}
public object ConvertBack(object value, Type targetType, object parameter, string language)
{
if (value is IList)
{
int count = (value as IList).Count;
if (count > 3)
{
return Windows.UI.Xaml.Visibility.Visible;
}
}
return Windows.UI.Xaml.Visibility.Collapsed;
}
9. (View) 修改 Control 的樣式
9.1. 在 Control 裡自訂 Style
View:
<TextBlock Foreground="Red" Text="SampleText" />
9.2. 使用統一的 Style – ResourceDictionary
View:
<Window.Resources>
<ResourceDictionary>
<Style TargetType="TextBlock" x:Key="ImportantText">
<Setter Property="Foreground" Value="Red" />
</Style>
</ResourceDictionary>
</Window.Resources>
...
<TextBlock Text="SampleText" Style={StaticResource ImportantText} />
9.3. 使用 ThemeResource
View:
<Window.Resources>
<ResourceDictionary>
<ResourceDictionary.ThemeDictionaries>
<ResourceDictionary x:Key="Light">
<Style TargetType="TextBlock" x:Key="ImportantText">
<Setter Property="Foreground" Value="Red" />
</Style>
</ResourceDictionary>
<ResourceDictionary x:Key="Dark">
<Style TargetType="TextBlock" x:Key="ImportantText">
<Setter Property="Foreground" Value="Yellow" />
</Style>
</ResourceDictionary>
<ResourceDictionary x:Key="HighContrast">
<Style TargetType="TextBlock" x:Key="ImportantText">
<Setter Property="Foreground" Value="Black" />
</Style>
</ResourceDictionary>
</ResourceDictionary.ThemeDictionaries>
</ResourceDictionary>
</Window.Resources>
...
<TextBlock Text="SampleText" Style={ThemeResource ImportantText} />
10. 使用 Panel
10.1. Grid
特點:
- 預設 Height/Width 等於父級元素;
建議:
- 在 Grid 中定義子元素的版面配置大小;
- 以比例顯示;
10.2. StackPanel
特點:
- 可以超越父元素邊界;
- Height 或 Width 隨 Panel 內的元素變化;
建議:
- 靈活使用 Padding 和比例;
- 在父級元素給 StackPanel 的 Width 或 Height 一個值, 以防止 Control 越界;
11. (View) 調適式 UI (Adaptive UI)
- 使用 VisualStateManager.VisualStateGroup
<VisualStateGroup>
<VisualState x:Name="WideLayout">
<VisualState.StateTriggers>
<AdaptiveTrigger x:Name="WideLayoutTrigger" MinWindowWidth="1280" />
</VisualState.StateTriggers>
<VisualState.Setters>
<Setter Target="SystemUpdateSideGrid.Width" Value="800" />
<Setter Target="SystemUpdateSideGrid.Grid.Row" Value="0" />
</VisualState.Setters>
</VisualState>
<VisualState x:Name="MidLayout">
<VisualState.StateTriggers>
<AdaptiveTrigger x:Name="MidLayoutTrigger" MinWindowWidth="700" />
</VisualState.StateTriggers>
<VisualState.Setters>
<Setter Target="SystemUpdateSideGrid.Width" Value="400" />
<Setter Target="SystemUpdateSideGrid.Grid.Row" Value="1" />
</VisualState.Setters>
</VisualState>
...
</VisualStateGroup>
12. 版面配置原則
- 不顯式設定元素的尺寸;
- 不使用螢幕座標指定元素的位置;
- 容器內子元素共享可用的空間;
- 可巢狀的版面配置容器;
13. 在地化
- 使用 x:Uid, 元素的唯一標記符
<TextBlock x:Uid="S_TextBlock1" />
- 使用 Resources File
<data name="S_TextBlock1.Text" xml:space="preserve">
<value>Sample text</value>
</data>
14. 命名慣例
大駝峰 big camel-case: firstName
小駝峰 little camel-case: FirstName
- Class: 大駝峰式
- Property: 大駝峰式
- Field: 小駝峰式 with prefix “_”
- Control in Xaml: 小駝峰式
15. 注意
- 謹慎重新命名 ViewModel 中的 Property, 因為 Xaml 中的 Binding 名稱不會跟隨重構;