MAUI で ListView を使って撃沈してみるテスト(Windowsのほうは大丈夫)

MAUI を始める、というか調べることにしたので、なにはともあれ Web API を呼び出して、なにかリスト表示をしようと作ってみる。だが、作ってみる途中でどうも動かないので、備忘録に記録しておく。

実は MAUI のドキュメント自体は極めてすくなくて、.NET Multi-Platform App UI documentation – .NET MAUI | Microsoft Docs 程度しかない。なんというか、いまのところ何もない状態に近いので、肝心の画面をどう作っていくのか?に関しては全くわからない。でも、まったくわからないながらも、もともと Xamarin.Forms からの移行になるのだから、Xamarin.Forms の XAML 形式をコピペすればいけるだろう。ということで、最初は Visual Studio 2019 を使って Xamarin.Forms で作ってみる。

<?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="XamarinWebApi.MainPage">

    <ScrollView>
        <Grid RowSpacing="25">
            <Grid.RowDefinitions>
                <RowDefinition Height="auto" />
                <RowDefinition Height="auto" />
                <RowDefinition Height="*" />
            </Grid.RowDefinitions>

            <Label Text="web api test"
                Grid.Row="0"
                HorizontalOptions="CenterAndExpand" />

            <Button Text="call group"
                    Grid.Row="1"
                    HorizontalOptions="CenterAndExpand"
                    Clicked="clickGroup"/>

            <ListView x:Name="lv" Grid.Row="2">
                <ListView.ItemTemplate>
                    <DataTemplate>
                        <TextCell Text="{Binding Name}" />
                    </DataTemplate>
                </ListView.ItemTemplate>
            </ListView>
        </Grid>
    </ScrollView>
</ContentPage>

たぶん、こんな風に ListView を使ったはず。ボタンをクリックしたときに Web API を呼び出すのはこんな感じ。

private async void clickGroup(object sender, EventArgs e)
{
    var cl = new HttpClient();
    var url = new Uri("http://192.168.1.28:5000/api/areagroup");
    var json = await cl.GetStringAsync(url);
    var js = new JsonSerializer();
    var items = JsonConvert.DeserializeObject<List<AreaGroup>>(json);
    this.lv.ItemsSource = items;
}

Web API 自体はローカルな dotnet で動かしているので、IP アドレスはローカルコンピュータのものになっている。Android エミュレータから呼び出すことになるので localhost ではなく、IP アドレスになっている。

さて、実はこれを動かすと次のようなエラーになる。

System.Net.WebException: 'Cleartext HTTP traffic to 192.168.1.28 not permitted'

とあるバージョンから Android は平文(HTTP)を通すことができないのである。

ああ、そうそう、ということで AndroidManifest.xml ファイルに android:usesCleartextTraffic=”true” の記述をいれることになる。

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
	<uses-sdk android:minSdkVersion="30" android:targetSdkVersion="30" />
	<application 
		android:allowBackup="true" 
		android:icon="@mipmap/appicon" 
		android:roundIcon="@mipmap/appicon_round" 
		android:supportsRtl="true"
		android:usesCleartextTraffic="true"
		>
	</application>
	<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
	<uses-permission android:name="android.permission.INTERNET"/>
</manifest>

これでデータが取得できるようになる。

デバッグ用の場合は平文の HTTP を通すのが手っ取り早いのだけど、一応 HTTPS のほうも通しておきたいと思って、書き換えてためしてみると。

var url = new Uri("http://192.168.1.28:5000/api/areagroup");
今度は証明書のエラーがでる。これは、dotnet コマンドが提供している証明書が通せないエラーなので、当然といえば当然なのだけど。ローカルなコンピュータに証明書を入れることはできないので、無視してほしい。

ということで、次のようにコードを書き換える。

private async void clickGroup(object sender, EventArgs e)
{
    var httpHandler = new HttpClientHandler { ServerCertificateCustomValidationCallback = (o, cert, chain, errors) => true };
    var cl = new HttpClient(httpHandler);
    var url = new Uri("https://192.168.1.28:5001/api/areagroup");
    var json = await cl.GetStringAsync(url);
    var js = new JsonSerializer();
    var items = JsonConvert.DeserializeObject<List<AreaGroup>>(json);
    this.lv.ItemsSource = items;
}

HttpClient のインスタンスを作成するときに、証明書のエラーをスルーするようにオプションを入れる。これも以前みたようなことがあった(また結構さがしたけど)。

これで無事通るようになる。

さて、Xamarin.Forms で動くようになったので、これを Visual Studio 2022 の MAUI のほうにコピーする。

XAML のほうは、そのままコピーで ok.

コードのほうはこんな感じ。

using System;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using Microsoft.Maui.Controls;
using System.Net.Http;
using System.Net.Http.Json;
using System.Collections.Generic;

namespace HelloWebApi
{
    public partial class MainPage : ContentPage
    {
        public MainPage()
		{
			InitializeComponent();

		}
        private async void clickGroup(object sender, EventArgs e)
        {
            var httpHandler = new HttpClientHandler { ServerCertificateCustomValidationCallback = (o, cert, chain, errors) => true };
            var cl = new HttpClient(httpHandler);
            var url = new Uri("https://192.168.1.28:5001/api/areagroup");
            var items  = await cl.GetFromJsonAsync<List<AreaGroup>>(url);
            this.lv.ItemsSource = items;
	    }
	}

JSON のパースは GetFromJsonAsync が使えるようになったので楽ですね。とか思ったら、ServerCertificateCustomValidationCallback なところでエラーが発生します。

どうやら、.NET 6(かな?)のほうでは現状 ServerCertificateCustomValidationCallback がサポートされていない模様。どうも次の preview のバージョンでこれは入る模様です。

仕方がないので、再び AndroidManifest.xml に android:usesCleartextTraffic=”true” を追加して HTTP で通すことにする。

これを Android エミュレータで動かすと。

どうやら、HttpClient でデータは返ってきているのだが、ListView が動いていない模様。

ちなみに、Windowsアプリのほう(実質 UWP)は動いてます。

懐かしの UWP な感じがするのだけど、これ、レイアウトが MainPage.xaml で完全に共有になっているので、マージンの調節とかが大変そう。Android と iOS 間で十分面倒なのだけど。

ひとまず、ここまで動いたという記録。

カテゴリー: 開発 パーマリンク