個人的なメモ

Tomohiro Suzuki @hiro128_777 のブログです。Xamarin に関する事を中心に書いています。 Microsoft MVP for Development Technologies 2017- 本ブログと所属組織の公式見解は関係ございません。

Silverlight は不滅でした... OpenSilver を試してみました。

Silverlightプラグイン不要のオープンソース再実装 OpneSilver のベータ版がリリースされたというニュースを見ましたので、昔 Silverlight を使っていた身としては「触ってみなければ」と思い、さっそく試してみました。
opensilver.net

Silverlightは、すでに Chrome、Edge、Safari などの最新のブラウザでは、プラグインもインストールできず、動作もしませんが、OpneSilver は見た目は酷似していますが中身は HTML5、CSS3、WebAssembly などの現在の Web 標準のテクノロジーで動作していますのでプラグインのインストールも不要ですし、最新のブラウザ上でも動作します。

なお、実装が全く違うので、xap ファイルを直接動作はできず、ソースコードからの再コンパイルが必要になります。

インストールは簡単です、トップページから VISX をダウンロードしてインストールするだけです。
f:id:hiro128:20210918182347p:plain

公式の Getting-started もあります。
doc.opensilver.net



インストールすると、OpenSilver のプロジェクトが作成できるようになります。


OpenSilver Application を選択します。
f:id:hiro128:20210918182458p:plain


雑に、Hello World を作ってみると…

細かいところは確認できていませんが、一応動きました。
f:id:hiro128:20210918182408g:plain


Silverlight は不滅ですね!みなさん、安心してこれからもSilverlight を使い続けましょう(嘘)


ちなみに何のためにこんなプロダクトを開発したのかな~と思いサイトをよく見てみると、
以下にある通りSilverlight のアプリのマイグレーションを請け負うビジネスモデルのようですね。
opensilver.net


なかなかおもしろいですね。もう少しいじってみようと思います。

Apple M1 Mac 上の Visual Studio for Mac で Xamarinはどれくらい使えるか調べました。(2020/12/13時点)

はじめに


こんにちは、@hiro128_777です。
 
この記事は「Xamarin Advent Calendar 2020」の13日目になります。
 
Apple M1 Mac が発売されて1ヶ月になりますね。というわけで、macOS Big Sur (Apple M1) 上の、Visual Studio for Mac で Xamarin がどれくらい使えるか再度確認してみました。
 
ちなみに、11/23 時点での状況こちらとなっております。
hiro128.hatenablog.jp


バージョン

 
Xcode は Universal App です。
f:id:hiro128:20201123213941p:plain

Visual Studio はまだ Intel App で Rosetta2 を介しての動作となります。
f:id:hiro128:20201213140416p:plain
 

Visual Studio for Mac のインストールと起動

 
インストールと起動は全く問題ありません。当該の Mac 上で、 なお、Rosetta 2 を利用するアプリの起動が初めての場合は、Rosetta 2 のインストールが走ります。
 

アプリごとの動作のまとめ

 

SDK 認識・コード編集 エミュレーター
デバッグ実行
実機
デバッグ実行
Xamarin Profiler
エミュレーター
Xamarin Profiler
実機
備考
iOS ○ OK ○ OK ○ OK ○ OK ○ OK  
Android ○ OK △ 一応動作 ○ OK ○ OK ○ OK エミュレーターはプレビュー版で動作
macOS ○ OK --------------- ○ OK --------------- ○ OK Intel App としてビルドされ、
Universal App としてビルドできない

 
結論から言うと、発売直後の状況より改善があります。
以下詳細となります。
  

iOS アプリ

 

SDK 認識・コード編集

問題ありません。
 

ビルド・デバッグ実行(シミュレータ)

問題ありません。
 

ビルド・デバッグ実行(実機)

問題ありません。
 
11/23 時点では iOS で実機にデプロイ時にプロビジョニングプロファイルの設定が不完全な時、1度だけ Visual Studio for Mac がクラッシュする現象が発生しましたが、それが起こらなくなりました。ですが、たまたま起こらなかったのか改善されたのかは不明です。
 

Xamarin Profiler(シミュレータ)

問題ありません。
 

Xamarin Profiler(実機)

問題ありません。
 

Android アプリ

 

SDK 認識・コード編集

問題ありません。
 

