WPF DataGrid コントロールへの未選択状態のセット

DataGrid コントロールの選択行に応じて編集用の項目にデータをセットするような場合、ウィンドウを開いた時には未選択状態になっているのが望ましいですよね。WPF の DataGrid コントロールには UnselectAllCells メソッドがありますが、プロパティにはそのようなものがないことからビューモデルから操作できません。そこで、UnselectAllCells 添付プロパティを作って操作できるようにしました。また、例示アプリケーションには Esc キーの押下で未選択状態にする機構も組み込んでいます(添付プロパティではなく、ビヘイビアとして書き直した記事を投稿しました)。

起動時のウィンドウは次のとおりです。
起動時のウィンドウ

行を選択したときのウィンドウは次のとおりです。
行を選択したときのウィンドウ

行の未選択状態のセットが目的なので、GataGrid のデータ表示はオート表示です。また、データ更新機能は実装していないので「更新」ボタンもありません 😀 データ永続化(DB への読み書き)も行っていません 😛

それではコードです。
プロジェクト名は「DataGridUnselectCell」としています。

プロジェクトで Blend SDK のSystem.Windows.Interactivity クラスを利用するので、参照設定を右クリックして参照を追加します。Visual Studio 2012 では Update 2 以降の更新プログラムが導入されていれば、既にインストールされているので、C:\Program Files\Microsoft SDKs\Expression\Blend\.NETFramework\v4.0\Libraries\System.Windows.Interactivity.dll への参照を追加すれば OK です。Blend SDK が入っていない場合には、かずきさんのブログ記事が詳しいので、ご覧ください。

コードは、最初にビヘイビアから。プロジェクトに「Behaviors」フォルダを作り、Behaviors フォルダ下に DataGridBehaviors クラスを作ります。

using System.Windows;

namespace DataGridUnselectCell.Behaviors
{
    public static class DataGridBehaviors
    {
        /// <summary>
        /// true をセットすると DataGrid のセルの選択を解除します。
        /// </summary>
        public static readonly DependencyProperty UnselectAllCellsProperty = DependencyProperty.RegisterAttached(
            "UnselectAllCells", typeof(bool), typeof(DataGridBehaviors),
            new FrameworkPropertyMetadata
            {
                DefaultValue = false,
                PropertyChangedCallback = UnselectAllCellsChanged,
                BindsTwoWayByDefault = true
            });

        public static bool GetUnselectAllCells(DependencyObject obj)
        {
            return (bool)obj.GetValue(UnselectAllCellsProperty);
        }

        public static void SetUnselectAllCells(DependencyObject obj, bool value)
        {
            obj.SetValue(UnselectAllCellsProperty, value);
        }

        private static void UnselectAllCellsChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
        {
            var dataGrid = sender as System.Windows.Controls.DataGrid;
            if (dataGrid == null) return;
            if (!(bool)e.NewValue) return;

            dataGrid.UnselectAllCells();

            dataGrid.SetValue(UnselectAllCellsProperty, false); // プロパティ値を false に戻す
        }
    }
}

機構としては、UnselectAllCells 添付プロパティへ true がセットされると UnselectAllCells メソッドを実行するようになっています。
具体的には、プロパティ・チェンジのイベントハンドラでイベントソースである DataGrid コントロールの UnselectAllCells メソッドを実行しています。

次に ESC キーの押下で未選択状態にする機構で利用する KeyDownEventTrigger クラスです。

using System;
using System.Windows;
using System.Windows.Input;

namespace DataGridUnselectCell.Behaviors
{
    public class KeyDownEventTrigger : System.Windows.Interactivity.EventTrigger
    {
        public KeyDownEventTrigger() : base("KeyDown") { }

        public static readonly DependencyProperty PressKeyProperty = DependencyProperty.Register(
            "PressKey", typeof(Key), typeof(KeyDownEventTrigger),
            new FrameworkPropertyMetadata { BindsTwoWayByDefault = true });

        public Key PressKey
        {
            get { return (Key)GetValue(PressKeyProperty); }
            set { SetValue(PressKeyProperty, value); }
        }

        protected override void OnEvent(EventArgs eventArgs)
        {
            var e = eventArgs as KeyEventArgs;
            if (e != null)
            {
                PressKey = e.Key;
            }

            base.OnEvent(eventArgs);
        }
    }
}

Blend SDK の EventTrigger を継承します。押されたキーの情報が必要になるので、System.Windows.Input.Key 型の依存関係プロパティ PressKey に押されたキーの情報を格納します(PressKey は双方向モード)。
XAML では InvokeCommandAction(コマンドの実行) と組み合わせて利用します。

次にモデルです。プロジェクトに「Models」フォルダを作り、Models フォルダ下に Sample クラスを作ります。

namespace DataGridUnselectCell.Models
{
    class Sample
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public int Quantity { get; set; }
    }
}

次にビューモデルです。プロジェクトに「ViewModels」フォルダを作り、ViewModels フォルダ下に MainWindowViewModel クラスを作ります。
MVVM パターンなので、お決まりのコマンド処理とプロパティチェンジイベントの発火を提供する ViewModelBase クラスは別記事の ViewModelBase で書いたものを使っています。別プロジェクトにして作成した DLL を参照設定で追加するなり、プロジェクト内にソースを取り入れるなりしてください(次のコードは DLL を参照する前提で書いています)。

using System.Collections.Generic;
using System.Windows.Data;
using System.Windows.Input;

using MakCraft.ViewModels;

using DataGridUnselectCell.Models;

namespace DataGridUnselectCell.ViewModels
{
    class MainWindowViewModel : ViewModelBase
    {
        private List<Sample> _samples;

