スポンサーサイト

上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。
当サイトは基本をすっ飛ばしてます。基本文法等は、@ITをどうぞ
カテゴリー: スポンサー広告

DaraGridのイベントからセルの情報をVMで活用する。(弐)

壱でCellClieckedとCellDoubleClickedを書いたので、CellChangedとSelectionChanged用の添付プロパティを実装する。
【注意】
なお、前の2つもふくめ、この記事における実装は、終了時の処理(ハンドラ削除等)を省いてある。このまま実装すると、ほぼ確実にメモリリークになるので、Window終了時に登録したイベントからハンドラメソッドを削除する処理を入れる必要がある。
添付プロパティ:CellChanged用コマンド
namespace TawamureDays {

/// <summary>
/// DataGridを拡張するためのクラス
/// </summary>
public static class DataGridExtender {

/// <summary>
/// DataGridカレントセル移動イベント用コマンドを取得します。
/// </summary>
/// <param name="obj">イベント発生元</param>
/// <returns>DataGridカレントセル移動イベント用コマンド</returns>
public static ICommand GetCellChangedCommand(DependencyObject obj) {
return (ICommand)obj.GetValue(CellChangedCommandProperty);
}

/// <summary>
/// DataGridカレントセル移動イベント用コマンドを設定します。
/// </summary>
/// <param name="obj">対象オブジェクト</param>
/// <param name="value">DataGridカレントセル移動イベント用コマンド</param>
public static void SetCellChangedCommand(DependencyObject obj, ICommand value) {
obj.SetValue(CellChangedCommandProperty, value);
}

/// <summary>DataGridカレントセル移動イベント用コマンド</summary>
public static readonly DependencyProperty CellChangedCommandProperty =
DependencyProperty.RegisterAttached("CellChangedCommand", typeof(ICommand),
typeof(DataGridExtender),
new PropertyMetadata(null,
OnCellChangedCommandPropertyChanged));

/// <summary>
/// CellChangedCommandプロパティ変更イベントハンドラ
/// </summary>
/// <param name="dpObj">変更発生元オブジェクト</param>
/// <param name="dpArgs">イベント引数</param>
private static void OnCellChangedCommandPropertyChanged(DependencyObject dpObj,
DependencyPropertyChangedEventArgs dpArgs) {
DataGrid dataGrid = dpObj as DataGrid;

if (dataGrid != null) {
//DataGridのカレントセル移動イベントをキャプチャします。
dataGrid.CurrentCellChanged -=
new EventHandler<EventArgs>(DgGrid_CurrentCellChanged);

if (dpArgs.NewValue != null) {
dataGrid.CurrentCellChanged +=
new EventHandler<EventArgs>(DgGrid_CurrentCellChanged);
//HACK:終了時に↑のイベントからメソッドを削除しよう。
}
}
}

/// <summary>
/// DataGrid カレントセル変更イベントハンドラ
/// </summary>
/// <remarks>CellChangedCommandプロパティに関連します。</remarks>
/// <param name="sender">イベント発生元</param>
/// <param name="e">イベント引数</param>
private static void DgGrid_CurrentCellChanged(object sender, EventArgs e) {

DataGrid srcGrid = sender as DataGrid;

if (srcGrid != null && srcGrid.CurrentCell != null) {
//実行コマンドを取得します。
ICommand command = DataGridExtender.GetCellChangedCommand(srcGrid) as ICommand;

if (command != null) {

int displayIndex = -1;
string boundProperty = string.Empty;

//カレントセルが属する列から
//・表示インデックス(DisplayIndex)
//・バインドされているデータとプロパティ名
//を取得します。
if (srcGrid.CurrentCell.Column != null) {
displayIndex = srcGrid.CurrentCell.Column.DisplayIndex;
boundProperty = srcGrid.CurrentCell.Column.GetBindingPath();
}

var boundData = srcGrid.CurrentItem;

if ((boundData == null || boundData == DependencyProperty.UnsetValue) &&
srcGrid.Items.Count > 0) {
//DataGridからフォーカスが外れたと判定します。
return;
}

int rowIndex = -1;

if (boundData != null) {
//行を特定して、行インデックスを取得します。
var gridRow = srcGrid.ItemContainerGenerator.ContainerFromItem(boundData);

if (gridRow != null) {
rowIndex = srcGrid.ItemContainerGenerator.
IndexFromContainer(gridRow);
}
}

//パラメータ生成
//Item1:行インデックス
//Item2:列の表示インデックス
//Item3:バインドされているアイテム
//Item4:列にバインドされているプロパティ名
//Item5:行単位で選択されるかどうか?
var parameter = Tuple.Create(rowIndex, displayIndex, boundData, boundProperty,
srcGrid.SelectionUnit == DataGridSelectionUnit.FullRow);

if (command.CanExecute(parameter)) {
command.Execute(parameter);
}
}
}
}
}
}

DataGridのCurrentCellプロパティは、前にも書いたけど、フォーカスが外れるとnullになる。当然CellChangedイベントも発生する。なので、nullチェックは必須である(コマンドも呼び出さない)。コマンドに渡すパラメータは、CellClickedと同じものになるように実装している。CurrentCellプロパティを元に情報を引き出すので、少しロジックが異なる。こっちの場合、行の特定がややめんどくさい。