ビルド・デバッグ実行(エミュレータ

11/23 時点では、エミュレータが起動できず実行できませんでしたが、プレビューですが、Google からApple M1 で動作するエミュレーターがリリースされており、そちらででエミュレーター上のデバッグが可能となりました。

エミュレータは以下からダウンロードしてインストールできます。
github.com

 
起動すると、Visual Studio for Mac で認識します。
f:id:hiro128:20201213141145p:plain
f:id:hiro128:20201213141455p:plain

 
なお、プロジェクトオプションで「迅速なアセンブリの配置」を外さないと、デプロイでエラーが出ました。
f:id:hiro128:20201213140940p:plain
 
プレビュー版で試したら発生しませんでしたので、以下の issue のようです。
github.com

 

ビルド・デバッグ実行(実機)

問題ありません。
 

Xamarin Profiler(シミュレータ)

問題ありません。
 

Xamarin Profiler(実機)

問題ありません。
 

mac アプリ

 

SDK 認識・コード編集

問題ありません。
 

ビルド・デバッグ実行(実機)

 
こちらは状況が変わらず、ビルド、デバッグ実行できますが、Universal App としてビルドさせる設定が見つからず、結果として、Intel App としてビルドされ、Universal App としてはビルドできません。
f:id:hiro128:20201123215611p:plain
 

Xamarin Profiler(実機)

問題ありません。
 

まとめ

というわけで、再度の検証では、Apple M1 Mac 発売直後より状況が改善され、動くには動くという状況にはなりました。少なくとも実機での動作は問題なくなっていますので、実機デバッグのみで開発するのであれば問題ない感じです。エミュレーターも使う場合には、もう少し待てば、Android でも問題なく使えるようになりそうです。
 
なお、速度的には全く遅いとは感じませんでした。あとは、Visual Studio for Mac が M1 ネイティブで動くようになると嬉しいのですが、情報が全く無くどこまで開発が進んでいるのかもわかりません。Xamarin 後継の .NET MAUI は Visual Studio Code をサポートするとアナウンスされていますので、MAUI リリースまで Rosetta 2 で引っ張って、Visual Studio for Mac は廃止で Visual Studio Code に移行というような事態にならないかがとても心配です。
 
早く Visual Studio for Mac を M1 ネイティブ対応にしていただけると助かります。
 
今回は以上です。

macOS Big Sur (Apple M1) 上で、Visual Studio for Mac を軽く試してみました。(2020/11/23速報版)

はじめに

こんにちは、@hiro128_777です。
 
ようやく、Apple M1 Mac が自宅に届きました。というわけで、早速 Apple M1 MacVisual Studio for Mac を一通り軽く試してみました。なお、まだ詳しい検証はできておりませんので、引き続き調査したいと思います。
 
Intel Mac の Big Sur 上の動作状況は以下をご覧ください。
hiro128.hatenablog.jp
 

バージョン

 
Xcode は Universal App です。
f:id:hiro128:20201123213941p:plain
 

Visual StudioIntel App で Rosetta2 を介しての動作となります。
f:id:hiro128:20201123214003p:plain 
 


Visual Studio for Mac のインストールと起動

 
インストールと起動は全く問題ありません。当該の Mac 上で、 なお、Rosetta 2 を利用するアプリの起動が初めての場合は、Rosetta 2 のインストールが走ります。
f:id:hiro128:20201123214132p:plain
 


なお、Visual Studio for Mac Version 8.8 のリリースノートは以下をご参照ください。
docs.microsoft.com
 


アプリごとの動作のまとめ

 

SDK 認識・コード編集 エミュレータ
デバッグ実行
実機
デバッグ実行
備考
ASP.NET(.NET 5) ○ OK --------------- × NG Safari (Universal) 、 Chrome (Universal) ともに同じエラー
iOS ○ OK ○ OK ○ OK(若干不安定) 実機でプロビジョニングプロファイルの設定が不完全な時、
1度 VS がクラッシュ
Android ○ OK × NG × NG エミュレータが起動せず、実機も認識しない
macOS ○ OK --------------- ○ OK Intel App としてビルドされ、Universal App としてビルドできない

 


ASP.NET(.NET 5)


自分の環境では、Intel Mac 同様、ASP.NET で初回のみ開発証明書に関するダイアログが出ましたが、以下のように、画面表示の通り許可すれば、次回以降は聞かれなくなります。
f:id:hiro128:20201113143123p:plain
 
f:id:hiro128:20201113143139p:plain
 
f:id:hiro128:20201113143153p:plain
 
f:id:hiro128:20201113143213p:plain
 

上記は、微妙に発生条件が違いますが、以下の issue が相当しそうです。
docs.microsoft.com
 


ただし、対処後もデバッグ実行に失敗します。
 
f:id:hiro128:20201123214109p:plain


下記のようなエラーメッセージが出ます。

-------------------------------------------------------------------
You may only use the Microsoft .NET Core Debugger (vsdbg) with
Visual Studio Code, Visual Studio or Visual Studio for Mac software
to help you develop and test your applications.
-------------------------------------------------------------------
Loaded '/usr/local/share/dotnet/shared/Microsoft.NETCore.App/5.0.0/System.Private.CoreLib.dll'. Skipped loading symbols. Module is optimized and the debugger option 'Just My Code' is enabled.
Stack overflow.
   at System.Collections.HashHelpers.GetPrime(Int32)
   at System.Collections.Generic.Dictionary`2[[System.__Canon, System.Private.CoreLib, Version=5.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e],[System.__Canon, System.Private.CoreLib, Version=5.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]].Initialize(Int32)
   at System.Collections.Generic.Dictionary`2[[System.__Canon, System.Private.CoreLib, Version=5.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e],[System.__Canon, System.Private.CoreLib, Version=5.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]]..ctor(Int32, System.Collections.Generic.IEqualityComparer`1<System.__Canon>)
   at System.AppContext.Setup(Char**, Char**, Int32)

 



iOS アプリ

 

SDK 認識・コード編集

問題ありません。
 

ビルド・デバッグ実行(シミュレータ)

問題ありません。
 

ビルド・デバッグ実行(実機)

問題ありません。
 
プロビジョニングプロファイルの設定が不完全な時、1度だけ Visual Studio がクラッシュしました。その後は、プロビジョニングプロファイルの設定が不完全であることを示すエラーが表示されました。
  


Android アプリ

 

SDK 認識・コード編集

問題ありません。
 

ビルド・デバッグ実行(エミュレータ

エミュレータが起動できず、実行できません。 


ビルド・デバッグ実行(実機)

実機を認識しませんでした。ただし、Intel Mac では、実機をきちんと認識しますので、Visual Studio for Mac 起因の問題ではない可能性もあります。
 


mac アプリ

 

SDK 認識・コード編集

問題ありません。
 

ビルド・デバッグ実行(実機)

 
ビルド、デバッグ実行できますが、Universal App としてビルドさせる設定が見つからず、結果として、Intel App としてビルドされ、Universal App としてはビルドできません。
f:id:hiro128:20201123215611p:plain

まとめ

というわけで、軽く触った感じでは、Apple M1 Mac ではまだ問題が結構ありそうです。エラーの原因も詳しく調べられていないので、もう少し色々試してみたいと思います。
なお、速度的には特に遅いとは感じませんでした。Visual Studio for Mac が M1 ネイティブで動くようになるのが楽しみです。

また、M1 Mac 発売から1ヶ月後の 2020/12/13 時点での、調査結果は以下をご覧ください。
hiro128.hatenablog.jp

 
今回は以上です。

macOS Big Sur (Intel) 上で、Visual Studio for Mac を軽く試してみました。

はじめに

こんにちは、@hiro128_777です。

待ちに待った macOS Big Sur がリリースされました。というわけで、早速アップデートし Intel Mac 上で Visual Studio for Mac を一通り軽く試してみました。

f:id:hiro128:20201113133120p:plain
 


なお、Apple M1 チップ Mac 上の検証結果は以下をご覧ください。
hiro128.hatenablog.jp



バージョン

 


Visual Studio for Mac 起動

問題ありません。
f:id:hiro128:20201113133151p:plain
 
なお、Visual Studio for Mac Version 8.8 のリリースノートは以下をご参照ください。
docs.microsoft.com


iOS アプリ

SDK 認識・コード編集

問題ありません。
f:id:hiro128:20201113131721p:plain
 

ビルド・デバッグ実行(シミュレータ)

問題ありません。
f:id:hiro128:20201113140048p:plain
 


Android アプリ

SDK 認識・コード編集

問題ありません。
f:id:hiro128:20201113142030p:plain
 

ビルド・デバッグ実行(シミュレータ)

問題ありません。
f:id:hiro128:20201113140029p:plain
 


.NET 5 ASP.NET アプリ

SDK 認識・コード編集

問題ありません。
f:id:hiro128:20201113142058p:plain
 

ビルド・デバッグ実行

自分の環境では、ASP.NET デバッグ実行時、開発証明書に関するダイアログが出ましたが、以下のように、画面表示の通り許可すれば、問題ありませんでした。

f:id:hiro128:20201113143123p:plain
 
f:id:hiro128:20201113143139p:plain
 
f:id:hiro128:20201113143153p:plain
 
f:id:hiro128:20201113143213p:plain
 

上記は、微妙に発生条件が違いますが、以下の issue が相当しそうです。
docs.microsoft.com
 

なお、対処後は無事デバッグ実行もできました。
f:id:hiro128:20201113133803p:plain
 


まとめ

というわけで、軽く触った感じでは、Intel Mac では問題なさそうです。
 
Apple M1 Mac での挙動は、実機が届き次第確認してみます。
 
今回は以上です。

DualScreen 対応の Xamarin.Forms アプリを作ってみる (3)

はじめに

こんにちは、@hiro128_777です。

とうとう iPhone 12 が発売されましたね。でも、Surface Duo も忘れないでください。Surface Duo 日本での発売はまだですが、すでに、エミュレーターで疑似体験することは可能です。

というわけで、10月27日(火)に、エミュレーターSurface Duo 対応アプリを体験するオンラインハンズオンを開催いたします!

こちらは、簡単な Surface Duo のエミュレーターで動作するマルチスクリーン対応アプリを作って体験していただけるコンテンツになっていますので、ご興味がある方はぜひご参加下さい!


csharp-tokyo.connpass.com
 

この記事は(3)ですので、最初から手順をお試しになる場合は下記の(1)からお願いします。
hiro128.hatenablog.jp

 

(2)は下記からお願いします。
hiro128.hatenablog.jp

 

今回やること

 
ViewModel と Service を紐づけてアプリを完成させます。
 

ViewModel と Service を紐づける

 
ViewModel と Service を紐づけるコードを追記していきます。

なお、このサンプルはコードをシンプルにし、Dual Screen の動作をわかりやすくするために、 DI などコードの量が増える手法はあえて省いています。
 

MasterDetailPage.xaml.cs を修正

 
// 追記の部分を追記してください。
 

        bool IsSpanned => DualScreenInfo.Current.SpanMode != TwoPaneViewMode.SinglePane;

        DetailsPage detailsPagePushed;

        MasterViewModel masterViewModel; // 追記
        DetailsViewModel detailsViewModel; // 追記

        public MasterDetailPage()
        {
            InitializeComponent();

            NavigationPage.SetHasNavigationBar(this, IsSpanned);

            detailsPagePushed = new DetailsPage();

            masterViewModel = masterPage.BindingContext as MasterViewModel; // 追記
            masterViewModel.SetupViewsAction = () => SetupViews(); // 追記

            detailsViewModel = masterViewModel.DetailsViewModel; // 追記
            detailsPagePushed.BindingContext = detailsViewModel; // 追記
            detailsPage.BindingContext = detailsViewModel; // 追記
        }

 

Master.xaml.cs を修正

 
// 追記の部分を追記してください。
 

        public Master()
        {
            InitializeComponent();

            BindingContext = new MasterViewModel(new DetailsViewModel()); // 追記
        }

 

View から確認用の マークアップ を消去

 
<!-- 確認用(後ほど消去する)ここから -->から<!-- 確認用(後ほど消去する)ここまで -->の部分を消去してください。

    <Picker
        Title="路線を選択して下さい"
        Margin="10"
        HeightRequest="60"
        FontSize="Large"
        HorizontalOptions="Fill"
        VerticalOptions="Fill"
        ItemsSource="{Binding LineItems}"
        SelectedItem="{Binding SelectedLineItem, Mode=TwoWay}"
        >
    </Picker>
    <!-- 確認用(後ほど消去する)ここから -->
    <Label FontSize="Title" TextColor="Coral" Text="Master" HorizontalOptions="CenterAndExpand">
    </Label>
    <!-- 確認用(後ほど消去する)ここまで -->
    <CollectionView
        x:Name="Stations"
        ItemsSource="{Binding StationItems}"
        SelectionMode="Single"
        SelectedItem="{Binding SelectedStationItem, Mode=TwoWay}"
        SelectionChangedCommand="{Binding SelectStationCommand}"
        >

 

Details.xaml から確認用の マークアップ を消去

 
<!-- 確認用(後ほど消去する)ここから -->から<!-- 確認用(後ほど消去する)ここまで -->の部分を消去してください。

        <Label
            BackgroundColor="White"
            FontSize="Title"
            Grid.Column="1" Grid.Row="1"
            Text="{Binding Name}"
            VerticalTextAlignment="Center"
            >
        </Label>
    </Grid>
    <!-- 確認用(後ほど消去する)ここから -->
    <Label FontSize="Title" TextColor="Coral" Text="Detail" HorizontalOptions="CenterAndExpand">
    </Label>
    <!-- 確認用(後ほど消去する)ここまで -->
    <StackLayout
        BackgroundColor="White"
        Padding="5">

 

以上でコードは完成です。
 

デバッグ実行し、Dual Screen でアプリが動作するか確認する

 
Dual Screen で表示させるためには以下の動画の手順でアプリをスパンしてください。

Surface Duo Span 操作
youtu.be
 

上記の動画のように、路線と駅が選択でき、時刻が表示されればサンプルアプリは完成です。
 

今回は以上です。

DualScreen 対応の Xamarin.Forms アプリを作ってみる (2)

はじめに

こんにちは、@hiro128_777です。

とうとう iPhone 12 が発表されましたね。でも、Surface Duo も忘れないでください。Surface Duo 日本での発売はまだですが、すでに、エミュレーターで疑似体験することは可能です。

というわけで、10月27日(火)に、エミュレーターSurface Duo 対応アプリを体験するオンラインハンズオンを開催いたします!

こちらは、簡単な Surface Duo のエミュレーターで動作するマルチスクリーン対応アプリを作って体験していただけるコンテンツになっていますので、ご興味がある方はぜひご参加下さい!

csharp-tokyo.connpass.com


なお、この記事は(2)ですので、最初から手順をお試しになる場合は下記の(1)からお願いします。
hiro128.hatenablog.jp



今回やること

TwoPaneViewを作成し、Surface Duo エミュレータ―上で、Master - Detail 構成のページを Dual Screen 表示できるようにします。

TwoPaneViewGridを継承しており、左右または上下に 2 つのビューを配置できるコンテナーです。

TwoPaneViewの詳細については下記公式ドキュメントをご参照ください。

docs.microsoft.com



Dual Screen に対応するには、以下の対応を行うだけであり、非常に簡単です。

  • Single Screen 用と、Dual Screen 用の "ガワ" の ContentPage を別々に用意し、それぞれのコンテンツの配置情報のみ記述する。

  • 具体的なコンテンツ(配置する UI)の XAML を用意する。

  • TwoPaneViewが 画面の表示領域のサイズによって、Single Screen 用または、Dual Screen 用 の画面を表示できるように、MinWideModeWidthMinTallModeHeightを設定します。


TwoPaneViewの Single Screen、Dual Screen の制御の詳細については下記公式ドキュメントをご参照ください。

docs.microsoft.com


これから作成するサンプルアプリの Single Screen、Dual Screen のときの構成を図に表すと以下のようになります。

Single Screen 時の構成

f:id:hiro128:20201018171513p:plain
 

Dual Screen 時の構成

f:id:hiro128:20201018171631p:plain
 

では、次にコードを書いていきましょう。 

TwoPaneView用のコンテンツページとコンテンツビューのファイル追加する

 

  1. View フォルダを追加します。
  2. 以下の4つのコンテンツページを追加します。
    1. MasterDetailPage.xaml コンテンツページ
    2. DetailsPage.xaml コンテンツページ
    3. Master.xaml コンテンツビュー
    4. Details.xaml コンテンツビュー


以下のようになればOKです。
f:id:hiro128:20201018162214p:plain


デフォルトの MainPage に設定されているMainPage.xamlを削除します。

f:id:hiro128:20201018165908p:plain
 

MasterDetailPage を MainPage として設定します。
 

App.xaml.cs
        public App()
        {
            InitializeComponent();
            MainPage = new NavigationPage(new MasterDetailPage());
        }

 

MasterDetailPage.xamlマークアップとコードを記述する

 
Single Screen 時は Master Page のコンテンツのみ表示し、Dual Screen 時は 左に Master Page のコンテンツ、右に Detail Page のコンテンツを表示します。
 

MasterDetailPage.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"
            xmlns:dualScreen="clr-namespace:Xamarin.Forms.DualScreen;assembly=Xamarin.Forms.DualScreen"
            xmlns:local="clr-namespace:XFSurfaceDuoSample2020"
            mc:Ignorable="d"
            x:Class="XFSurfaceDuoSample2020.MasterDetailPage">
    <dualScreen:TwoPaneView MinWideModeWidth="4000" MinTallModeHeight="4000">
        <dualScreen:TwoPaneView.Pane1>
            <local:Master x:Name="masterPage"></local:Master>
        </dualScreen:TwoPaneView.Pane1>
        <dualScreen:TwoPaneView.Pane2>
            <local:Details x:Name="detailsPage"></local:Details>
        </dualScreen:TwoPaneView.Pane2>
    </dualScreen:TwoPaneView>
</ContentPage>

 

MasterDetailPage.xaml.cs
using System.ComponentModel;
using System.Linq;
using Xamarin.Forms;
using Xamarin.Forms.DualScreen;
using Xamarin.Forms.Xaml;

using XFSurfaceDuoSample2020.Models;
using XFSurfaceDuoSample2020.ViewModels;

namespace XFSurfaceDuoSample2020
{
    [XamlCompilation(XamlCompilationOptions.Compile)]
    public partial class MasterDetailPage
    {
        bool IsSpanned => DualScreenInfo.Current.SpanMode != TwoPaneViewMode.SinglePane;

        DetailsPage detailsPagePushed;

        public MasterDetailPage()
        {
            InitializeComponent();
            NavigationPage.SetHasNavigationBar(this, IsSpanned);
            detailsPagePushed = new DetailsPage();
        }

        async void SetupViews()
        {
            NavigationPage.SetHasNavigationBar(this, !IsSpanned);
            NavigationPage.SetHasNavigationBar(detailsPagePushed, !IsSpanned);
            NavigationPage.SetHasNavigationBar(detailsPage, !IsSpanned);

            if (!IsSpanned)
            {
                if (!Navigation.NavigationStack.Contains(detailsPagePushed))
                    await Navigation.PushAsync(detailsPagePushed);
            }
        }

        protected override void OnAppearing()
        {
            base.OnAppearing();
            DualScreenInfo.Current.PropertyChanged += OnFormsWindowPropertyChanged;
        }

        protected override void OnDisappearing()
        {
            base.OnDisappearing();
            DualScreenInfo.Current.PropertyChanged -= OnFormsWindowPropertyChanged;
        }

        void OnFormsWindowPropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            if (e.PropertyName == nameof(DualScreenInfo.Current.SpanMode) ||
                e.PropertyName == nameof(DualScreenInfo.Current.IsLandscape))
            {
                SetupViews();
            }
        }

    }
}

 

DetailsPage.xamlマークアップとコードを記述する

 
Single Screen 時は Detail Page を表示します。Dual Screen 時は使用されません。
 

DetailsPage.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"
             xmlns:local="clr-namespace:XFSurfaceDuoSample2020"
             mc:Ignorable="d"
             x:Class="XFSurfaceDuoSample2020.DetailsPage"
             Title="Master Details"
             Padding="10,0,0,0"
             >
    <local:Details></local:Details>
</ContentPage>

 

DetailsPage.xaml.cs
using Xamarin.Forms.DualScreen;
using Xamarin.Forms.Xaml;

namespace XFSurfaceDuoSample2020
{
    [XamlCompilation(XamlCompilationOptions.Compile)]
    public partial class DetailsPage
    {
        bool IsSpanned => DualScreenInfo.Current.SpanMode != TwoPaneViewMode.SinglePane;

        public DetailsPage()
        {
            InitializeComponent();
        }

        protected override void OnAppearing()
        {
            base.OnAppearing();
            DualScreenInfo.Current.PropertyChanged += OnFormsWindowPropertyChanged;
        }

        protected override void OnDisappearing()
        {
            base.OnDisappearing();
            DualScreenInfo.Current.PropertyChanged -= OnFormsWindowPropertyChanged;
        }

        async void OnFormsWindowPropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
        {
            if(e.PropertyName == nameof(DualScreenInfo.Current.SpanMode) ||
                e.PropertyName == nameof(DualScreenInfo.Current.IsLandscape))
            {
                if (IsSpanned)
                    await Navigation.PopAsync();
            }
        }
    }
}

 
 

Master.xamlマークアップとコードを記述する

 
Master Page のコンテンツです。
 

Master.xaml
<?xml version="1.0" encoding="utf-8" ?>
<StackLayout
    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="XFSurfaceDuoSample2020.Master"
    >
    <StackLayout.Resources>
        <ResourceDictionary>
            <Style TargetType="Grid">
                <Setter Property="VisualStateManager.VisualStateGroups">
                    <VisualStateGroupList>
                        <VisualStateGroup x:Name="CommonStates">
                            <VisualState x:Name="Normal" />
                            <VisualState x:Name="Selected">
                                <VisualState.Setters>
                                    <Setter
                                        Property="BackgroundColor"
                                        Value="LightSkyBlue"
                                        />
                                </VisualState.Setters>
                            </VisualState>
                        </VisualStateGroup>
                    </VisualStateGroupList>
                </Setter>
            </Style>
        </ResourceDictionary>
    </StackLayout.Resources>
    <Picker
        Title="路線を選択して下さい"
        Margin="10"
        HeightRequest="60"
        FontSize="Large"
        HorizontalOptions="Fill"
        VerticalOptions="Fill"
        ItemsSource="{Binding LineItems}"
        SelectedItem="{Binding SelectedLineItem, Mode=TwoWay}"
        >
    </Picker>
    <!-- 確認用(後ほど消去する)ここから -->
    <Label FontSize="Large" TextColor="Coral" Text="Master Page のコンテンツ" HorizontalOptions="CenterAndExpand">
    </Label>
    <!-- 確認用(後ほど消去する)ここまで -->
    <CollectionView
        x:Name="Stations"
        ItemsSource="{Binding StationItems}"
        SelectionMode="Single"
        SelectedItem="{Binding SelectedStationItem, Mode=TwoWay}"
        SelectionChangedCommand="{Binding SelectStationCommand}"
        >
        <CollectionView.ItemTemplate>
            <DataTemplate>
                <Grid
                    Padding="5"
                    >
                    <Frame Visual="Material" BorderColor="LightGray">
                        <StackLayout
                            Padding="0"
                            >
                            <Label FontSize="Title" Text="{Binding Name}">
                            </Label>
                        </StackLayout>
                    </Frame>
                </Grid>
            </DataTemplate>
        </CollectionView.ItemTemplate>
    </CollectionView>
</StackLayout>

 

Master.xaml.cs
using Xamarin.Forms;
using Xamarin.Forms.Xaml;

using XFSurfaceDuoSample2020.ViewModels;

namespace XFSurfaceDuoSample2020
{
    [XamlCompilation(XamlCompilationOptions.Compile)]
    public partial class Master : StackLayout
    {
        public Master()
        {
            InitializeComponent();
        }
    }
}

 

Details.xamlマークアップとコードを記述する

 
Detail Page のコンテンツです。
 

Details.xaml
<?xml version="1.0" encoding="UTF-8"?>
<StackLayout 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="XFSurfaceDuoSample2020.Details"
             BackgroundColor="LightGray"
             Spacing="0"
             >
    <Grid
        BackgroundColor="White"
        RowSpacing="2" ColumnSpacing="2"
        Padding="8,8,8,0"
        VerticalOptions="Center"
        >
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"></RowDefinition>
            <RowDefinition Height="Auto"></RowDefinition>
            <RowDefinition Height="*"></RowDefinition>
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="Auto"></ColumnDefinition>
            <ColumnDefinition Width="*"></ColumnDefinition>
        </Grid.ColumnDefinitions>
        <Label
            BackgroundColor="White"
            HorizontalTextAlignment="End" VerticalTextAlignment="Center"
            FontSize="Title" Text="ナンバリング :"
            >
        </Label>
        <Label
            BackgroundColor="White"
            Grid.Row="1"
            HorizontalTextAlignment="End" VerticalTextAlignment="Center"
            FontSize="Title" Text="駅名 :"
            >
        </Label>
        <Label
            BackgroundColor="White"
            FontSize="Title"
            Grid.Column="1"
            Text="{Binding SelectedStationItem.ID}"
            VerticalTextAlignment="Center"
            >
        </Label>
        <Label
            BackgroundColor="White"
            FontSize="Title"
            Grid.Column="1" Grid.Row="1"
            Text="{Binding Name}"
            VerticalTextAlignment="Center"
            >
        </Label>
    </Grid>
    <StackLayout
        BackgroundColor="White"
        Padding="5">
        <!-- 確認用(後ほど消去する)ここから -->
        <Label FontSize="Large" TextColor="Coral" Text="Detail Page のコンテンツ" HorizontalOptions="CenterAndExpand">
        </Label>
        <!-- 確認用(後ほど消去する)ここまで -->
        <CollectionView
            Margin="3"
            BackgroundColor="LightGray"
            x:Name="Stations"
            ItemsSource="{Binding TimeTableRowItems}"
            SelectionMode="None"
            >
            <CollectionView.ItemTemplate>
                <DataTemplate>
                    <StackLayout
                        BackgroundColor="LightGray"
                        Padding="1">
                        <Grid
                            BackgroundColor="LightGray"
                            HorizontalOptions="Fill"
                            VerticalOptions="Center"
                            ColumnSpacing="2"
                            Padding="2,1,2,1"
                            >
                            <Grid.RowDefinitions>
                                <RowDefinition Height="Auto"></RowDefinition>
                            </Grid.RowDefinitions>
                            <Grid.ColumnDefinitions>
                                <ColumnDefinition Width="*"></ColumnDefinition>
                                <ColumnDefinition Width="10*"></ColumnDefinition>
                            </Grid.ColumnDefinitions>
                            <Label
                                BackgroundColor="LightSkyBlue"
                                Padding="8"
                                Grid.Column="0" Grid.Row="0"
                                Text="{Binding Hour}"
                                FontSize="Large"
                                HorizontalTextAlignment="Center" 
                                VerticalTextAlignment="Center"
                                >
                            </Label>
                            <Label
                                BackgroundColor="Beige"
                                Padding="8"
                                Grid.Column="1" Grid.Row="0"
                                Text="{Binding RowText}"
                                FontSize="Large"
                                HorizontalTextAlignment="Start"
                                VerticalTextAlignment="Center"
                                >
                            </Label>
                        </Grid>
                    </StackLayout>
                </DataTemplate>
            </CollectionView.ItemTemplate>
        </CollectionView>
    </StackLayout>
</StackLayout>

 

Details.xaml.cs
using Xamarin.Forms;
using Xamarin.Forms.Xaml;

namespace XFSurfaceDuoSample2020
{
    [XamlCompilation(XamlCompilationOptions.Compile)]
    public partial class Details : StackLayout
    {
        public Details()
        {
            InitializeComponent();
        }
    }
}

 

デバッグ実行し、Single Screen、Dual Screen で表示されるか確認する。

 
Surface Duo エミュレータ―にデプロイすると、以下のように、Single Screen、Dual Screen で表示が変化します。
 

Single Screen での表示

f:id:hiro128:20201018214953p:plain
 

Dual Screen での表示

f:id:hiro128:20201018215010p:plain

 
  
Dual Screen で表示させるためには以下の動画の手順でアプリをスパンしてください。
なお、アプリのロジックはまだ実装していないので、画面が表示されるだけで、UI は反応しません。
 
Surface Duo Span 操作
youtu.be

 

続きは以下をご覧ください。
hiro128.hatenablog.jp

DualScreen 対応の Xamarin.Forms アプリを作ってみる (1)

はじめに

こんにちは、@hiro128_777です。

とうとう iPhone 12 が発表されましたね。でも、Surface Duo も忘れないでください。Surface Duo 日本での発売はまだですが、すでに、エミュレーターで疑似体験することは可能です。

というわけで、10月27日(火)に、エミュレーターSurface Duo 対応アプリを体験するオンラインハンズオンを開催いたします!

こちらは、簡単な Surface Duo のエミュレーターで動作するマルチスクリーン対応アプリを作って体験していただけるコンテンツになっていますので、ご興味がある方はぜひご参加下さい!


csharp-tokyo.connpass.com


今回から数回 DualScreen 対応の Xamarin.Forms アプリを作る方法をご紹介します。

今回の内容の詳しい手順は、下記公式ドキュメントにもあります。
なお、サンプルのソリューションを準備しましたので、より詳しい手順を追って、実際に触って試していただけるようにしました。


docs.microsoft.com


エミュレーターのインストール

MacWindows それぞれの方法をまとめてありますので、以下の記事をご参照ください。

サンプルのソリューション

以下より取得してください。
github.com


XFSurfaceDuoSample2020/start-long/XFSurfaceDuoSample2020.slnを開ます。


SDK のインストール

NuGet パッケージ マネージャーで Xamarin.Forms.DualScreen を検索し、ソリューションのそれぞれのプロジェクトに、 Xamarin.Forms と同じバージョンの Xamarin.Forms.DualScreen パッケージをインストールします。

f:id:hiro128:20201013230011p:plain


MainActivity クラスの変更

 

属性の変更

Android プロジェクトの MainActivity クラスの ConfigurationChanges 属性を確認し、属性が足りなければ変更します。

ConfigurationChanges を設定することで、構成の変更が発生した場合にシステムによってアクティビティが再起動されなくなります。また、発生時の処理のコードを記述することもできます。

詳しくは、以下の公式ドキュメントをご参照ください。

docs.microsoft.com


以下の属性が設定されていることを確認してください。

ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation | ConfigChanges.ScreenLayout | ConfigChanges.SmallestScreenSize | ConfigChanges.UiMode

 

以下のようになります。

    [Activity(Label = "XFSurfaceDuoSample2020", Icon = "@mipmap/icon", Theme = "@style/MainTheme", MainLauncher = true, 
        ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation | ConfigChanges.UiMode | ConfigChanges.ScreenLayout | ConfigChanges.SmallestScreenSize 
    )]
    public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsAppCompatActivity

 

DualScreenService の初期化

次に、DualScreenService を初期化します。

OnCreate メソッドの LoadApplication(new App()); の直上に以下の初期化メソッド呼び出しを追加します。

// Initialize DualScreen Service
Xamarin.Forms.DualScreen.DualScreenService.Init(this);

 

以下のようになります。

            Xamarin.Essentials.Platform.Init(this, savedInstanceState);
            global::Xamarin.Forms.Forms.Init(this, savedInstanceState);

            // Initialize DualScreen Service
            Xamarin.Forms.DualScreen.DualScreenService.Init(this);

            LoadApplication(new App());
        }

 


エミュレータ―上でデバッグ実行

ビルドし、 (Android 10.0 - API 29) を選択し、デバッグ実行できれば成功です。

Mac

f:id:hiro128:20201013225904p:plain


Windows

f:id:hiro128:20201013225949p:plain



デプロイが完了し、以下のようになれば成功です。

f:id:hiro128:20201013232449p:plain


続きは以下をご覧ください。
hiro128.hatenablog.jp