Power Appsにおける画像の扱いについて

**新年一発目はPower Apps。**

Power Appsアプリは画像を扱えますが、作り込む前に把握しておかないと面倒な点が幾つかあります。
この記事ではそれら画像を扱う際のTipsや注意点を書いていきます。

#Power Appsで画像を扱えるコントロール
画像を扱えるのコントロールは以下の三つです。

**[画像の追加](https://docs.microsoft.com/ja-jp/powerapps/maker/canvas-apps/controls/control-add-picture)**
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/313502/99228cf5-1ffe-5519-8f15-b4bec80d0d42.png)

**[カメラ](https://docs.microsoft.com/ja-jp/powerapps/maker/canvas-apps/controls/control-camera)**
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/313502/461ba8c1-1365-b0ed-cb58-08440bb1759d.png)

**[画像](https://docs.microsoft.com/ja-jp/powerapps/maker/canvas-apps/controls/control-image)**
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/313502/7a6902b4-9d4b-b47b-4c17-0889cf494a75.png)

#Power Appsにおける画像
Power Appsにおいて、画像は**`画像`というデータ型で扱われます。**
[キャンバスアプリのデータ型](https://docs.microsoft.com/ja-jp/powerapps/maker/canvas-apps/functions/data-types#text-hyperlink-image-and-media)

この`画像`データ型を使える形式は以下の五つです。

|形式|参照先|
|:-:|:-:|
|アプリのリソースに追加した画像|appres://resources/ImageFileName|
|コントロールで取り込んだ画像|appres://blobmanager/xxx.../n|
|Web上の画像へのリンク|https://northwindtraders.com/logo.jpg|
|[データURIスキームを使用するインラインイメージ](https://en.wikipedia.org/wiki/Data_URI_scheme)|data:image/png;base64,xxxxx....   |
|Binary データ|![alt](https://pbs.twimg.com/profile_images/998209664671072257/KdESXw98_bigger.jpg)|

##形式 : URI
Power Appsは基本的に**[URIテキスト文字列](https://en.wikipedia.org/wiki/Uniform_Resource_Identifier)を利用したURI参照を行って画像を読み出します。**

該当するのはこの三つです。

|形式|参照先|
|:-:|:-:|
|アプリのリソースに追加した画像|appres://resources/ImageFileName|
|コントロールで取り込んだ画像|appres://blobmanager/xxx.../x|
|Web上の画像へのリンク|https://northwindtraders.com/logo.jpg|

データは**[画像コントロール](https://docs.microsoft.com/ja-jp/powerapps/maker/canvas-apps/controls/control-image)に画像が表示されるタイミングで取得される**ため、不必要な画像データを取得することなくアプリを構築できます。

下の例では、データ テーブルで選択した国旗だけが[画像コントロール](https://docs.microsoft.com/ja-jp/powerapps/maker/canvas-apps/controls/control-image)に表示されています。
**それ以外の画像は他のアイテムを選択するまで読み込まれません。**
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/313502/e0a6cafa-f4c0-e045-44f5-c4f81e1ee1d7.png)

##形式 : データURIスキームを使用するインラインイメージ
[画像コントロール](https://docs.microsoft.com/ja-jp/powerapps/maker/canvas-apps/controls/control-image)は、**文字列で記された[データURIスキームを使用するインラインイメージ](https://en.wikipedia.org/wiki/Data_URI_scheme)を表示する事もできます。**
`Image`プロパティに文字列として設定するだけです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/313502/a5083c4e-ea75-f54e-da55-219a16da9d3d.png)

##Binary データ
**画像そのものです。**
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/313502/964f99df-794d-de02-4409-81aa7761c377.png)

Binaryの取り込みは即ち画像自体の取り込みなので、**表示非表示に関係なくファイルサイズに応じてアプリに負荷が掛かります。**

これはアプリのリソースに追加した画像も同様で、**下手に使うとアプリ起動時の読み込みが遅くなります。**

#[画像の追加コントロール](https://docs.microsoft.com/ja-jp/powerapps/maker/canvas-apps/controls/control-add-picture)
[画像の追加コントロール](https://docs.microsoft.com/ja-jp/powerapps/maker/canvas-apps/controls/control-add-picture)を挿入すると、**`AddMediaButton`コントロール**と**`UploadedImage`コントロール**で構成された**グループ`AddMediaWithImage`**がスクリーンに追加されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/313502/9440bf89-7df8-db3f-bee5-66326bcf4884.png)

この内、**画像の追加コントロール特有の物は`AddMediaButton`だけです。**
`UploadedImage`はただの画像コントロールなので消しても問題ありません。

##AddMediaButtonの挙動
`AddMediaButton`は**特殊**なボタンです。
`AddMediaButton`の`OnSelect`プロパティは初期値が`false`ですが、クリックすると画像を取り込めます。

つまり`OnSelect`の内容に関係なく、**画像の取り込みだけに使える専用のボタンです。**

##画像の格納場所
画像は**`AddMediaButton`に紐付いた`appres://blobmanager/path/n`に格納されます。**
複数枚の画像を格納できず、取り込みごとに上書きです。

格納先の`appres://blobmanager/path/n`は各`AddMediaButton`の`AddMediaButton.Media`から**文字列として読み出せます。**

**アプリ起動時の`AddMediaButton.Media`はBlankで、各コントロールごとに最初に画像を取り込んだ際に初めて`appres://blobmanager/path/n`が割り当てられます。**

また、`appres://blobmanager/path/n`の`path`は同一アプリ内であれば共通です。
注意点として`path`はアプリ起動毎に変わる為、アプリにハードコーディングする事はできません。

末尾の`n`は画像の取り込みごとに+1されます。

|取込回数|appres://blobmanager|
|:-:|:-:|
|1回目|appres://blobmanager/path/1|
|2回目|appres://blobmanager/path/2|
|3回目|appres://blobmanager/path/3|

このURIは**`AddMediaButton.Media`から読み出せる最新のURIのみ有効**で、直接上書済みのn番台URIを指定しても`Blank`が返ってきます。

##複数の[画像の追加コントロール](https://docs.microsoft.com/ja-jp/powerapps/maker/canvas-apps/controls/control-add-picture)を配置した場合
複数の[画像の追加コントロール](https://docs.microsoft.com/ja-jp/powerapps/maker/canvas-apps/controls/control-add-picture)を配置した場合、**コントロールごとに格納場所が区別されます。**

例えば3枚画像を取り込んだ状況で、更に以下の三つの`AddMediaButton`コントロールで順番に画像を取り込んだ場合、`appres://blobmanager`のURIは以下のようになります。

|コントロール名|appres://blobmanager|
|:-:|:-:|
|AddMediaButton1|appres://blobmanager/path/4|
|AddMediaButton2|appres://blobmanager/path/5|
|AddMediaButton3|appres://blobmanager/path/6|

この時、`AddMediaButton.Media`から読み出せるURIは各コントロールごとに有効です。
つまり**`AddMediaButton1`で新しい画像を取り込んでも他の`AddMediaButton`のURIは有効のままです。**

#[カメラコントロール](https://docs.microsoft.com/ja-jp/powerapps/maker/canvas-apps/controls/control-camera)
[カメラコントロール](https://docs.microsoft.com/ja-jp/powerapps/maker/canvas-apps/controls/control-camera)を挿入すると、`Camera`コントロールがスクリーンに追加されます。

##Cameraの挙動
`Camera`は画像の表示とボタンが一体化した**特殊**なコントロールです。
`OnSelect`プロパティの初期値は`false`ですが、クリックするとカメラに表示されている画像を`Camera.Photo`に格納します。

これも`OnSelect`の内容に関係なく、**画像の取り込みだけに使える専用のボタンです。**

##画像の格納場所
画像は**[カメラコントロール](https://docs.microsoft.com/ja-jp/powerapps/maker/canvas-apps/controls/control-camera)ごとの`Camera.Photo`に格納されます。**
複数枚の画像は格納できず、撮影ごとに上書きされます。

[画像の追加コントロール](https://docs.microsoft.com/ja-jp/powerapps/maker/canvas-apps/controls/control-add-picture)で使った`AddMediaButton`の`AddMediaButton.Media`の中身は`appres://blobmanager/path/n`という文字列でしたが、**`Camera.Photo`にはBinaryの画像が格納されています。**

##複数の[カメラコントロール](https://docs.microsoft.com/ja-jp/powerapps/maker/canvas-apps/controls/control-camera)を配置した場合
**配置されたコントロールごとに別々の`Camera.Photo`に画像が格納されます。**
`Camera.Photo`は`コントロール名.Photo`なので、画像もコントロールごとに区別されます。

#Power Appsで画像を扱う際のTips・注意点
##URIとBinary
画像は**URIとBinaryの場合で扱いが異なります。**

###URI
URIは[画像コントロール](https://docs.microsoft.com/ja-jp/powerapps/maker/canvas-apps/controls/control-image)の`Image`プロパティに設定し、**画像として読み込まれないとBinaryになりません。**

例えば[画像の追加コントロール](https://docs.microsoft.com/ja-jp/powerapps/maker/canvas-apps/controls/control-add-picture)の`AddMediaButton.Media`を直接`Collect`した場合、中身はURIの文字列なので`appres://blobmanager/path/n`だけがコレクションに格納される。

```C#:画像の取り込みNG
ClearCollect(画像取り込み,AddMediaButton1.Media) = {Value:"appres://blobmanager/path/n"}
```

URIや[データURIスキームを使用するインラインイメージ](https://en.wikipedia.org/wiki/Data_URI_scheme)の画像をCollectする場合、**必ず[画像コントロール](https://docs.microsoft.com/ja-jp/powerapps/maker/canvas-apps/controls/control-image)を介してCollectする必要があります。**

[画像の追加コントロール](https://docs.microsoft.com/ja-jp/powerapps/maker/canvas-apps/controls/control-add-picture)であれば、`AddMediaButton`の`OnChange`にグループ`AddMediaWithImage`でセットになっている`UploadedImage.Image`とCollectを組み合わせる事で楽にCollectできます。

```C#:AddMediaButton.OnChange
ClearCollect(取り込んだ画像,UploadedImage.Image) = {Value:画像Binary}
```

また[画像コントロール](https://docs.microsoft.com/ja-jp/powerapps/maker/canvas-apps/controls/control-image)の`Visible`を`false`にしても、**コントロールが非表示なだけで画像は読み込まれています。**

###Binary
[カメラコントロール](https://docs.microsoft.com/ja-jp/powerapps/maker/canvas-apps/controls/control-camera)の`Camera.Photo`のように**既にImageが表示されていて、それを保存している場合はBinaryです。**

画像が表示されているという事は、読み込み済みの画像であるという事です。

##Binaryを[データURIスキームを使用するインラインイメージ](https://en.wikipedia.org/wiki/Data_URI_scheme)に変換する
画像のBinaryに対し、**[JSON関数](https://docs.microsoft.com/ja-jp/powerapps/maker/canvas-apps/functions/function-json)を使うとbase64のDataURI文字列に変換できます。**

これは画像をPower Automateに渡したり、画像ファイルをサポートしていないデータソースに画像を保存するのにとても役立ちます。

但し[画像コントロール](https://docs.microsoft.com/ja-jp/powerapps/maker/canvas-apps/controls/control-image).Imageを直接[JSON関数](https://docs.microsoft.com/ja-jp/powerapps/maker/canvas-apps/functions/function-json)の引数にするとエラーになります。

これは一旦コレクションか変数に格納してあげれば解決します。

```C#:画像のbase64DataURI化
Set( ImageJSON, JSON( Image.Image, JSONFormat.IncludeBinaryData ) )
```

##[JSON関数](https://docs.microsoft.com/ja-jp/powerapps/maker/canvas-apps/functions/function-json)の戻り値は文字列
[JSON関数](https://docs.microsoft.com/ja-jp/powerapps/maker/canvas-apps/functions/function-json)の戻り値は文字列なので、`"`で括られている。

`"`で括られてたままではPower Appsは勿論のこと、Power Automateで画像として扱う際にも支障が出るので`"`を外してDataURI文字列にしてあげる必要があります。

これは[JSON関数](https://docs.microsoft.com/ja-jp/powerapps/maker/canvas-apps/functions/function-json)や、その戻り値を[Substitute関数](https://docs.microsoft.com/ja-jp/powerapps/maker/canvas-apps/functions/function-replace-substitute)でラップして、`"`を取り除けば解決します。

```C#:"を外す
Substitute(ImageJSON,"""","")
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/313502/0cbd2abf-14bb-fea0-3623-e8e58ae2343e.png)

#まとめ
- **URIとBinaryの違いに気を付ける**
    - Power Appsのアプリは「使う時まで読み込まない」思想で動いています。
    - 使う時にBinaryにしてあげましょう。
- **画像の場所を意識する**
    - 「今どこにあるのか」「どこに格納されたか」を把握していれば、行うべき処理も見えてきます。
- **[JSON関数](https://docs.microsoft.com/ja-jp/powerapps/maker/canvas-apps/functions/function-json)は便利**
    - 戻り値の`"`をどうにかしてしまえば可能性は無限大です。

この記事は以下の勉強会で発表された問題を解決した結果、生まれました。
[PowerAppsでローコーディングな勉強会 #11 寄って集ってみんなで問題を解決する会](https://powerapps.connpass.com/event/159134/)

皆さんも色々と試行錯誤してみてください。