在 WinForms 的世界裡,常常需要寫程式碼去「手動更新畫面」,例如:
textBox1.Text = stock.Price.ToString();
但在 WPF,我們可以用 Binding (資料綁定),讓 UI 自動跟 ViewModel 的資料保持同步。
這不只讓程式碼更乾淨,也是 MVVM 架構的核心。
簡單來說:
Binding = View 與 ViewModel 的資料連線
在 XAML 中,我們可以把控制項屬性綁定到 ViewModel 的屬性:
<TextBlock Text="{Binding StockName}" />
這表示:
TextBlock.Text
會顯示 ViewModel 裡的 StockName
屬性值。StockName
改變,UI 也會跟著更新(前提是 ViewModel 有實作通知機制,稍後會介紹)。Binding 定義了資料同步的方向,主要有 4 種模式:
只在第一次載入時綁定,之後不會再更新。
<TextBlock Text="{Binding StockName, Mode=OneTime}" />
ViewModel → View(單向)。資料來源改變,UI 會更新;UI 改變不會回傳。
<TextBlock Text="{Binding Price, Mode=OneWay}" />
ViewModel ↔ View(雙向)。
<TextBox Text="{Binding TargetPrice, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
View → ViewModel(單向)。很少用,但可以監聽 UI 狀態。
<TextBox Text="{Binding UserInput, Mode=OneWayToSource}" />
關鍵在於:INotifyPropertyChanged。
假設我們有一個 ViewModel:
using System.ComponentModel;
public class StockViewModel : INotifyPropertyChanged
{
private string _stockName;
private decimal _price;
public string StockName
{
get { return _stockName; }
set
{
_stockName = value;
OnPropertyChanged(nameof(StockName));
}
}
public decimal Price
{
get { return _price; }
set
{
_price = value;
OnPropertyChanged(nameof(Price));
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
這樣當 ViewModel 的屬性變更時,會觸發通知,WPF UI 就會自動刷新。
這時候就要用 TwoWay Binding。
<TextBox Text="{Binding Price, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
這表示:
StockViewModel.Price
改變,TextBox 顯示會更新。StockViewModel.Price
也會同步更新。XAML:
<StackPanel>
<TextBox Text="{Binding StockName, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
<TextBox Text="{Binding Price, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
<TextBlock Text="{Binding StockName}" FontSize="16"/>
<TextBlock Text="{Binding Price}" FontSize="16"/>
</StackPanel>
ViewModel:
public class StockViewModel : INotifyPropertyChanged
{
private string _stockName = "2330 台積電";
private decimal _price = 1265m;
public string StockName
{
get { return _stockName; }
set
{
_stockName = value;
OnPropertyChanged(nameof(StockName));
}
}
public decimal Price
{
get { return _price; }
set
{
_price = value;
OnPropertyChanged(nameof(Price));
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName) =>
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
結果:
Price
,TextBox 與 TextBlock 會自動更新。今天我們學會了: