LoginSignup
1
2

More than 3 years have passed since last update.

【Xamarin.Forms】コンテキストメニューでListViewから行を削除する

Posted at

はじめに

Xamarin.FormsのListViewで行のコンテキストメニューからその行を削除します。
コードビハインドは書かず、MVVMで実現します。

最初に言い訳です。
この記事を書いてみて、Xamarin.Fromsの基礎がわかったいないことを改めて自覚。。。
https://docs.microsoft.com/ja-jp/xamarin/xamarin-forms/app-fundamentals/
曖昧な表現がありますので、予めご了承ください。

したいこと(できたこと)

ListViewContextMenuDelete.gif

環境

Visual Studio Community 2019 for Mac
Xamarin.Forms 4.2.0
Prism.Unity.Forms 7.2.0(オプション)

ソース

GitHubにソースを上げています。
https://github.com/ats-y/TryXamarinListViewContextMenu

以下、上記ソースを抜粋して説明します。

説明

ViewとViewModelにわけて説明します。

View

MainPage.xaml
<?xml version="1.0" encoding="utf-8"?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:d="http://xamarin.com/schemas/2014/forms/design"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
             mc:Ignorable="d"
             x:Class="TryContextMenu.MainPage"
             x:Name="MainPageContentPage">
    <StackLayout>

        <!-- 社員一覧 -->
        <ListView x:Name="EmployeeListView"
                  ItemsSource="{Binding EmployeeList}"
                  HasUnevenRows="True">

            <!-- 社員行 -->
            <ListView.ItemTemplate>
                <DataTemplate>
                    <ViewCell>

                        <!-- コンテキストメニューの定義-->
                        <ViewCell.ContextActions>

                            <!--
                            削除コンテキストメニュー
                            Command属性:タップするとMainPageContentPageのDeleteCommandを呼び出す。
                            CommandParameter属性:Command属性で指定したコマンドに渡すデータを指定。-->
                            <MenuItem Text="削除"
                                      IsDestructive="True"
                                      Command="{Binding Source={x:Reference MainPageContentPage}, Path=BindingContext.DeleteCommand}"
                                      CommandParameter="{Binding .}" />

                        </ViewCell.ContextActions>

                        <!-- レイアウト定義 -->
                        <StackLayout Padding="10,100,0,100"
                                     BackgroundColor="{Binding BackColor}"
                                     HorizontalOptions="FillAndExpand"
                                     Orientation="Horizontal">

                            <StackLayout Orientation="Vertical">
                                <Label Text="名前:" />
                                <Label Text="{Binding FullName}" />
                            </StackLayout>

                        </StackLayout>

                    </ViewCell>
                </DataTemplate>
            </ListView.ItemTemplate>

        </ListView>

    </StackLayout>
</ContentPage>

Viewのポイント

コードビハインド(MainPage.xaml.cs)

今回やりたいことを実現するにあたって、コードビハインドには特に何も書いていません。

ルートContentPageの名前付け

ルートのContentPage要素には「MainPageContentPage」という名前をつけています。

   <ContentPage (略)
    x:Name="MainPageContentPage">
削除コンテキストの定義

MenuItem要素で削除コンテキストメニューを定義します。

<!--
削除コンテキストメニュー
Command属性:タップするとMainPageContentPageのDeleteCommandを呼び出す。
CommandParameter属性:Command属性で指定したコマンドに渡すデータを指定。-->
<MenuItem Text="削除"
            IsDestructive="True"
            Command="{Binding Source={x:Reference MainPageContentPage}, Path=BindingContext.DeleteCommand}"
            CommandParameter="{Binding .}" />

Command属性、CommandParameter属性でコンテキストメニューをタップした時のコマンドとコマンドに渡すパラメータを定義しています。

Command属性で指定した値によって、
MainPageContentPageに紐づいているViewModelのDeleteCommandを呼び出してね」
と定義しています。
「MainPageContentPage」はルートのContentPage要素につけた名前です。

