📦 この記事のサンプルプログラムはGitHubで公開しています。
記事を読みながら実際に動かすと理解がさらに深まります。
👉 https://github.com/Kunitaroo/WpfBindingSamples
(GitHubページの「Code」→「Download ZIP」でダウンロードできます)
はじめに:「Windowsフォームと同じでしょ?」という罠
そのWPFについて、正直に告白します。
私はかつてWPFを使った開発案件を「Windowsフォームと変わらないだろう」と軽く見て受けてしまい、痛い目を見た経験があります。15年ほど前の話です。
当時はWPFを日本語で解説しているサイトもほとんどなく、「なんでボタンのイベントがここに書いてあるの?」「DataContextって何?」という状態から抜け出せないまま、プロジェクトを離れることになりました。
でも今、改めてWPFを触ってみると「これはちゃんと理解すれば絶対便利だ」と確信しています。今回はWindowsフォーム経験者の目線で、WPFの核心をできるだけわかりやすく解説します。
まず結論から:何が根本的に違うのか
Windowsフォームは 「コードで画面を動かす」 設計です。
WPFは 「データが変われば画面が自動で変わる」 設計です。
この一文だけでは伝わりにくいと思うので、具体的に比べてみましょう。
Windowsフォームの場合
テキストボックスに入力した内容をラベルに表示するとき、こう書きますよね。
private void textBox1_TextChanged(object sender, EventArgs e)
{
label1.Text = "入力された文字:" + textBox1.Text;
}「textBox1 の内容が変わったら label1 に代入する」という処理を自分で書きます。コントロールのID(label1、textBox1)を直接触るのが当たり前の世界です。
WPFの場合
同じことをWPFでやると、C#のコードにラベルやテキストボックスのIDが一切登場しません。
// ViewModelのプロパティを変えるだけ
public string InputText
{
get => _inputText;
set
{
_inputText = value;
Notify(nameof(InputText));
Notify(nameof(DisplayText)); // ← これだけで画面が動く
}
}
public string DisplayText =>
string.IsNullOrEmpty(_inputText)
? "(まだ入力されていません)"
: $"入力された文字:{_inputText}";「え、どこでラベルを更新してるの?」と思いますよね。答えはXAMLのBindingにあります。
DataContextとは何か
WPFで最初に理解すべき概念が DataContext です。
ひと言で言うと「この画面のデータの出どころ」です。
XAMLファイルの上の方にこういう記述があります。
<Window.DataContext>
<local:MainViewModel />
</Window.DataContext>これは「このウィンドウのデータは MainViewModel というクラスから来ますよ」という宣言です。
Windowsフォームに例えるなら「このフォームは○○クラスが管理します」と先に宣言するようなイメージ。ただしWindowsフォームと違うのは、フォームとクラスがコードを通さず自動でつながる点です。
Bindingとは何か
DataContextを設定したら、あとは {Binding プロパティ名} と書くだけです。
<!-- ViewModelの InputText プロパティと自動連結 -->
<TextBox Text="{Binding InputText, UpdateSourceTrigger=PropertyChanged}" />
<!-- ViewModelの DisplayText プロパティを自動表示 -->
<Label Content="{Binding DisplayText}" />{Binding InputText} と書いたコントロールは InputText プロパティだけを監視しています。プロパティの値が変わると、画面のコントロールが自動で更新されます。
「変わったよ!」と通知する仕組み
ViewModelのプロパティが変わったとき、画面に通知するのが INotifyPropertyChanged です。
public class MainViewModel : INotifyPropertyChanged
{
// 画面に「変わったよ!」と知らせる仕組み
public event PropertyChangedEventHandler? PropertyChanged;
private void Notify(string name) =>
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
private string _inputText = "";
public string InputText
{
get => _inputText;
set
{
_inputText = value;
Notify(nameof(InputText)); // ← ここで通知
}
}
}難しい名前ですが、やっていることはシンプルです。
「このプロパティが変わりましたよ」と画面に知らせているだけです。
データの流れをまとめると
テキストボックスに入力
↓
InputText プロパティが更新
↓
Notify() で「変わったよ!」と通知
↓
Bindingが検知して画面を自動更新
C#コードが直接ラベルを触る処理はどこにもありません。
ラベルが複数あったらどうなるの?
「Bindingって、複数のラベルがあったら区別できるの?」という疑問が出てきますよね。
答えは「Bindingで設定したプロパティ名がIDの代わり」です。
// ViewModelに複数のプロパティを用意
public string FirstName { get; set; }
public string LastName { get; set; }
public string Age { get; set; }
public string FullName => $"{LastName} {FirstName}";<!-- それぞれ別のプロパティに紐づく -->
<TextBox Text="{Binding FirstName, UpdateSourceTrigger=PropertyChanged}" />
<TextBox Text="{Binding LastName, UpdateSourceTrigger=PropertyChanged}" />
<TextBox Text="{Binding Age, UpdateSourceTrigger=PropertyChanged}" />
<Label Content="{Binding FullName}" />{Binding FirstName} と書いたコントロールは FirstName だけを見ています。他には干渉しません。
さらに便利なのが、同じプロパティを複数の場所に表示できることです。
<!-- 同じ FullName を2か所に表示 -->
<Label Content="{Binding FullName}" FontSize="20" />
<TextBlock Text="{Binding FullName}" Foreground="Gray" />Windowsフォームなら label1.Text と label2.Text を両方書かないといけませんが、WPFはプロパティを1回変えるだけで紐づいている全コントロールが一斉に更新されます。
ボタンイベントはどうなるの?
Windowsフォームでボタンをダブルクリックすると自動生成されるあれ、WPFでは Command(コマンド) という仕組みに変わります。
Windowsフォームの場合
// Windowsフォームのボタンイベント
private void button1_Click(object sender, EventArgs e)
{
label1.Text = "ボタンが押された";
}WPFの場合
WPFではこうなります。
// ViewModel にCommandを追加
public ICommand ClickCommand { get; }
public MainViewModel()
{
// ボタンが押されたときの処理をここに書く
ClickCommand = new RelayCommand(
execute: () => Message = $"「{InputText}」が送信されました!",
canExecute: () => !string.IsNullOrEmpty(InputText) // 空のときグレーアウト
);
}<!-- XAMLのボタンにCommandをBinding -->
<Button Content="送信" Command="{Binding ClickCommand}" />C#コードにボタンIDが出てきません。しかも canExecute の条件を書くだけで、テキストが空のときボタンを自動でグレーアウトできます。
これはWindowsフォームでは自分でコードを書かないといけない部分です。
「WPFはDBアクセスに向かない」は誤解?
最初にこう思いませんでしたか?WPFは 「データが変われば画面が自動で変わる」の 設計であれば
「WPFってリアルタイム更新の監視画面に向いてるでは?
DBのCRUD操作(データの作成、読み込み、更新、削除)はWindowsフォームの方がよくない?」
実はこれは誤解です。WPFはデータベース操作こそ得意なんです。
// Windowsフォームのパターン
var data = db.GetUsers();
dataGridView1.DataSource = data;
// 選択が変わるたびに手でセット
label1.Text = user.Name;
label2.Text = user.Email;
label3.Text = user.Phone;
// WPFのパターン
Users = new ObservableCollection<User>(db.GetUsers());
// XAMLのBindingが自動で処理してくれる
// labelを触るコードが不要一覧をクリックしたら詳細が自動表示、というパターンはWPFが最も得意とするところです。
結局どう使い分ける?
| 場面 | 向いている技術 |
|---|---|
| 既存Windowsフォームの改修・保守 | Windowsフォームのまま |
| 帳票・印刷中心のシンプルな画面 | Windowsフォームで十分 |
| 一覧+詳細のDB操作画面 | WPFが得意 |
| リアルタイム更新の監視画面 | WPFが特に得意 |
| 新規で作るWindowsアプリ全般 | WPFを選ぶべき |
「DB操作はWindowsフォーム、即時更新はWPF」ではなく、
「新規で作るならWPF、既存の改修はWindowsフォームのまま」が現実的な使い分けです。
まとめ:発想の転換が鍵
WPFで一番大切な発想の転換は、ここです。
Windowsフォームの発想
「ラベルのTextを変えたい」→ label1.Text = ○○ と書く
コントロールが主役
WPFの発想
「データを変えたい」→ プロパティを更新するだけ
データが主役、画面は勝手についてくる
15年前の私はこの発想の転換ができなかったために挫折しました。でも今、一つひとつ手を動かしながら確認すると**「なるほど、こういうことだったのか」**と腑に落ちます。
この記事のサンプルプログラムはGitHubで公開しています。
ぜひ手を動かしながら確認してみてください。
👉 サンプルプログラムはこちら(GitHub)
(「Code」→「Download ZIP」でダウンロードできます)
次回は 「Commandパターン完全解説 — ボタンのグレーアウトも自動でできる」 に進みます。