        public MainWindowViewModel()
        {
            _samples = new List<Sample> {
                new Sample { Id = 1, Name = "Sample1", Quantity = 1100 },
                new Sample { Id = 2, Name = "Sample2", Quantity = 1200 },
                new Sample { Id = 3, Name = "Sample3", Quantity = 1300 },
                new Sample { Id = 4, Name = "Sample4", Quantity = 1400 },
                new Sample { Id = 5, Name = "Sample5", Quantity = 1500 },
                new Sample { Id = 6, Name = "Sample6", Quantity = 1600 },
                new Sample { Id = 7, Name = "Sample7", Quantity = 1700 },
                new Sample { Id = 8, Name = "Sample8", Quantity = 1800 },
                new Sample { Id = 9, Name = "Sample9", Quantity = 1900 },
                new Sample { Id = 10, Name = "Sample10", Quantity = 2000 },
            };

            SampleView = new CollectionViewSource { Source = _samples };
        }

        /// <summary>
        /// DataGrid へデータソースを供給します。
        /// </summary>
        public CollectionViewSource SampleView { get; set; }

        private Sample _selectedItem;
        /// <summary>
        /// DataGrid で選択されたアイテムです。
        /// </summary>
        public Sample SelectedItem
        {
            get { return _selectedItem; }
            set
            {
                _selectedItem = value;
                if (_selectedItem == null)
                {
                    Name = "";
                    Quantity = 0;
                    return;
                }

                Name = _selectedItem.Name;
                Quantity = _selectedItem.Quantity;
            }
        }

        private bool _unselectAllCells;
        /// <summary>
        /// true がセットされると DataGrid が未選択状態になります。
        /// </summary>
        public bool UnselectAllCells
        {
            get { return _unselectAllCells; }
            set
            {
                _unselectAllCells = value;
                base.RaisePropertyChanged(() => UnselectAllCells);
            }
        }

        private string _name;
        public string Name
        {
            get { return _name; }
            set
            {
                _name = value;
                base.RaisePropertyChanged(() => Name);
            }
        }

        private int _quantity;
        public int Quantity
        {
            get { return _quantity; }
            set
            {
                _quantity = value;
                base.RaisePropertyChanged(() => Quantity);
            }
        }

        private Key _pressKey;
        /// <summary>
        /// ウィンドウ上で押下されたキー情報がセットされます。
        /// </summary>
        public Key PressKey
        {
            get { return _pressKey; }
            set { _pressKey = value; }
        }

        private void dataGridUnselectExecute()
        {
            UnselectAllCells = true;
        }
        private ICommand _dataGridUnselectCommand;
        /// <summary>
        /// DataGrid を未選択状態にするコマンドです。
        /// </summary>
        public ICommand DataGridUnselectCommand
        {
            get
            {
                if (_dataGridUnselectCommand == null)
                    _dataGridUnselectCommand = new RelayCommand(
                        param => dataGridUnselectExecute()
                        );
                return _dataGridUnselectCommand;
            }
        }

        private void keyDownActionExecute()
        {
            if (PressKey == Key.Escape)
                UnselectAllCells = true;
        }
        private ICommand _keyDownActionCommand;
        /// <summary>
        /// KeyDownEvent で実行するコマンドです。
        /// </summary>
        public ICommand KeyDownActionCommand
        {
            get
            {
                if (_keyDownActionCommand == null)
                    _keyDownActionCommand = new RelayCommand(
                        param => keyDownActionExecute()
                        );
                return _keyDownActionCommand;
            }
        }
    }
}

最後に MainWindow.xaml です。

<Window x:Class="DataGridUnselectCell.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
        xmlns:b="clr-namespace:DataGridUnselectCell.Behaviors"
        xmlns:vm="clr-namespace:DataGridUnselectCell.ViewModels"
        Title="MainWindow" SizeToContent="Height" Width="525">
    <Window.DataContext>
        <vm:MainWindowViewModel />
    </Window.DataContext>
    
    <i:Interaction.Triggers>
        <i:EventTrigger EventName="Loaded">
            <i:InvokeCommandAction Command="{Binding DataGridUnselectCommand}" />
        </i:EventTrigger>
        <b:KeyDownEventTrigger PressKey="{Binding PressKey}">
            <i:InvokeCommandAction Command="{Binding KeyDownActionCommand}" />
        </b:KeyDownEventTrigger>
    </i:Interaction.Triggers>
    
    <StackPanel>
        <TextBlock
            Text="DatGrid コントロールの選択行の未選択状態セットのテスト"
            FontSize="18" Margin="10 15" HorizontalAlignment="Center" />
        
        <DataGrid
            ItemsSource="{Binding SampleView.View}" MaxHeight="200" Margin="10" IsReadOnly="True"
            SelectedItem="{Binding SelectedItem}"
            b:DataGridBehaviors.UnselectAllCells="{Binding UnselectAllCells}" />
        
        <StackPanel Orientation="Horizontal" Margin="10">
            <Label Content="名称:" />
            <TextBox Text="{Binding Name}" MinWidth="100" />
            <Label Content="数量:" Margin="6 0" />
            <TextBox Text="{Binding Quantity}" MinWidth="80" HorizontalContentAlignment="Right" />
        </StackPanel>
    </StackPanel>
</Window>

Window の Loaded イベントをトリガーに DataGridUnselectCommand コマンドを実行するようにすることで、ウィンドウを開いた時に DataGrid のセルが未選択になるようにしています。また、KeyDownEventTrigger を使って Window の KeyDown イベントをトリガーに KeyDownActionCommand コマンドを実行するようにすることで、Esc キー押下時に DataGrid のセルが未選択になるようにしています。

以上でプログラムが動きます。


WPF DataGrid コントロールへの未選択状態のセット」への1件のフィードバック

コメントを残す

メールアドレスが公開されることはありません。