CommandParameter属性はCommand属性で指定したコマンドを呼び出すときに渡す値を定義します。
{Binding .}とすることでタップした行データ、つまりListView要素のItemSource属性に紐づいているListのタップした行の要素が渡されました。

ViewModel

MainPageViewModel.cs
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Windows.Input;
using TryContextMenu.Models;
using Xamarin.Forms;

namespace TryContextMenu.ViewModels
{
    public class MainPageViewModel : ViewModelBase
    {
        /// <summary>
        /// 社員リスト
        /// </summary>
        private ObservableCollection<Employee> _employeeList;

        /// <summary>
        /// 社員リストプロパティ
        /// </summary>
        public ObservableCollection<Employee> EmployeeList
        {
            get { return _employeeList; }
            set { SetProperty(ref _employeeList, value); }
        }

        /// <summary>
        /// コンストラクタ
        /// </summary>
        public MainPageViewModel()
        {
            // 社員リストを生成する。
            _employeeList = new ObservableCollection<Employee>
            {
                new Employee { FullName = "社員 太郎" },
                new Employee { FullName = "社員 二郎" },
                new Employee { FullName = "社員 三郎" },
                new Employee { FullName = "社員 四郎" },
                new Employee { FullName = "社員 五郎" },
            };
        }

        /// <summary>
        /// 社員リストから引数で指定した社員を削除する。
        /// </summary>
        public ICommand DeleteCommand =>
            new Command<Employee>((arg) =>
        {
            Debug.WriteLine("MainPage.DeleteCommand()");

            EmployeeList.Remove(arg);
        });
    }
}

ViewModelのポイント

社員リストおよびプロパティ
MainPageViewクラスの社員リストおよびプロパティの定義部分
/// <summary>
/// 社員リスト
/// </summary>
private ObservableCollection<Employee> _employeeList;

/// <summary>
/// 社員リストプロパティ
/// </summary>
public ObservableCollection<Employee> EmployeeList
{
    get { return _employeeList; }
    set { SetProperty(ref _employeeList, value); }
}

EmployeeListプロパティは社員リストです。
MainPageViewModelクラスに対するView「MainPage.xaml」のListViewのItemSource属性で指定したバインディングItemsSource="{Binding EmployeeList}"と同じ名前にすることで、社員ListViewのリストデータとMainPageViewModel.EmployeeListが紐づきます。
これにより、MainPageViewModel.EmployeeListに入れたデータがListViewに表示されます。

今回はMainPageViewクラスのコンストラクタでEmployeeListプロパティに社員リストに社員を追加していて、実行すると追加した社員がListViewに表示されます。

MainPageViewクラスのコンストラクタ
/// <summary>
/// コンストラクタ
/// </summary>
public MainPageViewModel()
{
    // 社員リストを生成する。
    _employeeList = new ObservableCollection<Employee>
    {
        new Employee { FullName = "社員 太郎" },
        new Employee { FullName = "社員 二郎" },
        new Employee { FullName = "社員 三郎" },
        new Employee { FullName = "社員 四郎" },
        new Employee { FullName = "社員 五郎" },
    };
}

また、ObservableCollection<Employee>型にすることで、社員リストプロパティの内容が変わるとViewに反映されるようになります。

DeleteCommandコマンド
MainPageViewクラスのDeleteCommandコマンド定義部分
/// <summary>
/// 社員リストから引数で指定した社員を削除する。
/// </summary>
public ICommand DeleteCommand =>
    new Command<Employee>((arg) =>
{
    Debug.WriteLine("MainPage.DeleteCommand()");

    EmployeeList.Remove(arg);
});

削除コンテキストのMenuItem要素のCommand属性で指定したPathと同じ名前でICommand型で定義します。そして、CommandParameter属性で渡すことにした型を型引数にしています。

引数argには、削除コンテキストをタップした行のEmployeeオブジェクトが渡されます。
EmployeeList.Remove(arg);で社員リストプロパティから、argで渡されたEmployeeオブジェクトを削除します。
社員リストはObservableCollection<>型なので、これで社員ListViewから当該社員行が削除されます。

1
2
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
2