C#Xamarin.Formsでスマホ開発#21 ViewCellの共通化のやり方

当サイトではアフィリエイト広告を利用しています。

Xamarin.Formsでスマホアプリ開発

ViewCellの共通化とは?

今回はViewCellの共通化というお話をしたいと思います。ViewCellというのは以前MenuPageを作成したときにListViewを使いましたが,そのなかで1行分のレイアウトを定義していた部分の事です。今後のレクチャーではコントロールに関して色々と解説していく関係で,MenuPageから遷移したページでもMenuPageと同じレイアウトで一覧表示をしたいと思っています。その際に,MenuPageのXamlをコピーしてもうひとつ作ってしまってもいいのですが,コードが重複するのはいろんな場面でよくないことが起きてしまいます。コードが共通化されていれば,アプリケーション全体にわたって共通の見た目になったり,バージョンアップでレイアウトを変更する場合も1か所を変更するだけで,すべてが反映されて,変更漏れを防ぐことができますし,何より工数の削減になります。共通化に関してはこれくらいにして,要するにListViewに表示されるレイアウトを共通化しようというのが今回のViewCellの共通化のお話となります。それではやり方を見ていきましょう。

MenuPageのおさらい

MenuPageではListViewを作成してViewCellでレイアウトを作成しています。

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="XSample.Pages.MenuPage">
    <ContentPage.Content>

        <ListView x:Name="MyListView"
                  HasUnevenRows = "True"
                  ItemTapped="MyListView_ItemTapped">
            <ListView.ItemTemplate>
                <DataTemplate>
                    <ViewCell>
                        <StackLayout Orientation="Horizontal">
                            <Image Source="{Binding ImageName}"/>
                            <StackLayout>
                                <Label Text="{Binding Title}" TextColor="Blue"/>
                                <Label Text="{Binding SubTitle}"/>
                            </StackLayout>
                        </StackLayout>
                    </ViewCell>
                </DataTemplate>
            </ListView.ItemTemplate>
        </ListView>
    </ContentPage.Content>
</ContentPage>

データはMenuDtoをバインドしています。

namespace XSample.Objects
{
    public sealed class MenuDto
    {
        public MenuDto(string title, string subTitle, string imageName)
        {
            Title = title;
            SubTitle = subTitle;
            ImageName = imageName;
        }

        public string Title { get; set; }
        public string SubTitle { get; set; }
        public string ImageName { get; set; }
    }
}

ControlsPageの作成

MenuPageにControlsPageという新しいページを追加して呼び出せるようにします。

using System.Collections.Generic;
using Xamarin.Forms;
using Xamarin.Forms.Xaml;
using XSample.Objects;

namespace XSample.Pages
{
    [XamlCompilation(XamlCompilationOptions.Compile)]
    public partial class MenuPage : ContentPage
    {
        public MenuPage ()
        {
            InitializeComponent ();

            var items = new List<MenuDto>();
            items.Add(new MenuDto("ControlsPage", "コントロールの一覧", "mail.png"));

            MyListView.ItemsSource = items; 
        }

        private void MyListView_ItemTapped(object sender, ItemTappedEventArgs e)
        {
            var item = e.Item as MenuDto;
            if (item.Title == "ControlsPage")
            {
                Navigation.PushAsync(new ControlsPage());
            }
        }
    }
}

新規追加でControlsPageというコンテンツページを作成します。XamlにListViewを作成しましょう。DataTemplateにはMenuPageと同じレイアウトにしたいので,MenuPageからコピーして貼り付けてもいいのですが,それではコードが重複してしまいます。そこでViewCellの部分だけを共通化したいと思います。

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="XSample.Pages.ControlsPage">
    <ContentPage.Content>

        <ListView>
            <ListView.ItemTemplate>
                <DataTemplate>
                    
                </DataTemplate>
            </ListView.ItemTemplate>
        </ListView>
    </ContentPage.Content>
</ContentPage>

ViewCellを新規作成

ViewCellを新規作成します。XSampleプロジェクトに「UserControls」というフォルダーを作成します。

このUserControlsフォルダーの中にViewCellを新規追加します。

「追加」「新しい項目」を選択し,Xamarin.FormsのViewCellを選択し,名前を「MenuViewCell」として追加します。

作成されたMenuViewCellのデフォルトで作成されたStackLayoutの部分をMenuPage.xamlのViewCellの中のStackLayoutの部分で置き換えます。

<?xml version="1.0" encoding="UTF-8"?>
<ViewCell xmlns="http://xamarin.com/schemas/2014/forms" 
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="XSample.UserControls.MenuViewCell">
  <ViewCell.View>
        <StackLayout Orientation="Horizontal">
            <Image Source="{Binding ImageName}"/>
            <StackLayout>
                <Label Text="{Binding Title}"
                                       FontSize="16" 
                                       TextColor="Blue"/>
                <Label Text="{Binding SubTitle}" 
                                       FontSize="14" 
                                       TextColor="Gray"/>
            </StackLayout>
        </StackLayout>
    </ViewCell.View>
</ViewCell>

これでViewCellの共通化は完了です。

ControlsPageの実装つづき

ControlsPageに戻りましょう。DataTemplateの中身が途中になっていましたが,その部分に今作ったMenuViewCellを適応します。やり方は,まずxmlnsで始まる部分にucの行を追加します。「uc」はUserControlsの略でそうしています。ここはお好きな名前にしてください。ネームスペースはUserControlsの階層を指定してください。こうしておくとDataTemplateの中でucを使用でき,MenuViewCellが選択できるようになります。次のようにMenuViewCellを指定すればOKです。あとはListViewの名前を「MyListView」にして一度ビルドしておきましょう。

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:uc="clr-namespace:XSample.UserControls"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="XSample.Pages.ControlsPage">
    <ContentPage.Content>

        <ListView x:Name="MyListView">
            <ListView.ItemTemplate>
                <DataTemplate>
                    <uc:MenuViewCell/>
                </DataTemplate>
            </ListView.ItemTemplate>
        </ListView>
    </ContentPage.Content>
</ContentPage>

ControlsPageのコードビハインド側

ControlsPageのコードビハインド側に行き,MyListViewのItemsSourceを設定します。ここでは適当なMenuDtoを作成し,そのリストをItemsSourceに設定します。MenuPageのListViewと同じ形式にしているため,バインドするクラスもMenuDtoを使用します。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

using Xamarin.Forms;
using Xamarin.Forms.Xaml;
using XSample.Objects;

namespace XSample.Pages
{
    [XamlCompilation(XamlCompilationOptions.Compile)]
    public partial class ControlsPage : ContentPage
    {
        public ControlsPage ()
        {
            InitializeComponent ();

            var items = new List();
            items.Add(new MenuDto("AAA", "BBB", "mail.png"));
            MyListView.ItemsSource = items;
        }
    }
}

実行

実行すると,MenuPageと同じレイアウトでControlsPageも表示されるはずです。