前回の記事では複数選択する方法を書きました。
takataka430.hatenablog.com
今回の記事ではチェックを入れた要素を削除する機能を実装してみたいと思います。
*この記事内での「編集モード」はチェックボックスと削除ボタンが表示されている状態を意味します。
コード
画面(MainPage.Xaml)
xml version="1.0" encoding="utf-8"
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlnsx="http://schemas.microsoft.com/winfx/2009/xaml"
xmlnslocal="clr-namespace:CheckBox"
xClass="CheckBox.MainPage">
<StackLayout>
<StackLayout Orientation="Horizontal">
<Button xName="DeleteButton"
Text="削除"
Clicked="Delete_clicked"
TextColor="Red"
IsVisible="false"/>
<Button xName="BackButton"
Text="戻る"
Clicked="Back_clicked"
IsVisible="false"/>
<Button xName="EditButton"
Text="編集"
Clicked="Handle_Clicked"
HorizontalOptions="EndAndExpand"/>
</StackLayout>
<ListView xName="list">
<ListViewItemTemplate>
<DataTemplate>
<ViewCell>
<Grid>
<GridColumnDefinitions>
<ColumnDefinition Width="30"/>
<ColumnDefinition Width="*"/>
</GridColumnDefinitions>
<CheckBox IsChecked="{Binding IsChecked}"
IsVisible="{Binding EditMode}"
GridColumn="0"/>
<Label Text="{Binding Name}"
GridColumn="1"
VerticalOptions="Center"/>
</Grid>
</ViewCell>
</DataTemplate>
</ListViewItemTemplate>
</ListView>
</StackLayout>
</ContentPage>
コードビハインド(MainPage.Xaml.cs)
using System;
using System.Collections.ObjectModel;
using System.ComponentModel;
using Xamarin.Forms;
namespace CheckBox
{
[DesignTimeVisible(true)]
public partial class MainPage : ContentPage
{
private bool IsEditMode = false;
private ObservableCollection<Person> person = new ObservableCollection<Person>
{
new Person{Name = "A"},
new Person{Name = "B"},
new Person{Name = "C"},
new Person{Name = "D"},
new Person{Name = "E"},
};
public MainPage()
{
InitializeComponent();
list.ItemsSource = person;
}
void Handle_Clicked(object sender, EventArgs e)
{
ChangeMode();
}
private async void Delete_clicked(object sender, EventArgs e)
{
bool isDelete = await DisplayAlert("削除の確認", "選択した項目を削除してよいですか?", "削除する", "キャンセル");
if (isDelete)
{
var itemNumber = person.Count;
var itemCollection = new ObservableCollection<Person>(person);
for (int i = 0; i < itemNumber; i++)
{
var item = itemCollection[i];
if (item.IsChecked)
{
person.Remove(item);
}
}
}
}
private void Back_clicked(object sender, EventArgs e)
{
ChangeMode();
}
private void ChangeMode()
{
IsEditMode = !IsEditMode;
DeleteButton.IsVisible = IsEditMode;
BackButton.IsVisible = IsEditMode;
EditButton.IsVisible = !IsEditMode;
foreach (var a in person)
{
a.EditMode = IsEditMode;
if (!IsEditMode)
{
a.IsChecked = false;
}
}
}
}
public class Person : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private string name;
public string Name
{
get { return this.name; }
set
{
if (name != value)
{
this.name = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Name)));
}
}
}
private bool isChecked;
public bool IsChecked
{
get { return this.isChecked; }
set
{
if (isChecked != value)
{
this.isChecked = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(IsChecked)));
}
}
}
private bool editMode;
public bool EditMode
{
get { return this.editMode; }
set
{
if (editMode != value)
{
this.editMode = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(EditMode)));
}
}
}
}
}
では動きを見てましょう。
うまく動きました!
実装の失敗例
しかし、削除機能を実装するのに結構苦労しました・・・。何かの参考になるかと思い、失敗した実例をご紹介したいと思います。(Delete_clickedイベントの「チェックが入っている要素を削除 」というコメントがある所の処理です)
失敗1
「繰り返しだからforeachだろう」と思って以下のようなコードにしてみました。
foreach (var item in person)
{
if (item.IsChecked)
{
person.Remove(item);
}
}
これだとエラーになります。調べてみると、どうやらforeach内でコレクションの追加、削除などはできないようです。こういった処理をしたい場合はfor文を使うといいみたいです。
失敗2
for文を利用して以下のように実装してみました。
for (int i = 0; i < person.Count; i++)
{
var item = person[i];
if (item.IsChecked)
{
person.Remove(item);
}
}
これはうまく動くこともありますが、動きが変になります。例えば全ての要素にチェックを入れて削除しようとしても中途半端に残ってしまいます。
これはfor文の中でコレクションの要素の数が変わってしまうためです。person.Countは削除するたびに減少しますし、person[i]は意図した要素が選択されません。これを解決するため、上のコードではpersonと全く同じ別のコレクションを作成しました。
ところでこの方法であってるんでしょうか・・・?あまり自信がないので、修正点あったら教えてください。
以上、一例として削除機能を実装してみました。是非皆さんも使ってみてくださいね。それでは!