WPFとWindowsフォームの違い — DataContext・Bindingの衝撃

モニター2画面に向かって開発しているイメージ画像。WPF開発らしさをだしている

📦 この記事のサンプルプログラムはGitHubで公開しています。
記事を読みながら実際に動かすと理解がさらに深まります。
👉 https://github.com/Kunitaroo/WpfBindingSamples
(GitHubページの「Code」→「Download ZIP」でダウンロードできます)

目次

はじめに:「Windowsフォームと同じでしょ?」という罠

WPF(Windows Presentation Foundation)は、Microsoftが開発したWindowsアプリケーション用のUIフレームワークです。Windowsフォームの後継にあたり、.NET環境で動作します。画面の見た目をXAMLというXML形式で記述するのが大きな特徴で、現在も業務アプリ開発で広く使われています。

その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(label1textBox1)を直接触るのが当たり前の世界です。

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.Textlabel2.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パターン完全解説 — ボタンのグレーアウトも自動でできる」 に進みます。

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!
目次