添付プロパティ:SelectionChanged用コマンド
このコマンドだけは、他の3つと同じというわけにはいかなかった…。
namespace TawamureDays {

/// <summary>
/// DataGridを拡張するためのクラス
/// </summary>
public static class DataGridExtender {

/// <summary>
/// DataGrid SelctionChangedイベント用コマンドを取得します。
/// </summary>
/// <param name="obj">対象オブジェクト(DataGrid)</param>
/// <returns>DataGrid SelctionChangedイベント用コマンド</returns>
public static ICommand GetSelectionChangedCommand(DependencyObject obj) {
return (ICommand)obj.GetValue(SelectionChangedCommandProperty);
}

/// <summary>
/// DataGrid SelctionChangedイベント用コマンドを設定します。
/// </summary>
/// <param name="obj">対象オブジェクト(DataGrid)</param>
/// <param name="value">DataGrid SelctionChangedイベント用コマンド</param>
public static void SetSelectionChangedCommand(DependencyObject obj, ICommand value) {
obj.SetValue(SelectionChangedCommandProperty, value);
}

/// <summary>DataGrid SelctionChangedイベント用コマンド</summary>
public static readonly DependencyProperty SelectionChangedCommandProperty =
DependencyProperty.RegisterAttached("SelectionChangedCommand",
typeof(ICommand),
typeof(DataGridExtender),
new PropertyMetadata(new PropertyChangedCallback(
OnSelectionChangedCommandPropertyChanged)));

/// <summary>
/// SelectionChangedCommandプロパティ変更イベントハンドラ
/// </summary>
/// <param name="dpObj">発生元オブジェクト</param>
/// <param name="dpArgs">イベント引数</param>
private static void OnSelectionChangedCommandPropertyChanged(DependencyObject dpObj,
DependencyPropertyChangedEventArgs dpArgs) {
DataGrid dataGrid = dpObj as DataGrid;

if (dataGrid != null) {
dataGrid.SelectionChanged -=
new SelectionChangedEventHandler(DgGrid_SelectionChanged);

if (dpArgs.NewValue != null) {
dataGrid.SelectionChanged +=
new SelectionChangedEventHandler(DgGrid_SelectionChanged);
//HACK:終了時に↑のイベントからメソッドを削除しよう。
}
}
}

/// <summary>
/// DataGrid 選択項目変更イベントハンドラ
/// </summary>
/// <remarks>SelectionChangedCommandプロパティに関連します。</remarks>
/// <param name="sender">イベント発生元</param>
/// <param name="e">イベント引数</param>
private static void DgGrid_SelectionChanged(object sender, SelectionChangedEventArgs e) {

var dataGrid = sender as DataGrid;

if (dataGrid == null || !dataGrid.IsLoaded) {
//ロード前には反応させない。
return;
}

if (dataGrid != null) {
//コマンドを取得します。
ICommand command = DataGridExtender.GetSelectionChangedCommand(dataGrid);

//パラメータは、タプル
//Item1:選択に追加されたアイテム
//Item2:選択から除外されたアイテム
//Item3:上下キー移動によるイベントかどうか
var parameter = Tuple.Create(e.AddedItems, e.RemovedItems,
Keyboard.IsKeyDown(Key.Up) || Keyboard.IsKeyDown(Key.Down));

//コマンドを実行します。
if (command != null) {
if (command.CanExecute(parameter)) {
command.Execute(parameter);
}
}
}

return;
}
}
}
選択の切り替えなので、コマンドに渡すパラメータは他と変えている。
Item1:選択に追加されたアイテム
Item2:選択から除外されたアイテム
Item3:上下キー移動によるイベントかどうか
Item3は、キーによるものかマウスによるものかを識別したかった為のプロパティ。
SelectionChangedは、セル選択ではなく、項目選択の変更なので、SelectionUnitプロパティが、「Cell」の時は発生しない。

使い方:
全部書くとアレなので、1つだけ実装例を。
○VM側にコマンド用プロパティを用意する。
/// <summary>セルクリックイベント用コマンド</summary>
private RelayCommand cellClickedCommand_;

/// <summary>
/// セルクリックイベント用コマンドを取得します。
/// </summary>
public ICommand CellClickedCommand {
get {return cellClickedCommand_;}
}
VMインスタンス生成時にコマンドのインスタンスを生成しておく。
cellClickedCommand_ = new RelayCommand(ExecuteCellClicked);

終了時には、Clearメソッドを呼んでおく。でないと、リークしちゃう。
if (cellClickedCommand_ != null) {
cellClickedCommand_.Clear();
cellClickedCommand_ = null;
}

○XAML側で設定する。設定しないと動かないよ。
<DataGrid Name="PeoplesList"
AutoGenerateColumns="False"
local:DataGridExtender.CellClickedCommand="{Binding CellClickedCommand}"
(中略)
>
<DataGrid.Columns>
(中略)
<DataGrid.Columns>
</DataGrid>
だいたいこんな感じかな。動かない時は、デバッグの時にVSの「出力」を見るのが良いかな。
バインディングエラーとかがあれば、表示されているし。
スポンサーサイト
当サイトは基本をすっ飛ばしてます。基本文法等は、@ITをどうぞ
カテゴリー: WPF4 | コメント: 0 | トラックバック: 0


この記事へのコメント

コメントの投稿

非公開コメント


サイドバー背後固定表示サンプル

当ブログに書かれたソースコードは流用自由です。

バグ、スペルミス等はありうる事です。

ご利用の際は自己責任でお願いしますm(_ _)m

上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。