1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

#WPF DataGrid の右クリックメニューから編集ダイアログを開いて閉じるまで

Last updated at Posted at 2025-11-13

本記事では、DataGrid の行を右クリック → 編集メニュー → 編集ダイアログ表示 → 保存 or キャンセル → ウィンドウを閉じる
という一連の流れを、MVVM + CommunityToolkit.Mvvm を使った実装としてまとめます。

この記事で扱う構造は以下のとおりです。


🔧 構成概要

  1. DataGrid 行の右クリックメニュー(View)
  2. EditCommand を受けて編集処理をする MainViewModel
  3. 編集ダイアログ(View / ViewModel)
  4. Messenger を利用してダイアログを閉じる仕組み
  5. DialogService(ViewModel からウィンドウを開くための仲介)
  6. ダイアログを閉じるためのイベント & メッセージクラス(← 追記した部分)

1. DataGrid 行に右クリックメニューを追加する(View)

<DataGrid.RowStyle>
    <Style TargetType="DataGridRow">
        <!-- DataGrid 全体の DataContext(MainViewModel) を Tag に保持 -->
        <Setter Property="Tag"
                Value="{Binding DataContext, RelativeSource={RelativeSource AncestorType=DataGrid}}" />

        <Setter Property="ContextMenu">
            <Setter.Value>
                <ContextMenu>
                    <MenuItem Header="編集..."
                              Command="{Binding PlacementTarget.Tag.EditCommand,
                                                RelativeSource={RelativeSource AncestorType=ContextMenu}}"
                              CommandParameter="{Binding}" />
                </ContextMenu>
            </Setter.Value>
        </Setter>
    </Style>
</DataGrid.RowStyle>

ポイント

  • CommandParameter="{Binding}" により、右クリックされた行のデータ(EmployeeRecord)が渡される
  • PlacementTarget.Tag から MainViewModel を取得して EditCommand を実行

2. MainViewModel が行編集を処理する

[RelayCommand(CanExecute = nameof(CanEdit))]
private async Task EditAsync(EmployeeRecord? record)
{
    var target = record ?? SelectedEmployee;
    if (target is null)
    {
        return;
    }

    var editable = (EmployeeRecord)target.Clone();

    var updated = _dialogService.ShowEditDialog(editable);
    if (updated is null)
    {
        return;
    }

    updated.Id = target.Id;

    await _databaseService.UpdateAsync(updated);
    await LoadAsync();
}

3. 編集ダイアログの View(EditEmployeeWindow)

<Window x:Class="MvvmSampleContext.Views.EditEmployeeWindow"
        Title="{Binding Title}"
        Height="260"
        Width="420"
        WindowStartupLocation="CenterOwner">
    <Grid Margin="16">
        <!-- 名前 -->
        <TextBlock Text="名前" />
        <TextBox Grid.Column="1"
                 Text="{Binding Name, UpdateSourceTrigger=PropertyChanged}" />

        <!-- 社員番号 -->
        <TextBlock Grid.Row="1" Text="社員番号" />
        <TextBox Grid.Row="1" Grid.Column="1"
                 Text="{Binding EmployeeNumber, UpdateSourceTrigger=PropertyChanged}" />

        <!-- 部署 -->
        <TextBlock Grid.Row="2" Text="部署" />
        <TextBox Grid.Row="2" Grid.Column="1"
                 Text="{Binding Department, UpdateSourceTrigger=PropertyChanged}" />

        <!-- エラー表示 -->
        <TextBlock Grid.Row="3"
                   Grid.ColumnSpan="2"
                   Foreground="Tomato"
                   Text="{Binding ErrorMessage}" />

        <!-- ボタン -->
        <StackPanel Grid.Row="4" Grid.ColumnSpan="2" Orientation="Horizontal">
            <Button Content="キャンセル" Command="{Binding CancelCommand}" />
            <Button Content="保存" Command="{Binding SaveCommand}" />
        </StackPanel>
    </Grid>
</Window>

4. 編集ダイアログ ViewModel(EditEmployeeViewModel)

[RelayCommand]
private void Save()
{
    if (string.IsNullOrWhiteSpace(Name) || string.IsNullOrWhiteSpace(EmployeeNumber))
    {
        ErrorMessage = "名前と社員番号は必須です。";
        return;
    }

    ErrorMessage = string.Empty;

    var updated = new EmployeeRecord
    {
        Id = _original.Id,
        Name = Name.Trim(),
        EmployeeNumber = EmployeeNumber.Trim(),
        Department = Department.Trim(),
    };

    UpdatedRecord = updated;

    _messenger.Send(new DialogCloseRequestedMessage(
        new DialogCloseRequestedEventArgs(true, updated)));
}

[RelayCommand]
private void Cancel()
{
    UpdatedRecord = null;

    _messenger.Send(new DialogCloseRequestedMessage(
        new DialogCloseRequestedEventArgs(false, null)));
}

5. ダイアログサービス(DialogService)

public EmployeeRecord? ShowEditDialog(EmployeeRecord editable)
{
    var vm = new EditEmployeeViewModel(editable);
    var window = new EditEmployeeWindow
    {
        DataContext = vm,
        Owner = Application.Current.MainWindow,
    };

    var result = window.ShowDialog();

    if (result == true)
    {
        return vm.UpdatedRecord;
    }
    return null;
}

6. ✔ 必須:ダイアログを閉じるための EventArgs & Message

編集ダイアログは、ViewModel → Messenger → Window の順で閉じられます。

6-1. DialogCloseRequestedEventArgs(必須)

using System;
using MvvmSampleContext.Models;

namespace MvvmSampleContext.ViewModels;

public class DialogCloseRequestedEventArgs : EventArgs
{
    public DialogCloseRequestedEventArgs(bool dialogResult, EmployeeRecord? updatedRecord)
    {
        DialogResult = dialogResult;
        UpdatedRecord = updatedRecord;
    }

    public bool DialogResult { get; }

    public EmployeeRecord? UpdatedRecord { get; }
}

6-2. DialogCloseRequestedMessage(必須)

using CommunityToolkit.Mvvm.Messaging.Messages;

namespace MvvmSampleContext.ViewModels;

public sealed class DialogCloseRequestedMessage : ValueChangedMessage<DialogCloseRequestedEventArgs>
{
    public DialogCloseRequestedMessage(DialogCloseRequestedEventArgs value)
        : base(value)
    {
    }
}

6-3. EditEmployeeWindow でメッセージを受け取る

public EditEmployeeWindow()
{
    InitializeComponent();

    WeakReferenceMessenger.Default.Register<EditEmployeeWindow, DialogCloseRequestedMessage>(
        this,
        static (recipient, message) =>
        {
            recipient.OnCloseRequested(message.Value);
        });
}

private void OnCloseRequested(DialogCloseRequestedEventArgs args)
{
    DialogResult = args.DialogResult;
    Close();
}

🔚 全体の流れ(まとめ)

  1. DataGrid 行を右クリック → 編集メニューを押す
  2. EditCommand が実行され、対象 EmployeeRecord が渡される
  3. DialogService が編集ウィンドウを表示
  4. 編集ダイアログで SaveCommand → Messenger.Send()
  5. Window がメッセージを受信し、DialogResult を設定して Close()
  6. DialogService が UpdatedRecord を返す
  7. MainViewModel が DB 更新 → 再読み込み

この記事とコードをそのまま使えば、MVVMらしく整った 「右クリック編集 → ダイアログ → 更新」 が実現できます。


1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?