Quantcast
Channel: C#タグが付けられた新着記事 - Qiita
Viewing all articles
Browse latest Browse all 9707

DataGridやListBox内でクリックされたら自身の行を削除するButton

$
0
0

概要

DataGridやListBoxで複数のアイテムを表示しているときに、アイテム自身に削除ボタンをつけたいことがあります。

image.png
👇 "JIRO"を削除
image.png

MVVMでやっている場合は、アイテムごとViewModelに削除Commandを用意して、、、となります。
なしの場合はコードビハインドで、削除ボタンが押されたItemを検索して、、、となります。
どちらにしても、手間ですし他のコードで使い回せません。

そこで、添付プロパティを使って、Buttonに自身が所属しているItemsControlから削除する機能を追加します。

Viewだけで完結しているので、ViewModel側には追加作業は必要ありません。
MVVMを使用していない場合も、コードビハインドから呼び出して使えます。
ItemsControlを継承しているコントロールなら使えるので、ItemsControl、DataGrid、ListBox、ListView、ComboBoxでも使えます。

方法

添付プロパティ

まず、指定されたオブジェクトを含む行を親のItemsControlから削除するメソッドを定義します。
コードビハインドを使用する場合は、このメソッドをButtonのクリックイベントから呼び出しても使えます。

/// <summary>/// 指定されたオブジェクトを含む行を親のItemsControlから削除する/// </summary>publicstaticvoidRemoveItemFromParent(DependencyObjectelementInItem){DependencyObjectparent=elementInItem;varparentTree=newList<DependencyObject>{parent};//指定されたオブジェクトのVisualTree上の親を順番に探索し、ItemsControlを探す。//ただし、DataGridは中間にいるDataGridCellsPresenterは無視するwhile(parent!=null&&!(parentisItemsControl)||parentisDataGridCellsPresenter){parent=VisualTreeHelper.GetParent(parent);parentTree.Add(parent);}if(!(parentisItemsControlitemsControl))return;//ItemsControlの行にあたるオブジェクトを探索履歴の後ろから検索varitem=parentTree.LastOrDefault(x=>itemsControl.IsItemItsOwnContainer(x));int?removeIndex=itemsControl.ItemContainerGenerator?.IndexFromContainer(item);if(removeIndex==null||removeIndex<0)return;//Bindingしていた場合はItemsSource、違うならItemsから削除する((itemsControl.ItemsSourceasIList)??itemsControl.Items)?.RemoveAt(removeIndex.Value);}

そして、Buttonのクリックイベントでこのメソッドを呼ぶ添付プロパティを用意します。

#region RemoveItem添付プロパティ
publicstaticboolGetRemoveItem(DependencyObjectobj)=>(bool)obj.GetValue(RemoveItemProperty);publicstaticvoidSetRemoveItem(DependencyObjectobj,boolvalue)=>obj.SetValue(RemoveItemProperty,value);publicstaticreadonlyDependencyPropertyRemoveItemProperty=DependencyProperty.RegisterAttached("RemoveItem",typeof(bool),typeof(MyExt),newPropertyMetadata(default(bool),OnRemoveItemChanged));privatestaticvoidOnRemoveItemChanged(DependencyObjectd,DependencyPropertyChangedEventArgse){if(!(disButtonBasebutton))return;if(!(e.NewValueisboolb))return;if(b)button.Click+=RemoveItem;elsebutton.Click-=RemoveItem;}privatestaticvoidRemoveItem(objectsender,RoutedEventArgse)=>RemoveItemFromParent(senderasDependencyObject);#endregion

使用方法

ViewModel側にこんなプロパティがあるとします。

publicObservableCollection<string>Names{get;}=newObservableCollection<string>(new[]{"TARO","JIRO","SABRO"});

それに対して、ViewではDataGridで上記のNamesプロパティにBindingしています。

DataGrid(MVVM)
<DataGridAutoGenerateColumns="False"ItemsSource="{Binding Names}"><DataGrid.Columns><DataGridTextColumnBinding="{Binding}"/><DataGridTemplateColumn><DataGridTemplateColumn.CellTemplate><DataTemplate><StackPanel><Buttonlocal:MyExt.RemoveItem="True"Content="✖"/></StackPanel></DataTemplate></DataGridTemplateColumn.CellTemplate></DataGridTemplateColumn></DataGrid.Columns></DataGrid>

ListBoxの場合は以下です。

ListBox(MVVM)
<ListBoxItemsSource="{Binding Names}"><ListBox.ItemTemplate><DataTemplate><StackPanelOrientation="Horizontal"><TextBlockWidth="100"Text="{Binding}"/><Buttonlocal:MyExt.RemoveItem="True"Content="✖"/></StackPanel></DataTemplate></ListBox.ItemTemplate></ListBox>

MVVMを使用しない場合は、以下のようになります

DataGrid(Plane)
<DataGrid><DataGrid.Columns><DataGridTextColumnBinding="{Binding Text}"/><DataGridTemplateColumn><DataGridTemplateColumn.CellTemplate><DataTemplate><ButtonClick="XButton_Click"Content="X"/><!--<Button Content="X" local:MyExt.RemoveItem="True">--></DataTemplate></DataGridTemplateColumn.CellTemplate></DataGridTemplateColumn></DataGrid.Columns><TextBlockText="AAA"/><TextBlockText="BBB"/><TextBlockText="CCC"/></DataGrid>

添付プロパティを使用せず、クリックイベントのコードビハインドから呼び出す場合は、上記Xamlのコメント部分を解除した上で、コードビハインドに以下を追加します。

privatevoidXButton_Click(objectsender,RoutedEventArgse){if(senderisDependencyObjectdObj)MyExt.RemoveItemFromParent(dObj);}

注意点

DataGridなどで並び替えしていると、正しく動きません。削除するときのIndex算出は並び替え後のIndexですが、削除時はデフォルトの並びでのIndexを指定する必要があるためです。

View側での変更をViewModelに伝えるため、ItemsSourceのBindingはTwo-Wayにする必要があります。
つまり、ReadOnlyなコレクションがBindingされていた場合は使えません。

環境

VisualStudio2019
.NET Core 3.1
C#8


Viewing all articles
Browse latest Browse all 9707

Trending Articles