Profile
ブログ : http://blog.processtune.com
プロフィール : Facebook, Twitter or MVP
コミュニティ : .NETラボの運営スタッフ
Microsoft MVP : July 2010 ~ Jun 2022
Current expertise : MVP for Developer Technologies
システム構築のプロセス評価、改善、策定、開発フ
レームワークの設計、実装管理、プリセールスやプロ
ジェクトの立ち上げなど
Agenda
カラー
System.Drawing名前空間では、プログラムの描画は直接レンダリングエンジンに渡されていましたが、Microsoft.Maui.Graphicsでは、WPFのように描画プラット
フォームで描画データに変換されレンダリングエンジンに非同期に渡されます。このような描画の仕組みは Microsoft.Maui.Graphicsでも同様です。そのため、WPF
のSystem.Windows.Mediaと同じようなコーディングになります。 WPFアプリケーションやMAUIアプリケーションでは、System.Drawing名前空間を参照させてプロ
グラムを作成することもできますが、これはMAUIアプリケーションへの順次移行の段階でのみ有用なのかもしれませんので、 System.Drawing、 System.Window
s.Media、 Microsoft.Maui.Graphicsの関係を解説します。
ブラシ
ブラシを使う際はコーディング時に軽微な違いがありますので解説しておきます。
画面写真
System.Drawing名前空間では直接レンダリングエンジンに描画するので、画面のキャプチャを行うためにはシステムの値を利用したグラフィックスを再構成する必
要がありますが、System.Windows.Mediaでは描画プラットフォームの機能を使って画面に出力していたものをファイルに書き出すように命令をすることができ、コン
ポーネント単位でキャプチャすることができます。 Microsoft.Maui.Graphicsでも同じ仕組みを使ったコーディングができます。
.NET 7 で統合されたプラットフォームAPI
.NET7では、 System.Windows.Mediaで行っていたコーディングを使えますがWindows依存のアプリケーションになります。そのため新たに用意された.NET 7 MA
UI統合プラットフォームを使うことで、マルチプラットフォームのコーディングの差異を最小化することができます。「await Screenshot.CaptureAsync()」だけで画面写
真を撮れるので、パーミッションの設定やディレクティブを使わないプラットフォームごとの定義箇所、ディレクティブを使ったコーディングなどをあわせて解説します。
MAUI(Multi-platform App UI)アプリ開発の準備
MAUIアプリ開発の準備 - Graphics Architecture
C++/COM APIs
出典:Overview of the Windows Graphics Architecture: Microsoft Learn
DirectX
Microsoft.DirectX
Microsoft.DirectX.Direct3D
Microsoft.DirectX.Direct3DX
Microsoft.DirectX.DirectDraw
Microsoft.DirectX.DirectInput
Microsoft.DirectX.DirectPlay
Microsoft.DirectX.DirectSound
GDI+
System.Drawing.IDeviceContext
System.Drawing.Common
System.Drawing.Graphics
GDI+ 描画サーフェイスのカプセル化
System.Drawing
System.Drawing.Drawing2D
LinearGradientBrush
PathGradientBrush
HatchBrush
Blendなど
System.Drawing.Imaging
Bitmap
Metafileなど
System.Drawing.Text
FontCollectionなど
C# APIs
MAUIアプリ開発の準備 - Support
.NET MAUI apps
Android 5.0 (API 21) 以上
iOS 11以上, Xcodeの最新リリース
macOS 10.15以上(Mac Catalyst使用)
Windows 11 and Windows 10 version 1809以上
( Windows UI Library 3: WinUI 3 使用)
.NET MAUI Blazor apps
Android 7.0 (API 24)以上
iOS 14以上
macOS 11以上(Mac Catalyst使用)
MAUIアプリ開発の準備 – Windows OS
MAUIアプリ
Blazor
ContentPage
Native
ContentPage
ContentView
MAUI名前空間を使ったアプリ
Blazor WASM
Razor chtml
Native(WPF、UWP)
XAML
Cross
platform
graphic
canvas
Android
Native
Android
Wear
iOS
MAUIアプリ開発の準備 – Windows OS
MAUIアプリ開発の準備 - 非標準のコントロールライブラリ
Visual Studioで作成
Visual Studio Codeで作成するのは困難
Blend for Visual Studio 2022でデザインはできない
名前空間を使ってテキストの色を変更する
グラデーション・ブラシを使う
画面写真を撮る
Blend for Visual Studio 2022(XAML designer extensibility migration:巻末リンク参照)
Surface isolation(.NET Framework プロセスでホストできないターゲット ランタイムをサポートするためのアーキテクチャ)
現在.NET Core WPFとUWPをサポート
Blendの
テンプレート
Blendで
XAMLを開く
MAUIアプリ開発の準備-ColorをXAMLで確認
Visual Studioで作成
Visual Studio Codeで作成するのは困難
Blend for Visual Studio 2022でデザインはできない
名前空間を使ってテキストの色を変更する
グラデーション・ブラシを使う
画面写真を撮る
F12
MAUIアプリ開発の準備-Colorをコードで確認
Visual Studioで作成
Visual Studio Codeで作成するのは困難
Blend for Visual Studio 2022でデザインはできない
名前空間を使ってテキストの色を変更する
グラデーション・ブラシを使う
画面写真を撮る
MAUIアプリ開発の準備-System.Drawing
Visual Studioで作成
Visual Studio Codeで作成するのは困難
Blend for Visual Studio 2022でデザインはできない
名前空間を使ってテキストの色を変更する
グラデーション・ブラシを使う
画面写真を撮る
System.Windows.MediaとMicrosoft.Maui.Graphicsについて
MAUIアプリ-Gradient Blush
Visual Studioで作成
Visual Studio Codeで作成するのは困難
Blend for Visual Studio 2022でデザインはできない
名前空間を使ってテキストの色を変更する
グラデーション・ブラシを使う
画面写真を撮る
ブラシ
単色
SolidColorBrush
グラデーション
LinearGradientBrush RadialGradientBrush
MAUIアプリ-Gradient Blush
Visual Studioで作成
Visual Studio Codeで作成するのは困難
Blend for Visual Studio 2022でデザインはできない
名前空間を使ってテキストの色を変更する
グラデーション・ブラシを使う
画面写真を撮る
var brush = new RadialGradientBrush();
var gradientStops = new GradientStopCollection();
gradientStops.Add(new GradientStop(Colors.WhiteSmoke, 0.0f));
gradientStops.Add(new GradientStop(Colors.CornflowerBlue, 0.7f));
gradientStops.Add(new GradientStop(Colors.BlueViolet, 1.0f));
brush.GradientStops = gradientStops;
brush.Center = new Point(0.2, 0.2);
brush.Radius = 1.2d;
this.Background = brush;
System.Windows.MediaやSystem.Drawing時代の画面写真
System.DrawingからMicrosoft.Maui.Graphicsへ
MAUIアプリ
Blazor
ContentPage
Native
ContentPage
ContentView
MAUI名前空間を使ったアプリ
Blazor WASM
XAML
Cross
platform
graphic
canvas
Native(WPF、UWP)
XAML
Cross
platform
graphic
canvas
Android
Native
Android
Wear
iOS
WPFアプリ-Desktop Native
Visual Studioで作成
Visual Studio Codeで作成するのは困難
Blend for Visual Studio 2022でデザインはできない
名前空間を使ってテキストの色を変更する
グラデーション・ブラシを使う
画面写真を撮る
>dotnet new wpf
>dotnet run
Blend for Visual Studio 2022
画面写真 – System.Windows.Media名前空間
画面写真 – Legacy(System.Drawing名前空間)
マルチプラットフォーム開発 - Android
System.DrawingからMicrosoft.Maui.Graphicsへ
MAUIアプリ
Blazor
ContentPage
Native
ContentPage
ContentView
MAUI名前空間を使ったアプリ
Blazor WASM
XAML
Cross
platform
graphic
canvas
Native(WPF、UWP)
XAML
Cross
platform
graphic
canvas
Android
Native
Android
Wear
iOS
画面写真 - .NET7のPlatform API
エミュレーター
Windows
Arm64では動きません
Android
Mac(要実機)
シミュレーター
or デバイス
(ローカル/リモート)
Visual Studio
2022
Ver 17.4
.NET 7
Microsoft.Maui.Graphics.Media名前空間
Screenshotクラス
CaptureAsyncメソッド
画面写真 - .NET7 MAUIアプリの作成
画面写真 - .NET7 非マルチプラットフォーム
画面写真 – System.Environmentを使う場合
MainPage.xaml.csでSystem.Environmentを使う場合
Windowsの場合(#if WINDOWディレクティブを使う)
dirPath = Environment.GetEnvironmentVariable("OneDrive", EnvironmentVariableTarget.User);
dirPath = Environment.GetEnvironmentVariable("TMP", EnvironmentVariableTarget.User);
Androidの場合(#elif ANDROIDディレクティブを使う)
dirPath = Environment.GetEnvironmentVariable("TMPDIR");
しかしAndroidの場合、ユーザーが書き込めるのは
「storage/emulated/0/Android/data/com.companyname.mauiappnative/cache」などユーザーがマウントしたド
ライブであり、Environment.GetEnvironmentVariableが返すのは
「/data/user/0/Android/data/com.companyname.mauiappnative/cache」などユーザーのデータ領域のため、
dirPath = dirPath.Replace("/data/user/0/","/sdcard/android/data/");
などを行う必要がある。
MainPage.xaml.cs
画面写真 – 環境変数の差異
画面写真 – 各プラットフォーム→MauiProgram.cs構造
画面写真 –各プラットフォーム→MauiProgram.csコード
このような文字列が取得できる
/storage/emulated/0/Android/data/com.companyname.mauiappnative/cache
/storage/3730-3830/Android/data/com.companyname.mauiappnative/cache
マルチプラットフォームプログラムで利用→
プラットフォーム依存のJava.IO.File[]オブジェクトを取得
このコードではひとつしか値が返ってきませんが、マルチプラッ
トフォーム側のコードでWindows側が返してくる複数の値を
取得するコードを合わせたいのでListにしている。
MainApplication.cs
MauiProgram.cs
画面写真 – プラットフォーム依存の値を利用する
MainPage.xaml.cs
画面写真 – MainActivity.cs
画面写真 – App container
アプリケーションが使用するOSの機能の宣言 ユーザーの許可を得ているかの確認 確認の結果によってどのように挙動させたいか
AndroidManifest.xmlで宣言
MainActivity.csのOnCreateで
ユーザー同意の確認
MainActivity.csの
OnRequestPermissionsResult
のオーバーライドで確認結果別の挙動を制御
画面写真 – AndroidManifest.xml
アプリケーションが使用するOSの機能の宣言 ユーザーの許可を得ているかの確認 確認の結果によってどのように挙動させたいか
AndroidManifest.xmlで宣言
画面写真 – AndroidManifest.xml
アプリケーションが使用するOSの機能の宣言 ユーザーの許可を得ているかの確認 確認の結果によってどのように挙動させたいか
MainActivity.csのOnCreateで
ユーザー同意の確認
画面写真 – AndroidManifest.xml
アプリケーションが使用するOSの機能の宣言 ユーザーの許可を得ているかの確認 確認の結果によってどのように挙動させたいか
MainActivity.csの
OnRequestPermissionsResult
のオーバーライドで確認結果別の挙動を制御
画面写真 - .NET7 FilePicker
画面写真 – 汎用ホストのサービス追加
Conclusion
カラー
描画の仕組みは Microsoft.Maui.GraphicsとWPFのSystem.Windows.Mediaは同じなので、同じようなコーディングになります。さほど工数はかからないので、今のうち
にMicrosoft.Maui.Graphicsを使うコードに更新しておくことをお勧めします。 System.Drawing、 System.Windows.Media、 Microsoft.Maui.Graphicsの描画の特
徴に合わせたプログラムを作成します。 System.Drawingは直接ディスプレイに描画する仕組みであり、モダンなアプリでは描画プラットフォームでデータ化された描画結果
をOSに渡すのでリスト表示などで描画遅延は軽減されます。しかしながら多くのアイテムを表示するリストが使いやすいのかどうかは考慮すべきです。アプリケーションでExc
elのような機能を作った際の操作感は、OneDrive上のExcelの操作感に劣るはずです。ユーザビリティを考慮したビジネスフロー全体の運用設計を行うことをお勧めします。
ブラシ
Microsoft.Maui.Graphics名前空間は、System.Drawing名前空間からSystem.Windows.Media名前空間に変更した時ぐらい軽微な作業です。 System.Drawin
gとSystem.Windows.Mediaは、そもそも描画の仕組みが違うのでコーディングはそれほど変わらなくとも、アプリケーションの挙動に影響するためアプリケーションの設計に
影響するケースもあると思います。一方Microsoft.Maui.GraphicsとSystem.Windows.Mediaは描画プラットフォームで描画データを生成するという点においてアプリケー
ションの挙動には影響しません。
画面写真
Microsoft.Maui.GraphicsでもSystem.Windows.Mediaと同じコーディングができますが非マルチプラットフォームなアプリケーションになりますので、マルチプラットフォームへ
の移行段階で採用できる手法です。
.NET 7 で統合されたプラットフォームAPI
.NET7では、新たに統合された.NET 7 プラットフォームAPIを使うことで、非常に容易にマルチプラットフォームのコーディングが可能です。統合された.NET 7 プラットフォームA
PIの使用では多くの作業は、各プラットフォームの軽微な差異の吸収になります。
Links1
Pexels
https://www.pexels.com/ja-jp/photo/7451932/
Overview of the Windows Graphics Architecture: Microsoft Learn
https://learn.microsoft.com/en-us/windows/win32/learnwin32/overview-of-the-windows-graphics-architecture
dotnet/Microsoft.Maui.Graphics: GitHub(追加シナリオ用)
https://github.com/dotnet/Microsoft.Maui.Graphics
dotnet/maui: GitHub(Microsoftサポート統合)
https://github.com/dotnet/maui
Android Open Source Project
https://source.android.com/
Android™️ 用 Windows サブシステム: Microsoft Learn
https://learn.microsoft.com/ja-jp/windows/android/wsa/
Android 用 Windows サブシステム ™️のリリース ノート: Microsoft Learn
https://learn.microsoft.com/ja-jp/windows/android/wsa/release-notes
XAML designer extensibility migration: GitHub
https://github.com/microsoft/xaml-designer-extensibility/blob/main/documents/xaml-designer-extensibility-migration.md
XAML デザイナーを使用して UI を作成する: Microsoft Learn
https://learn.microsoft.com/ja-jp/visualstudio/xaml-tools/creating-a-ui-by-using-xaml-designer-in-visual-studio?view=vs-2022
ブラシ: Microsoft Learn
https://learn.microsoft.com/ja-jp/dotnet/maui/user-interface/brushes/
System.Drawing.Common: NuGet
https://www.nuget.org/packages/System.Drawing.Common/6.0.0
Links2
Make System.Drawing.Common only supported on Windows: GitHub
https://github.com/dotnet/designs/blob/main/accepted/2021/system-drawing-win-only/system-drawing-win-only.md
Method Draw
https://editor.method.ac/
Microsoft.Maui.Animations 名前空間: Microsoft Learn
https://github.com/dotnet/Microsoft.Maui.Graphics
Screenshot クラス: Microsoft Learn
https://learn.microsoft.com/ja-jp/dotnet/api/microsoft.maui.media.screenshot?view=net-maui-6.0
プラットフォームの統合: Microsoft Learn
https://learn.microsoft.com/ja-jp/dotnet/maui/platform-integration/?view=net-maui-7.0
SDK Platform-Tools リリースノート
https://developer.android.com/studio/releases/platform-tools
USBドライバー: Motorola Device Manager
https://motorola-global-portal-jp.custhelp.com/app/usb-drivers/
Manifest.permission: Google Developers Documentation
https://developer.android.com/reference/android/Manifest.permission
xamarin/Essentials/Xamarin.Essentials/Permissions/Permissions.android.cs: GitHub
https://github.com/xamarin/Essentials/blob/main/Xamarin.Essentials/Permissions/Permissions.android.cs
<uses-sdk>: developers documentation(What is API Level?: table of android API Level and Platform ver.)
https://developer.android.com/guide/topics/manifest/uses-sdk-element#ApiLevels
/APPCONTAINER: Microsoft Learn
https://learn.microsoft.com/ja-jp/cpp/build/reference/appcontainer?view=msvc-170
Links3
Android Debug Bridge(adb): developers documentation
https://developer.android.com/studio/command-line/adb
Android 6.0 Marshmallow
https://www.android.com/intl/ja_jp/versions/marshmallow-6-0/features/
.NET での依存関係の挿入 - サービス登録メソッド: Microsoft Learn
https://learn.microsoft.com/ja-jp/dotnet/core/extensions/dependency-injection#constructor-injection-behavior
.NET 7 統合されたプラットフォームAPIのGitHubサンプル
https://github.com/TetsuroTakao/MauiNativeAppSample
Microsoft.Maui.Graphics名前空間を使用したWPFアプリのGitHubサンプル
https://github.com/TetsuroTakao/MAUIGraphicsNamespaceWPF

MAUIGraphicsNamespace.pptx

Editor's Notes

  • #2 描画機能を提供する名前空間は3つあります。System.Drawing名前空間はGDI+ の基本的なグラフィックス機能を使用する目的で使用します。一方System.Windows.MediaはWPFの描画プラットフォームでデータ化された描画結果を稼働環境のレンダリングエンジンに非同期に渡します。これにより、GPUが使える環境ではGPUを使った描画を行ったり、データバインディングを行ったりすることができ、必要に応じてプログラムでビジュアルステートなとを制御できるようになります。 Microsoft.Maui.Graphics名前空間は、System.Windows.Media名前空間と同じ仕組みで描画プラットフォームで描画データを生成しますが、稼働環境のレンダリングエンジンに非同期で転送する際にマルチプラットフォームで動作するよう設計されています。 描画オブジェクトが抽象化されるように通知やドライブ、バッテリーやセンサーといったエッジのオブジェクトはプラットフォーム個別の機能が統合されたプラットフォーム APIで抽象化されます。 その一つとして描画を担当するMicrosoft.Maui.Graphics名前空間があります。 今回は、System.Drawing名前空間を使って描画処理を行っていた開発者向けに、Microsoft.Maui.Graphics名前空間を使った画面写真の撮り方やブラシの作成などの入門的な解説を行います。
  • #3 自己紹介読む
  • #6 MAUIアプリ開発の準備として、前提となる知識を少しお話しします。GDI+を使う場合、P-InvokeでDLLを読込んだプログラムを行うか、Windows.Formsのようなレンダリングエンジンに直接描画できるプラットフォームを利用してSystem.Drawing名前空間を使ってプラットフォームに描画命令を出す必要があります。そのため、C#ではかなり多くの描画APIを利用することができます。
  • #7 Microsoft.Maui.Graphicsは、System.Windows.Mediaと同じ描画の仕組みを使うので、System.Windows.Mediaの描画プログラムを作成できる人は描画プラットフォームの違いを意識することで、それほど多くの学習コストを必要としません。MAUI appsとMAUI Blazor appsはこのような稼働環境の違いがありますが、後述する.NET 7 MAUI統合プラットフォームでは、たとえばパーミッションを利用してSDカードなどにファイルを書き込む際、Androidでは Marshmallow以上(Android 6.0 API 23以上)など、実装する機能によって描画プラットフォームの挙動よりマルチプラットフォームの稼働条件などを意識する必要があります。
  • #8 MAUI Graphics Architectureは、System.Windows.Mediaとほぼ同じですので、WPFやUWPなどで利用することができます。 【クリック】そのため、Microsoft.Maui.Graphics名前空間を使いたいだけならWPFアプリで十分です。 【クリック】開発の準備として、MAUIアプリを作成する場合は、Windowsの開発者モードはオンにしておきます。任意のフォルダでWindowsターミナルやVisual Studio Codeのターミナルなどを使って「dotnet new maui」でアプリケーションを作ることもできますが、コマンドから作成した場合は一度Visual Studioでプロジェクトを開いてソリューションファイルを作ってやる必要があるので、最初からVisual Studioで作成した方が楽です。
  • #9 こちらは、Blazor WASMプロジェクトにMicrosoft.Maui.Graphics名前空間をインストールする例です。
  • #10 もうひとつ注意が必要なのですが、Blend for Visual Studio 2022でもデザイナーを使うことはできないので、MAUIアプリの作成は今のところVisual Studioで作成する方が賢明です。 この開発環境の描画の仕組みにも少し触れておきます。Visual Studio 2019 XAML designerとBlend for Visual Studio 2022は同じアーキテクチャであるSurface isolationとDesigner isolationを使ったビジュアルデザインインターフェイスを持っています。Surface isolationは、.NET Framework プロセスでホストできないターゲット ランタイムをサポートするためのアーキテクチャで現在.NET Core WPFとUWPをサポートしています。Designer isolationはコントロールライブラリやXAML DesignerなどのDLLをXDesProc.exeがホストする外部プロセスに読み込んで、ビジュアル・デザインツールとコーディングツール、たとえばVisual StudioとBlendで同期させるアーキテクチャです。一方、WpfSurface.exeがホストするプロセスにコントロールライブラリだけを読み込むことでマルチプラットフォームに対応するのがSurface isolationアーキテクチャです。XDesProc.exeがホストするプロセスに読み込まれていたコントロールライブラリはWpfSurface.exeがホストするプロセスには読み込まれませんので、コントロールライブラリを開発されていた方々は注意が必要です。詳細は巻末リンクを参照してください。
  • #11 後程実際の作成手順はお話ししますが、まずは入門として簡単な名前空間のお話をします。 Visual StudioでMAUIアプリケーションを作成して、MainWindow.xamlを開いて任意の場所の「TextColor」プロパティを変更します。 【クリック】TextColorの文字に合わせてF12をクリックしてMicrosoft.Maui.Graphics名前空間のColorオブジェクトであることを確認できます。
  • #12 また、MainWindow.xaml.cs側でコードでTextColorプロパティを変更して、ブレークポイントでクィックウォッチでも名前空間は確認できます。 【クリック】MAUIアプリケーションを作成するとMicrosoft.Maui.Graphics名前空間が使われることになります。
  • #13 Microsoft.Maui.Graphics名前空間と同じように描画プラットフォームに対して描画コードを投げるWPFアプリケーションの場合も同じですが、無理やりSystem.Drawingをusingできます。 【クリック】この場合、Colorだけを記述すると、どちらの名前空間を使うのか聞かれます。 【クリック】解説のためSystem.Drawingを無理やり使いましたが、もちろんMAUIアプリではなくなります。 【クリック】稀な例だとは思いますが、マルチアプリケーションに移植中のプロジェクトは最初にMAUIプロジェクトにしてから、移行中の間だけNugetでSystem.Drawingを使うことになります。
  • #14 一通りSystem.Drawing名前空間との比較や開発の準備の解説が終わったので、今度はブラシを使ってSystem.Windows.MediaとMicrosoft.Maui.Graphicsのコーディングの違いについてお話しします。
  • #15 System.Windows.MediaとMicrosoft.Maui.Graphicsは描画の仕組みが同じなので、コーディングもほぼ同じになります。注意が必要なのは、System.Drawingの時にDirectX2DのSystem.Drawing.Drawing2D名前空間で提供されていたHatchBrushやPathGradientBrushは使えません。
  • #16 コーディングはSystem.Windows.Mediaとほぼ同じですがグラデーションの場合、Radiusの設定だけが少々異なります。 【クリック】Microsoft.Maui.GraphicsのRadiusはダブル型のプロパティです。 【クリック】System.Windows.MediaはXとYを別々に設定します。この部分以外は全く同じコーディングになります。
  • #17 のちほどSystem.Windows.MediaやSystem.Drawingを使った画面写真のコーディングとMicrosoft.Maui.Graphicsの画面写真のコーディングを比べたいので、ここではSystem.Windows.MediaとSystem.Drawingが同居できるWPFアプリケーションを使って解説を勧めます。
  • #18 現在WPFを使って作成しているコンテンツを将来MAUI対応にしたいと計画している場合は、System.Drawing名前空間で行っている描画部分をMicrosoft.Maui.Graphics名前空間に変更しておくことをお勧めします。同じコードで画面写真の.NET7 統合プラットフォームのハードルの低さを説明したいので、まずは、WPFデスクトップNativeアプリを作成します。
  • #19 サンプルとして、WPFのSystem.Drawing名前空間を使った画面写真をSystem.Windows.Media名前空間での画面写真に置き換えます。
  • #20 デザインはBlendでも行えます。Blendでプロジェクトを開きます。解説のためMauiアプリのテンプレートにあるbotの画像を拝借して同じような画面を作ってます。画像はsvgなのでMethod DrawでPNGにエクスポートしておきます。BlendのツールボックスからImageを挿入してpngを指定してプロパティでビルドアクションをリソースにしておいてから実行します。
  • #21 ボタンのところのコードをこんな感じで作成します。これはSystem.Windows.Media名前空間を使って画面写真を撮るコードになります。ビットマップにコントロール(this)を描いてます。コントロールが描きこまれたビットマップをエンコードしてファイルに出力しています。
  • #22 もう一つボタンを作ってSystem.Drawingを作ります。 【クリック】同じようにビットマップを作成していますが、ポイントはシステムの値を使った矩形の設定とそれをグラフィックに書き直しているところです。普通はこれを描画プラットフォームがディスプレイに出力してくれています。 【クリック】これをグラフィックスに出力します。
  • #23 それではMauiではどのように描くかという話になります。マルチプラットフォームアプリケーションで使うことで威力を発揮するMicrosoft.Maui.Graphics名前空間なのですが、.NET 7でさらに強化されたのがMAUI統合プラットフォームです。
  • #24 同じコードで画面写真の.NET7 統合プラットフォームのハードルの低さを説明するので、ここでは同じ構造のMAUIアプリケーションと考えてください。
  • #25 .NET 7のMicrosoft.Maui.Graphics.Media名前空間のScreenshotクラスのCaptureAsyncメソッドを使った画面写真を撮るアプリを作成するので、Platform APIを使うための稼働条件になりますが、Visual Studio 2022 Version 17.4以降が必要になります。 【クリック】17.4以降にはMAUIアプリケーションの.NET 7のテンプレートが入ってますのでVisual Studio Installerから更新してください。 【クリック】既定で.NET マルチプラットフォームアプリのUI開発がチェックされていると思いますので、確認して必要に応じてインストールしてください。 【クリック】実機を持っていないので今回はMacの話はしません。また、Visual StudioでArmをサポートしましたが、MAUIアプリは作れません。Armで開発している方はもうしばらく待ちましょう。
  • #26 Visual Studio 2022 17.4のMAUIプロジェクト作成時には、フレームワークとして.NET 7.0を選択することができます。 【クリック】作成されたプロジェクトのプロジェクトファイルでは、ターゲットフレームワークがプロパティ定義されていることを確認できます。
  • #27 ここでは3つ目のボタンを作っています。まず非マルチプラットフォームなコードで概要を説明します。 【クリック】クリックするとタスクが走ってその中でTakeScreenShotというタスクを呼んで結果をファイルに出力しています。これが基本のプログラムになります。 【クリック】ただし、これは出力するファイルの場所が非マルチプラットフォームです。ポイントは画面写真を撮るだけなら「Screenshot.CaptureAsync()」一行で終わりということです。なぜこんなことができるのかというとScreenshotオブジェクトを提供する「Microsoft.Maui.Media」名前空間を含む非常に多くの統合されたプラットフォームAPIが用意されています(巻末リンク参照)。本日は統合されたプラットフォームAPIの中でファイルディレクトリの取得についてお話しします。
  • #28 このMainPage.xaml.csはマルチプラットフォームのコードの部分ですので、WindowsとAndroidをディレクティブで分けてコーディングします。理由はSystem.EnvironmentのGetEnvironmentVariableを使う際に、環境変数名が違うのでこのようなコードの登場シーンはそれほど多くはありません。【読む】 【クリック】しかし、これでは保守性がよくありません。そこで、マルチプラットフォームのコードはこのようなアプローチを行います。まずプラットフォームに依存するコードを定義する場所があるのでそれを使って同じプロパティでマルチプラットフォームのコード側に公開するという手順です。これがMauiProgram.ExternalDirsという部分になります。取得できたらファイルに書き込むという感じです。この部分のコーディングは後述します。 【クリック】このタスクはクリック時に呼ばれます。その後、パーミッションをチェックして宣言された機能の許可をユーザーから取得しているかどうかを確認します。WriteとReadを確認しています。後述しますがWriteを許可されるとReadも許可されます。
  • #29 こちらはAndroidの環境変数です。いずれも/data/user/0になっており、ユーザー領域のパスを取得できます。 【クリック】androidのアプリケーションはその端末のユーザーではありませんのでsdカードなどに書き込みを行っても書き込みはできますが、ファイルを見ることができません。 【クリック】これはadbコマンド(Android Debug Bridge)を使って確認することができます。
  • #30 そのため、MAUIアプリのプロジェクトはプラットフォームに依存した部分をコーディングする個所と共通のロジックを定義する個所が用意されています。プロジェクトのPlatforms\Android\MainApplication.csがプラットフォームに依存した部分をコーディングする個所で、ルートのMauiPrograms.csが共通のロジックを定義する個所です。プラットフォームのMainActivityに関しては後述します。この部分では汎用ホストを使っていますのでサービス化することもできますが、いったんプロパティで共有します。サービス化する方法は後述します。 【クリック】Windows側はプラットフォーム固有のところでOneDriveとTMPを入れてあって、使う側でLinqのLastOrDefaultを使ってユーザー領域のTempフォルダを取得しています。 【クリック】Android側は、プラットフォーム固有のところで外部ストレージのキャッシュ領域を取得しています。こちらはひとつしか入れてないですが、共通のロジックとして使う側で同じようにLinqのLastOrDefaultを使っています。
  • #31 コード側は「AndroidX.Core.App.ActivityCompat.GetExternalCacheDirs」というコンパーチブル関数を使えばJavaのファイルオブジェクトからキャッシュディレクトリの文字列を受け取ることができます。AndroidXはバージョンに依存しない汎用の名前空間ですのでそれを使います。 【クリック】ActivityCompat名前空間はContextCompatのサブセットで、ファイル操作やカメラ操作などアプリケーションのアクティビティに関する部分を制御します。 【クリック】マルチプラットフォームのMauiProgram.csではコンストラクタで受け取って外に公開しています。
  • #32 そうすると、先ほどお見せしたMainPage.xaml.csでMauiProgram.ExternalDirs.LastOrDefault()で文字列を取得することができます。 【クリック】ディレクティブを使ってやる方法はこんな感じの記述になります。マルチプラットフォーム側で環境変数を使う必要がある場合は、ディレクティブを使う必要があります。このサンプルでは使っていません。 【クリック】そうすると起動時にアクセス許可を端末に求めるようになります。ではこの確認画面について少々深堀します。
  • #33 このパーミッションを必要とするアクティビティに関する事柄は、プラットフォームに依存するのでMainActivity.csに定義します。
  • #34 Windows 8と同じようにAndroid 6.0 MarshmallowはAppコンテナの概念を採用しました。つまり、OSのサブセットであるシングルトンのインスタンスをアプリケーションに貸し出すという考え方です。これには、アプリ側でOS側のどの機能を貸し出してもらいたいかをインストール時に宣言する必要があります。そのため、その手続きには3つのステップがあります。最初にアプリケーションが使用するOSの機能の宣言、その機能についてユーザーの許可を得ているかの確認、確認の結果によってどのように挙動させたいかという3つのステップです。 そのコーディングを解説します。
  • #35 既定でマニフェストにはネットワークとインターネットは設定されていますのでandroid.permission.WRITE_EXTERNAL_STORAGEをuses-permissionに追加します。
  • #36 まず、デバイスのAPIがM以上(Marshmallow以上)であることを確認しています。 【クリック】次にAndroidManifest.xmlにアプリが機能を使用することを宣言してあるかどうか確認しています。 【クリック】最後に許可を得ているかどうか確認しています。 【クリック】得ていない場合は許可を得るようにしています。Platform.Initで初期化してからリクエストを出すと確認のダイアログをユーザーに提示することができます。また、Task.FromResultで結果を返さないタスクを実行しています。
  • #37 ここでは処理を書いていませんが、画面のボタンを非活性化するためのオブジェクトを用意するなり、ユーザーにアプリケーションの一部の機能が使えないことを通知するなりのアプリケーションの挙動に関する処理を定義します。
  • #38 実際に許可が得られると、アプリ情報で確認することができます。また、アクティビティはアプリケーションの稼働環境に依存しますので、この資料の巻末のリンクにも貼ってありますが、AndroidプラットフォームのバージョンとSDKの対応、バージョンコードとプラットフォームにおける機能の特長をまとめたページを参照してください。
  • #39 MAUIアプリも含めて.NETのアプリケーションは汎用ホストを使います。汎用ホストはサービスを後から追加してアプリケーションの環境をカスタマイズすることができます。そのためアプリケーションのスコープやセッションのスコープでライフサイクルをコントロールしたい値やオブジェクトをアプリケーション起動時に用意してやることができます。特にチーム開発を行う場合は、各ビューのプログラマ任せにするのではなく、アプリケーションのサービスを開発リーダーが設計することをお勧めします。 【クリック】プラットフォーム依存の定義を記述する場所とマルチプラットフォーム共通の定義を記述する場所があることをお話しした通りですが、マルチプラットフォーム共通の定義を記述する場所が、アプリケーション起動時に汎用ホストを使ってサービスを組み込む場所になります。このMauiProgramクラスでパブリックなフィールドを定義していましたがコメントアウトし、汎用ホストにサービスを組み込みます。MAUIアプリの場合はサービスを使用するビューもここで登録しておくことでビューのスコープとビューが使うサービスのスコープを合わせることも、意図的にスコープを変えることもできます。これはアプリケーションの設計に依存しますので適宜設定してください。巻末にスコープのドキュメントへのリンクを用意しています。ここではAddSingletonでアプリケーションレベルで使うように設定しています。ここでは解説の都合上ディレクトリサービスをMauiProgram.csで指定していますが、ビューで使うという意味でViewModelを使ってもいいですしアプリケーションの基盤としてフレームワーク化しても良いかと思います。 【クリック】DirservicesクラスのExternalDirsフィールドにセットしていますので、MainPage側ではコンストラクタで受け取れるので、LastOrDefaultを使ってビューのグローバル変数に入れてやることでTaskで指定していた部分をコメントアウトして使うことができます。