コルネの進捗や備忘録が記されたなにか

進捗や成果物や備忘録てきななにかを雑に更新していきます。

Power Appsで自作のスクロールバーを作成してみよう

はじめに

Power Appsのコンポーネントでこんな感じのカレンダービューを作成してみました。

その過程でスクロールバーも作成してので、今回はPower Appsでのスクロールバーの作成方法について纏めてみます。

カレンダー表示しているところはまた今度!

自作のスクロールバーを作成できるとなにが嬉しいの?

Power Appsでスクロール可能な画面を作成したい場合はこの「スクロール可能」を選択する必要があります。

この画面を追加すると、Canvas1というコントロールがあります。

このCanvas内にコントロールを追加していくことでスクロール可能な画面を作成していくわけですが、コンポーネント内にはこのコントロールは追加できません

ではどうやってスクロール可能なコンポーネントを作成するか?というと以下2点があるかと思います。

  • ギャラリーを追加し、ギャラリー内にコントロールを追加する
  • スクロールバーを自作する

ギャラリーを使う方法が一番簡単な方法ではありますが、これだと特定のコントロールの組み合わせのループになってしまいます。

自由に配置したコントロールをスクロールによって表示させたい場合はスクロールバーを自作する必要があるというわけです。

後は一部の人に対する需要ですが、SVGで描画した際にスクロールバー自作できると色々捗りますね。

スクロールバーを作成する

スクロールバーの構成

スクロールバーは「スライダー」とアイコンの「四角形」の2つで構成されています。

完成イメージは冒頭のツイート内の動画をご確認ください。

スライダーだけだとスクロールバーっぽくないので、四角形を上に被せている感じです。

スライダーの設定

スクロールバーの肝となる部分です。

縦方向のスクロールバーを作成したいのでレイアウトは"縦"に設定しています。

Layout

Layout.Vertical

スクロールバーの位置やサイズは自由に設定してください。
今回は
X: Parent.Width - Self.Width
Y: 0
Width: 20
Height: Parent.Height
としています。
ご参考までに。

スライダーの数値によってスクロールが今どこまで行われているか判断させたいので

Min

0

Max

Self.Height

にそれぞれ設定します。

あとはスライダーの罫線やレール、ハンドルの色を透明に設定してあげます。
非表示にしたらスライダー触れないので透明にしてくださいね。

デフォルト時の色だけでなく、ホバー時や選択時の色などの設定があるので忘れずに透明にしましょう。

透明にするには

RGBA(0, 0, 0, 0)

のようにアルファ値(第4引数)を0に設定してください。

最後に値の表示(ShowValue)をfalseに設定して値が表示されないようにします。

これでスライダーの設定は完了です。

簡単ですね。

実はスクロールバーを作るのはたいして難しくなくって、どちらかというとこの値を使って座標などの値を変化させる方が面倒だったりします。

四角形の設定

これがスクロールバーのバーの箇所になります。

実際はスライダーを変更させるわけですので、四角形はスライダーの下にくるように配置してください。

バーのサイズは、幅はスライダーに合わせたいので、

Width

ScrollBarSlider.Width

としています。

高さは適当に

Height

Parent.Height / 6.4

としています。

もっとページの高さによってちゃんと計算させた方がいいかもしれませんが、まぁこれでもそれっぽくなるので。
一点注意するとすれば、固定値はやめましょう。

どこかでも解説したような気がしますが、コンポーネントを作成する際はなるべく他のコントロールの値などを相対的に参照するようにして固定値(マジックナンバー)を設定しないほうがよいです。

理由は、コンポーネントはどのようなサイズでそのアプリに組み込まれることになるかわからないので、ある程度アプリ側でコンポーネントのサイズを変更されてもレイアウトが崩れないようにするためです。

Y座標の設定ですが、これがちょっと厄介です。

スライダーを縦に設定すると最上部が最大値で、最下部が最小値になります。

つまり、スクロールバーを下に持っていけばもっていくほど値が小さくなる。というわけですね。

なのでY座標はこれを考慮して設定する必要があります。

これを考慮した式を設定すると

Parent.Height - ScrollBarSlider.Value

となります。

ただし、これではスクロールバーのハンドルの大きさと、バーの高さに差異がある影響で下にスクロールするほど、スライダーの値と四角形の座標のずれが大きくなってしまいます。

このずれを補正するために

ScrollBarSlider.Max * (ScrollBarSlider.Height - Self.Height)

上記のような式で割ってあげます。
纏めると以下のような式になります。

Y

(Parent.Height - ScrollBarSlider.Value) / ScrollBarSlider.Max * (Parent.Height - Self.Height)

これでいい感じに補正が効いてくれるはずです。

以上でスクロールバーの自作は完成です!

あとはこの値を用いてスクロールさせる方法を説明したいと思います。

自作したスクロールバーのスクロール値をコントロールに適用させる

スクロールバーをスクロールさせたらコントロールたちをそれによって移動させたいので、各コントロールのY座標に式を追加させます。

スクロール領域の言葉を以下のように定義しています。

スクロールによってコントロールが動くように見せたければ、下にスクロールさせればコントロールを上に動かして、スクロールを上に動かせばコントロールを下に動かす。というような処理をすればいいですね。

次に考慮しなければいけないのは例えばスクロールバーの値が1動いたら、画面上ではどれだけコントロールを動かすか?というスクロールによるコントロール移動の倍率です。
これは

  • スクロール可能な領域の最大高さ
  • スクロール可能な領域の実際の高さ
  • スクロールバーの値
  • スクロールバーの最大値

の4つの要素を使えば算出できます。

これらの要素を考慮して式を作成すると以下のようになります。

[配置したい座標] - ([スクロール可能な領域の最大高さ] - [スクロール可能な領域の実際の高さ]) * ([スクロール可能な領域の実際の高さ] - ScrollBarSlider.Value) / ScrollBarSlider.Max

スクロールバーのバーの座標補正にスクロール領域の倍率を掛けているだけですね。
(Parent.Height - Self.Height)による反転は不要なので式内に追加していないです。

ちなみにSVGでスクロールバーの値を適用するときは

viewBox='0 "& ([スクロール可能な領域の最大高さ] - Self.Height) * (Self.Height - ScrollBarSlider.Value) / ScrollBarSlider.Max &" "& Self.Width & " " & Self.Height & "'

みたいにすればいいですね。

おまけにもう一つ。
GalleryのTemplateSize = 0にしたギャラリーにスクロールバーを適用するならこんな感じです。

ThisItem.y - ([スクロール可能な領域の最大高さ] - [スクロール可能な領域の実際の高さ]) * ([スクロール可能な領域の実際の高さ] - ScrollBarSlider.Value) / ScrollBarSlider.Max - (ThisItem.index - 1)

最後の箇所の

 - (ThisItem.index - 1)

この部分はTemplateSize = 0にしたときのずれの補正ですね。

TemplateSize = 0に設定しても実際にはTemplateSize = 1になってしまうので微妙にずれが発生しちゃうのでその補正です。

おわりに

以上、Power Appsでスクロールバーを自作する方法でした。
理解しちゃえば(数学さえ理解すれば)そこまで難しい処理じゃないかと思います。

コンポーネントは市民開発者すべてが理解し管理できるようになるのではなく、一部の開発者が開発・運用・管理をして展開できればよいと私は考えているので、これで実運用を行うのも選択肢としてはありかと思います。

このやり方をマスターするとアプリ開発の幅が広がると思いますので、皆さまも是非お試しくださいー

ところどころ説明端折っちゃった箇所もありますので、質問などあればお気軽にコメントなどでお願いします。

次はこれを使って作成したカスタムカレンダーコントロールの解説を行いたいですね。

【2022/09/01検証】Power AppsのOffice365Users.SearchUserV2アクションのパラメータが変更されている

はじめに

Power AppsのOffice 365 UsersコネクタのSearchUserV2アクションのパラメータが変更されていました。

docs.microsoft.com

これによりこれまでと同じ式を設定しても動作が異なる。などの事象が発生することを確認しましたので検証結果を記載します。

記載内容に誤りがある場合は、コメントなどご指摘いただけますと幸いです。

また、この変更により既存アプリに直ちに影響があるわけでは(多分)ないと思います。
ただ念のため動作確認を行うことをおすすめします。

この記事はこの変更とこの変更による問題点を多くの方に共有するために纏めました。
Ideasに修正要望を上げる予定ですので、記載したらリンクをまたこちらに記載します。

Ideasに投稿しました。
よろしければVoteをお願いします。
Voteの多いものほど対応してもらえます。

powerusers.microsoft.com

SearchUserV2の変更内容

SearchUserV2ですが、公式のリファレンスをみてみるとパラメータに新規でisSearchTermRequiredが追加され、skipTokenが削除されています。

実際にPower Apps上の画面でみてみると、isSearchTermRequiredが確かに追加されています。
ただ、skipTokenはこれを見る限りでは設定できそうに見えますね。

isSearchTermRequiredパラメータ

isSearchTermRequiredパラメータは、リファレンスの説明にもある通り検索語句が空の場合に結果を返すかどうかを指定するパラメータです。

リファレンスには「Yes」か「No」を設定するように書いていますが、正しくは「true」か「false」ですね。

Office365ユーザー.SearchUserV2({isSearchTermRequired: true}).value

に設定するとsearchTermが設定されていないのでなにも返されません。

Office365ユーザー.SearchUserV2({searchTerm: "",isSearchTermRequired: true}).value

これも上の式と同様searchTermが空なのでなにもなにも返ってこないですね。

これらを例えばコンボボックスのItemsなどに設定すると、この式はなにも返さないのでユーザの検索ができなくなってしまいます。
注意しましょう。

Office365ユーザー.SearchUserV2({isSearchTermRequired: false}).value

この場合は、フィルタリングがされないのでsearchTermが空でも検索結果が返ってきます。

これがこれまでの

// これまでの設定
Office365ユーザー.SearchUserV2().value

この式と同じ結果が得られますね。

さてこのisSearchTermRequiredはどうやらデフォルトtrueなようです。

これがどういうことかというと、これまでの書き方と同様に

Office365ユーザー.SearchUserV2().value

Office365ユーザー.SearchUserV2({searchTerm: TextInput1.Text}).value

みたいにかいてしまうと、これまでの動作と新規でアプリを作成したときの動作で差異が発生するという事象が起きてしまいます。

うーんこれはよくないですね。。。

skipTokenパラメータ

skipTokenパラメータはドキュメントから(なぜか)削除されちゃっていますが、こちら現在でも設定でき、また動作します。

ちなみにskipTokenの設定方法について纏めているサイトが少ないので、記載しておくと

Set(
    varSkipToken,
    With(
        {odataNextLink: Office365ユーザー.SearchUserV2({isSearchTermRequired: false}).'@odata.nextLink'},
        Mid(odataNextLink, Find("skipToken=", odataNextLink) + Len("skipToken="))
    )
)

みたいな式で取得したvarSkipTokenを設定すればよいです。

既存アプリへの影響

この変更がいつなされたのか正直わからないですが、8/29頃に作成したアプリの中でSearchUserV2アクションを実行しているアプリがあったので、動作確認をしてみました。

結果として、このアプリはisSearchTermRequiredパラメータが存在せず、また設定していなくともユーザの検索結果を取得することができました。

ただし、アプリのデータからOffice365Usersコネクタを削除し、再度Office365Uersコネクタを追加する。ということを行い、コネクタのバージョンを上げるような操作を行うと、SearchUserV2アクションにisSearchTermRequiredパラメータが設定できるようになっており、またisSearchTermRequiredパラメータを設定していないと、ユーザの検索結果が取得できませんでした。

なので、既存のアプリには今すぐは(多分)影響はないですが、何かの拍子でアプリ内で利用しているコネクタのバージョンが更新されると、これまでとは異なる動作をする恐れがある。

ということになるかと思います。

ちなみにこのコネクタのバージョンってどこで定義されているんですかね?
コード化して探してみたんですけど、わからなかったです。。。(見落としている可能性大)

おわりに

今回のこの変更はなぜこのようにしたのか?設計の意図が全然わかんないな。と個人的に思っています。
パラメータの内容を変えるのであれば、SearchUserV3などのようにバージョン上げるべきでは??と思いますし、最低限既存の式と最新の式とで同じ式であれば同じ動作とするべきだと思っています。

これのせいで不要な混乱が発生する恐れを危惧しています。

最初にも記載しましたが、この件をIdeasに投稿しました。
よろしければVoteをお願いします。
Voteの多いものほど対応してもらえます。

powerusers.microsoft.com

JPPGBでゲーム作成コンテストをやりたい という構想

注意!!!

この記事は技術的な内容は含みません。
こんなイベント開催したいなーって思いを殴り書きで思いのままに記載しています。

グループの紹介

Japan Power Platform Game Builders 通称 JPPGB というコミュニティを主催しています。

jppgb.connpass.com

まだメンバーでない方は、是非メンバー登録していただけると幸いです!

このコミュニティでは本来業務アプリを作成するはずのPower Platformでゲームを作成してみよう!
といったことが主軸のちょっと他のPowerなコミュニティと比較すると風変りなコミュニティとなっていますw

Power Platformでゲーム作成している方、ゲーム作成に興味のある方、そんな人たちを見てみたい方。どんな方も大歓迎です!

緩く楽しんでいきましょう!

ゲーム作成コンテストやりたい!

さて、そんなJPPGBですがいつかゲーム作成コンテストとかやってみたい!と(勝手に)考えています。

Twitterでそんな呟きをしてみたところAkiraさんが乗っかってくれたのと、前回のイベントで軽くこのことお話してみたら受けがよさそうでしたのでちょっと真面目に考えてみよう。

といったことで、こんなイベントにしたいなーな構想をこのブログにメモ書きがてら纏めてみようと思います。

こんなのは?など皆様のアイディアも募集しています!!

イベント構想

イベント開催目的

まず、正直にいいますとこのイベントをやりたい!と思ったきっかけは面白そうだからが一番です。

なのでこのイベントは、参加者皆さんで楽しめる。ということを主軸にイベントを実施していきたいな。と思います。

もちろん真面目な開催理由も大きく分けて2つあります。

1つ目が、スキルを磨けるよ。ということです。

前回のイベントでもお話ししていましたが、Power Platformでのゲーム開発の経験を、直接業務アプリ開発に生かす。というのは少し難しいですが、ゲーム開発をしていく中で発生したエラーやパフォーマンス改善のために培ったノウハウなんかは、業務アプリ開発でもそのまま生かすことができます。

また、ゲーム開発はものによっては少しテクニカルな処理を挟まなくてはいけないことがしばしばありますが、そこで普段使っていなかったような関数やコネクタの利用方法なんかを発見することもできます。

2つ目が、(ちょっと上からな発言になっちゃいますが)こういったゲーム開発を行う場を用意することで、ゲーム開発を行う機会を提供したい。という思いがあります。

なにかを新しく始めるのって結構手間で労力も掛かりますよね?(私はそうです。)
そんな人たちが一歩踏み出せる機会を用意できたらな。と思っています。

あとこういったコンテスト形式のイベントにすることで、多数の方に自分の作ったゲームをプレーしてもらい、フィードバックが貰える。というのもコンテスト形式のいい点かな。と考えています。

頑張って作ったのだから、誰かにプレーしてもらいたですし、評価されたいですよね。

どういった形式で開催するか?

作成したゲームの応募・公開方法

このイベントに参加してくれる人が一体何人程いるのか正直わからないですが、仮に各Power部門ごとに平均5人参加してくれた場合、4 * 5で20人の応募者がいることになります。

これを1回のイベントですべて紹介しきるというのは、時間てきに現実的ではないですね。

なので

  • ゲーム作成 & 解説資料作成期間
  • 参加者がゲームを遊んでみる期間
  • 投票期間

を設けて、投票期間終了後にイベント開催。
みたいにするのがいいんじゃないかな?と思っています。

イベントでは、応募のあったゲームの紹介 & 投票結果の発表
みないな内容になるかな?

審査方法

審査方法としては、

  • 各Power部門ごとで審査
  • ゲーム作成経験別で審査
  • ゲームのジャンルごとで審査
  • 技術面やアイディア面での審査

の4パターンがあるかな?と思っています。
どれか1つにするのではなく、これらを複合するなどしてそれぞれの部門で入賞者決める形式ですね。

審査をする人は

  • イベント参加者
  • イベント応募者
  • イベント運営者
  • イベントのスポンサー(※後で記載)

かな?
このメンバーで各部門ごとで気に入ったゲームを記載してもらうかたち。
複数投票防ぐ処理あったほうがいいかな。やるならconnpassのアカウント名記載してね。とかだろうか?

入賞者への賞や賞品とか

どうせやるなら賞品とかあったほうが盛り上がりますよね!
ということで入賞者の方にはなにか賞品を用意したいなーと思っています。

ここで問題になってくるのが、「その賞品どこから用意するの?」です。
どこから用意しようか。。。

できれば企業の方とかにスポンサーになってもらってその企業のグッズや商品券なんかを融資いただけたらなー。。。と淡い期待を抱いております。

そこでもう一つ課題として出てくるのが、どうやってスポンサー集めるか。ですね。

正直私には色々な企業にスポンサーを依頼するためのコネもないですし、スポンサーとなってくれた方への見返りも自社の宣伝ぐらいしか現状思いつかないです。

賞品の用意が壁になってきそうですね。
もう少しちゃんと考えなくては。

おわりに

とりあえずこんなこと考えているよーっていうメモ書き纏めてみました。(ちゃんとまとまってなくってすみません。。。)

このイベントに興味ある方は、反響も知りたいのでこのブログにスターつけたりコメント付けたり私に直接コンタクト取ってもらえると助かりますー。

Power Appsで小数の計算に誤差が生じる

はじめに

つい先ほど、Power Appsで小数の計算を行った際に例えば以下のように計算に誤差が生じてしまったので、簡単に原因と対策を纏めます。

発生した誤差の例)
10 + 0.1 = 10.099999999996

一応断っておくと、これは市民開発者向けに纏めた記事なので、情報系に明るい人からすると、今更そんなこと詳しく書かなくても。。。な内容になっています。
予めご了承ください。

問題の根本原因

このような小数の計算で誤差が生じる。という問題はPower Appsに限らず、他のプログラミング言語でも発生する問題です。

ではなぜ誤差が生じてしまうのか?というと、浮動小数点型の丸め誤差によってこの問題が発生します。

"浮動小数点型"とはプログラミングにおける変数のデータ型の1つで、実数を表すデータ型のことをいいます。

まず前提として、コンピュータ上では我々が普段の生活で扱っている10進数ではなく2進数で処理が行われています。

10進数は0~9の値で数値が表現され、9に1加算されると10に桁上がりが行われますね。
これは普段の生活でも利用している表現方法なのでなんとなくわかるかと思います。

2進数は、0と1の値で数値が表現されます。
10進数でいう'1'は2進数では'1'、10進数でいう'2'は2進数では'10'、10進数でいう'3'は2進数では'11'といったふうに表現されます。

なぜこのような表現方法を用いているのかというと、「コンピュータでの処理は0と1で表現されている」みたいな話はきいたことはないでしょうか?
コンピュータ上ではオフを意味する「0」とオンを意味する「1」をいくつも組み合わせることであらゆるデータを表現しているのです。

とりあえず今はコンピュータ上では2進数という0と1の組み合わせでデータが処理されているんだな。という理解で大丈夫です。

さて、この2進数ですが 小数点以下の数を正確に表現できない という問題があります。

10進数を2進数に変換する方法の詳細は割愛しますが、小数の変換の理論だけ言葉で説明すると、変換対象の小数点以下の値にひたすらに2を掛けていき、整数部分の数値を抜出ます。これを小数部が0になるまで繰り返します。

簡単に例を示すと、「0.25」という10進数の数値を2進数に変換してみます。


0.25 * 2 = 0.50
0.50 * 2 = 1.00


という計算結果から、整数部分を抜き出すと、「0」と「1」になりますね。
なので10進数の'0.25'は2進数で表すと'0.01'になる。というわけです。

このやりかたをもとに、例えば冒頭でも計算に用いていた「0.1」を2進数に変換してみましょう。


0.1 * 2 = 0.2
0.2 * 2 = 0.4
0.4 * 2 = 0.8
0.8 * 2 = 1.6
0.6 * 2 = 1.2
0.2 * 2 = 0.4
0.4 * 2 = 0.8
...


と、このように計算結果が循環してしまいます。
これにより、正確に小数の値が表現できずに、近似値を使うことによって丸め誤差が発生してしまうわけですね。

この微妙な計算誤差の積み重ねは特に金融系のシステムでは命取りなので金融系のシステムに関わっている人は必ず意識してシステム開発をしましょう。

対応策

この問題はfloat型やdouble型などの浮動小数点型を利用しなければ発生しない問題なので、int型などの整数型で計算を行うようにすればいいです。

たとえば、最初の例であれば

10 + 0.1 → 100 + 1

のように整数で計算する方法ですね。

ただ、最終的に小数で扱いたいんだ。という場合がほとんどだと思いますのでそのような場合は有効桁で値の四捨五入を行うという回避方法があります。

例えば有効桁2桁が欲しいのであれば

Round(10 + 0.1, 2)

みたいに計算させます。

おわりに

多分情報系とか他プログラミングを経験していないで、この問題にあたると「なんだこれ!?」となるかな?と思ったので簡単に纏めてみました。

だいぶかみ砕いて説明したので気になった人は調べてみるともう少し詳しい記事とか見つかるかと思います。

最後に、ここで記載した回避方法はざっくりこれでできるよ。
というやり方で、場合によっては誤差が生じる恐れもあります。

その誤差が一切許せないような厳密な計算が必要なのであれば、それはローコード/ノーコード開発ではなくプロの開発者に最低限そこの数値の計算を行うAPI開発を依頼する。や、システム開発自体を依頼する。といったことをしたほうが良いと思います。

参考までにOracle浮動小数点演算についての記事載せておきます。

浮動小数点演算について

Power Automateで数値の切捨て / 切上げ / 四捨五入を行う

はじめに

Power AppsにはRound、RoundDown、RoundUp、および Trunc関数という切捨て、切上げ、四捨五入を行う関数があるのに、なんでPower Automateにはそれと同等の関数がないんですかね...?

docs.microsoft.com

不便じゃない?と思ったので切捨て、切上げ、四捨五入を行う式を作成してみました。

ものすごくだるかったですね。

インプット値

インプット値は、切捨て、切上げ、四捨五入をする数値と少数N桁まで表示させる。ということを表す数値です。

名前 内部名
triggerBody()['number']
小数点以下 triggerBody()['number_1']

内部名 = 数式内で扱うときの動的な値です。
数式をコピペする際に、適宜置き換えてください。

四捨五入

これが一番簡単ですね。
四捨五入というより正確にいうと丸めですが。

float(
    formatNumber(triggerBody()['number'], concat('F', triggerBody()['number_1']))
)

ただ、数値のフォーマットを行っているだけですね。

F[N]で少数N桁の数値に丸めます。

F3だと少数3桁の数値に丸められますね。

詳しくはHiroさんのブログを参照。

qiita.com

その他フォーマットはこちらを参照。

docs.microsoft.com

2022/12/17 追記

コメントにて、切上げの際に桁上がりするケースが正しく動いていないとの指摘をいただきましたので修正を行いました。

切捨て/切上げともにやり方を変更し、割とシンプルなものに修正できました。

一応以前のやり方も消さずに残してはおきます。

切捨て

切捨ては判定対象となる桁の数値(例えば、小数第2位まで残すのであれば小数第3位が判定対象)を0にしたうえで、四捨五入を行えば確実に切捨てされますね。

判定対象の桁の数値を0にするには、

 {元の数値} - \frac{1}{10^{切捨てを行う桁}} * {元の数値の判定対象の数}

で計算してあげれば0にできます。
具体的な数値で表すのであれば、

  • 元の数値: 10.092
  • 切捨てを行う桁: 3

とした場合、以下となりますね。

 10.092 - \frac{1}{10^{3}} * 2 = 10.09

ただし、残念なことにPower Automateには累乗を計算する関数は用意されていません。
ドウシテ。。。

累乗を式関数だけで表現するのは少しハードルが高いので発想を変えてみましょう。

今回欲しいのは  10^{N} であり、基数(底)は10で固定です。
つまり1, 10, 100, 1000・・・みたいな数値を作成できればいいわけですね。

これを実現するためにjoin 関数range 関数をうまいこと活用します。

range関数は第一引数で指定した数値から始まる連続した数値の配列を、第二引数で指定した数値の数だけ作成してくれる関数で、join関数は配列を指定した区切り文字で結合した文字列を返してくれる関数でしたね。

range関数を用いて1~Nまでの連続した数値の配列を作成して、join関数で結合してあげれば、N桁目が1のN桁の数値が作成できます。

式にすると以下ですね。(今回NはN桁目まで保持としているので、1余計に加算されています。)

sub(
    int(
        join(
            range(
                1, 
                add(triggerBody()['number_1'], 2)
            ), 
            ''
        )
    ),
    int(
        join(
            range(
                2, 
                add(triggerBody()['number_1'], 1)
            ), 
            ''
        )
    )
)

ただしこれですと、N + 2が10を超えると上手くいきません。
12345678910
と桁数が変わってしまうからですね。

それを対策するためにtake 関数で指定桁のみ取得するようにします。

sub(
    int(
       take(
           join(
               range(
                   1, 
                   add(triggerBody()['number_1'], 2)
               ), 
               ''
           ),
           add(triggerBody()['number_1'], 2)
        )
    ),
    int(
       take(
           join(
               range(
                   2, 
                   add(triggerBody()['number_1'], 1)
               ), 
               ''
           ),
           add(triggerBody()['number_1'], 1)
        )
    )
)

私のこの書き方の他にも、subで引き算を行った結果に対してtakeを実施するのでもOKです。
ただしその場合、takeで返されるのは文字列なので、int関数で数値に戻すことを忘れないでください。

私が例のようにやっているのは文字列 ⇆ 数値の変換回数をなるべく減らして後から見返した際などに混乱しないようにするためですね。

型を無暗に変えすぎるのはバグの元ですので。

あとは、上で説明した内容を式に起こすだけですが、1点注意が必要です。

div関数は引数に設定した型を引き継いでしまうので、 div(1, 10) みたいにしてしまうと 0 が返ってきてしまいます。
それを回避するために、 div(float(1), 10) のようにしてあげましょう。

最終的に以下のような式で切捨てが行えます。

float(
   formatNumber(
       sub(
           triggerBody()['number'],
           mul(
               div(
                   float(1),
                   sub(
                       int(
                           take(
                               join(
                                   range(
                                       1, 
                                       add(triggerBody()['number_1'], 2)
                                   ), 
                                   ''
                               ),
                               add(triggerBody()['number_1'], 2)
                           )
                       ),
                       int(
                           take(
                               join(
                                   range(
                                       2, 
                                       add(triggerBody()['number_1'], 1)
                                   ), 
                                   ''
                               ),
                               add(triggerBody()['number_1'], 1)
                           )
                       )
                   )
               ),
               int(last(string(triggerBody()['number'])))
           )
       ),
       concat('F', triggerBody()['number_1'])
   )
)

以下古いやり方(以前まで公開していたもの)

切上げ

続いて切上げですが、ロジックは切捨てとほぼほぼ同じです。
切捨ての場合は四捨五入して切り捨てられるように0に設定していましたが、今度は切り上げられるように9になるように設定してあげます。

これは
 {対象の数} + (9 - {対象の数})

のようにやってあげれば、四捨五入を行う箇所が9となるので、切上げが行われますね。

切上げを行う箇所の数値は「.」が出てくるまでの文字数 + 残したい桁数 + 1で取得できます。
これを式にすると以下のようになります。

float(
   formatNumber(
       add(
           triggerBody()['number'],
           mul(
               div(
                   float(1),
                   sub(
                       int(
                           take(
                               join(
                                   range(
                                       1, 
                                       add(triggerBody()['number_1'], 2)
                                   ), 
                                   ''
                               ),
                               add(triggerBody()['number_1'], 2)
                           )
                       ),
                       int(
                           take(
                               join(
                                   range(
                                       2, 
                                       add(triggerBody()['number_1'], 1)
                                   ), 
                                   ''
                               ),
                               add(triggerBody()['number_1'], 1)
                           )
                       )
                   )
               ),
               sub(
                   9,
                   int(
                       last(
                           take(
                               string(triggerBody()['number']),
                               add(
                                   indexOf(
                                       string(triggerBody()['number']), 
                                       '.'
                                   ),
                                   add(1, triggerBody()['number_1'])
                               )
                           )
                       )
                   )
               )
           )
       ),
       concat('F', triggerBody()['number_1'])
   )
)

以下古いやり方(以前まで公開していたもの)

おわりに

なんか切上げの数式ごちゃごちゃやっちゃったけど、もうちょっと簡単にかけそうな気がしますね。

2022/12/17 追記
文字列変換をしまくってやるよりは多少マシになりましたかね?
これぐらい標準関数で搭載して欲しいですねー

Power Automateでデータ型の判定を頑張って行ってみる

はじめに

わたるふ(@wataruf01)さんのこちらのツイートをみて面白そうだな。と思って頑張ってPower Automateでデータ型の判定を行ってみました。

フローの全体はこんな感じ。

一応サンプル値をいくつか実行してみて、多分動いているだろう。
ということで記事にしてみましたが、このフローが正しいかは正直保証しかねます。

ここおかしくないか?とかありましら、遠慮なく指摘してください。(指摘もらうために公開したまである。)

データ型の判定を行う方法

データ型の判定を行う方法として2種類のフローを考えてみました。

まず1つ目が、元の値とその値を型変換した値を比較して判断する方法
2つ目は、関数が成功したかで型判定する方法
です。

2つめはおいしみ(@ksgiksg )さんもリプで案を上げていましたね。

データ型の判定を行う

今回判定を行う型の一覧は以下です。

  • int型
  • float型
  • DateTime型
  • bool型
  • array型
  • JSON
  • XML
  • string型


2種のフローの共通部は以下になります。

  • 型判定対象の型はアレイ変数に追加する
  • 型判定対象の値は作成アクションにいれる

型判定の結果をarrayの変数にいれているのは、諸事情によるところも正直あるのですが、たとえば以下のような場合はJSON型かつArray型になりますね。

[
    {
        "name": "Bob",
        "age": 20
    },
    {
        "name": "Nancy",
        "age": 18
    }
]

なのでその値の型は配列変数に入れていることにしています。

判定する値を作成アクションに入れている理由は、変数やトリガーのインプットのように明示的にこちらで型宣言をしたくないからです。

これらの部分は2つとも同じなので説明とか省きますね。

元の値とその値を型変換した値を比較して判断する

この方法が私がぱっと思いついた方法ですね。

Tryスコープの中身はこのようになっています。

実行条件ですが、配列変数に追加するアクションが実行されるのはその型判定を行っている作成アクションが成功したときのみ実行しています。

型判定は直前の型判定、つまり配列変数への追加アクションが成功した場合か、スキップされた場合のみ実行しています。
配列変数への追加アクションがスキップされた場合 = 直前の型判定の結果、判定した型ではなかった場合
ですね。

処理をそのまま言葉にするとわかりにくいですが要約すると、型判定はint、string...それぞれ毎回行いますよ。ということです。
失敗したときやタイムアウトしたときに実行しないのは、その場合は不正な結果なのでエラーとして扱いたいからです。

そして型判定を行っている数式ですが、以下のようにしています。

equals(outputs('作成'), int(outputs('作成')))

単純ですね。
判定する値と、判定する値を判定する型に変換した値とで、値が同じかを判定しています。

なぜ同じか?を判定する必要があるかというと、たとえばbool関数って数値や'true'や'false'なんかの文字列を渡しても成功するんですね。

これは公式docsにも実行結果の例として記載されているので、気になる人はみてみてください。

この場合、型判定がエラーになっていないとはいえ、10などの数値をbool値として扱いたくないので、equals関数で値が同じか?を判定しています。

正直この型判定のところではequals関数なくてもいいんですけど、後続の配列変数に追加するアクションと数式を揃えたかったのでこのようにしています。

後からフローをみたときに処理をわかりやすくするためですね。

配列変数に追加するアクションの式は以下のようにしています。

if(
    equals(outputs('作成'), int(outputs('作成'))),
    'int',
    false
)

後の数式は、それぞれの型に変換して判定を行っているだけなので割愛します。
1点、JSONの判定だけが、json関数に渡す値はstringかxmlでないといけないので、元の値をstringに変換してからjson関数に入れています。

equals(outputs('作成'), json(string(outputs('作成'))))

このTryのスコープですが、必ずstringの判定を最後にしてください。

stringの型判定を行っている数式たちは、(多分)エラーにはならないはずです。

equals(outputs('作成'), string(outputs('作成')))
if(
    equals(outputs('作成'), string(outputs('作成'))),
    'string',
    false
)

string関数使ってのstring型への変換は多分ですがエラーにならないんじゃないかな?
もしここがエラーになってしまうと、スコープの最後の処理がエラーになってしまうことになるので、Tryスコープが(成功している処理があっても)エラー扱いになり、後のCatchスコープが問答無用で実行されちゃいます。

stringがエラーになる場合の回避策は次に紹介する方法で記載しています。

CatchスコープはTryスコープが成功しなかったときに実行したいので、以下としています。

中の処理としては単純で配列変数に'Error'を追加しているだけです。

FinallyスコープはCatchスコープが成功かスキップした場合のみ実行しています。

このスコープ内では、型がなんだったかを、作成アクションで表示するようにしています。

ただ、上で述べたように型判定する値が数値だった場合、bool型の判定が失敗せず、配列変数にfalseが追加されてしまっているので、余分なものを取り除いてから型の表示を行います。

これにはアレイのフィルター処理アクションが使えますね。

「項目」というのはitem関数を設定しているとこう表示されます。

数式でみてみるとこうなりますね。

@not(equals(item(), false))

この結果を用いて、作成アクションで値の型を表示させるわけですが、以下の場合はなにかしらエラーが起きているということなので、エラーを表す文言を表示させています。

  • 配列の最後がErrorの場合
  • 配列が空の場合
if(
    and(
        not(equals(last(body('アレイのフィルター処理')), 'Error')),
        not(empty(body('アレイのフィルター処理')))
    ),
    concat('データ型は: ', join(body('アレイのフィルター処理'), ', ')),
    concat(join(body('アレイのフィルター処理'), ''), ' 型判定に失敗しました。')
)

エラーの場合は別にjoin関数いらないんですけど、これでここだけみればCatchが実行されたのか飛ばされたのかが把握できるので入れています。
デバッグ用ですね。

上で共通部はここ!って書きましたが、CatchとFinallyはもう1つのフローでも同じ処理をしています。
上で書かなかった理由はいきなりここの部分だけ書かれてもわからないな。と思ったそれだけの理由です。

この方法のだめな点は、例えば 10 と入力すると、
「データ型は: int, float, json
と返ってくる点です。

正しくは
「データ型は: int」 と返ってきて欲しいですね。

また、DateTime型の判定もDateTimeの定義を年、月、日、時間が含まれている。と定義するならうまくいってないんですよね。

この判定では

yyyy-MM-ddTHH:mm:ss.fffffffK

というタイムフォーマットでないと、DateTime型として認められません。

関数が成功したかで型判定する

初めに書いておくと、この方法はあまりお勧めしません。(私のやり方がよくないだけかもですが)

実行条件の構成は先ほどと同じで、異なるのは設定している式だけです。

この方法では例えばint型の判定ですと、int型をさないと関数の結果としてエラーを返すような関数を用いて、判定を行う方法です。

まずint型の判定ですが、addDays関数を用いています。
もちろん、パラメータとしてint型の値を設定すべき他の関数を用いてもOKです。

int型の判定

addDays(utcNow(), outputs('作成'))

配列変数に追加アクションでは、
このアクションが実行される = 型判定の結果、判定した型であった
ということなのでそのままその型の値をいれています。

float型、json型、xml型はそれぞれの値のみを受け入れるパラメータをもった関数が見つからなかったので、今回対象外にしています。
(これもこの方法がダメな点ですね)
json関数はxmlをパラメータとして受け取りますが、string型も受け入れるので完全な型判定が行えないので、対象外にしています。

他型の判定式を以下列挙していきます。

DateTime型

ticks(outputs('作成'))

bool型

not(outputs('作成'))

array型

join(outputs('作成'), '')

string型

length(outputs('作成'))

このままではstring型でない関数がやってきたとき、stringの判定(最後の判定)でエラーになってしまい、Tryスコープ全体としてエラー扱いになるので、回避策を行う必要があります。

Tryの中でエラーと判定したいときは、配列変数になにも入っていないときですね。
なのでstring型判定が失敗したら、配列変数の中身を確認して、空であればなにもしない(エラーのまま)。値が入っていればなにかアクションを実行する。
ということにします。
(まぁこの方法配列に入っていたのがfalseだけだったらダメなんですけどね!!!)

条件アクションを追加して、実行条件の構成をstring型の配列変数に追加アクションがスキップされたときに設定します。

条件には、配列変数が空でなかった場合。となるようにします。

左辺が

empty(variables('DataType'))

で、右辺が

true

ですね。

はいの場合、つまり配列変数に値があった場合に実行するアクションはなんでもいいのですが、とりあえず失敗しなさそうな、現在の時刻アクションでも入れておきます。

さて、このやり方のダメなところはDateTimeの判定ですね。
DateTime型は(私の理解だと)

yyyy-MM-ddTHH:mm:ss

が基本フォーマットになるはずで、少なくとも

yyyy/MM/dd

はDateTime型ではないはずです。
これはTimeStampを含文字列、string型に分類されるかな。

ただ、Power Automateの関数はそもそもパラメータとしてDateTime型のみを受け入れるものはなく、すべてTimeStampを含む文字列を受け入れる仕様なので、このやり方ではDateTime型ではなくTimeStampを含む文字列の判定しか行えません。

Power Automate側がどう定義しているかちょっとわからないですが(そもそもDateTimeを型として取り扱ってないんじゃないかな)、時間ありをDateTime型、時間なしをDate型と簡単に定義するとしてもこの方法はだめですね。

また、このやり方のダメな点もう1つはjson型やarray型もstring型として判断されてしまう点ですね。

[1, 2, 3] とかを入れて試してみてください。

コピペ用

元の値とその値を型変換した値を比較して判断する

変数を初期化する(DataType)

作成

Try 型判定

Catch 型判定

Finally 型判定

関数が成功したかで型判定する

変数を初期化する(DataType)

作成

Try 型判定

Catch 型判定

Finally 型判定

コピペのやり方

以下記事の「なにこのコードどうやって使うの」でやり方紹介していますので、参考にしてください。

koruneko.hatenablog.com

完成形

両者のやり方にそれぞれ問題点があったので改善してみました。

DateTime型は以下と定義しました。
(ここらへんの定義はよく、DBとかで使われている理解で、それ製品やバージョンによって定義違ったはずだから、公式でこれだよ。っていってくれてないと正直わからんのですよね)

yyyy-MM-ddTHH:mm:ss

もしくはミリ秒があってもOKとします。

yyyy-MM-ddTHH:mm:ss.fffffffK

Date型は

yyyy-MM-dd

単なる日付フォーマットはTimeStampを含む文字列(便宜上TimeStamp型)にします。

新たに判定する型が増えたので、それぞれ判定するアクションを追加します。

まず今まで話してきた日付型たちからいきましょうか。

ちなみに実行条件の構成は変更なしです。

まずはDateTime型ですが、上記のフォーマットであるか?を判断できればよいので、日付部分と時間の部分で分けて判断します。
分けるために、間にある'T'を利用したいと思います。

式にすると以下のようになります。

and(
    equals(
        slice(
            outputs('作成'),
            0,
            mul(sub(length(outputs('作成')), indexOf(outputs('作成'), 'T')), -1)
        ),
        formatDateTime(outputs('作成'), 'yyyy-MM-dd')
    ),
    or(
        equals(
            slice(
                outputs('作成'),
                add(indexOf(outputs('作成'), 'T'), 1)
            ),
            formatDateTime(outputs('作成'), 'HH:mm:ss')
        ),
        equals(
            slice(
                outputs('作成'),
                add(indexOf(outputs('作成'), 'T'), 1)
            ),
            formatDateTime(outputs('作成'), 'HH:mm:ss.fffffffK')
        )    
    )
)

ちょい複雑ですが、上が日付部分の判定、下が時間の判定です。
時間ですが、ミリ秒含むか?も判断必要なので、or関数で秒数含む場合と含まない場合の判断を行っています。

配列変数に追加するアクションは以下のようにしています。

if(
    outputs('型判定(DateTime)'),
    'DateTime',
    false
)

作成アクションの型判定でtrue or falseの判断までしてるんだから、この判定方法でよかったですね。

Date型は以下方法で判断します。

equals(
    outputs('作成'),
    formatDateTime(outputs('作成'), 'yyyy-MM-dd')
)

これは簡単ですね。
もとの値と、フォーマット後の値が一緒か?を判断しているだけです。

配列変数の追加はDateTimeと同じなので割愛。

最後にTimeStamp型ですがこちらは、エラーで判定していたころのDateTime型の判定をTimeStamp型の判定と定義を変えただけです。

残りはfloat型の判定とjson型の判定ですね。

まずfloat型です。

float型は、

  • float関数を実行してもエラーにならない
  • 整数ではなく浮動少数点であればよい

という条件を満たしていればいいですね。

1つ目の条件はただ実行すればいいだけなのでいいとして、問題は2つ目の条件ですね。

値に少数を含むか?は
元の値 - 元の値から小数点以下切り捨てたもの > 0
であればよいです。(作成アクションで 10.00 のように入力しても 10 になるので .00 などは考えないです)

ただし、Power Automateには切り捨てを行う関数は残念ながら存在しないです。
丸めならあるんですけどね。

丸めのやり方はHiroさんがQiitaで纏めてくれている記事があったので、そちらを参照してください。

qiita.com

さて、では切り捨てですが、これはDateTime型判断のときに日付を切り出したとき同様、slice関数使って切り出してあげればいいです。

数式にするとこんな感じですね

greater(
    sub(
        float(outputs('作成')), 
        int(
            slice(
                string(outputs('作成')),
                0,
                mul(
                    sub(
                        length(string(outputs('作成'))), 
                        indexOf(string(outputs('作成')), '.')
                    ), 
                    -1
                )
            )
        )
    ),
    0
)

最後にjson型です。

こいつは、判定条件に最初が「 { 」か最後が「 [ 」であること。
という条件をプラスするだけでOKですね。

厳密な判定をいうと、最後も同じ閉じかっこである必要がありますが、そうでなかった場合はjson関数での変換で引っかかるので今回はそこまでやらなくてOKです。

and(
    or(
        equals(first(string(outputs('作成'))), '{'),
        equals(first(string(outputs('作成'))), '[')
    ),
    equals(outputs('作成'), json(string(outputs('作成'))))
)

これで(多分)型判定がちゃんとできるようになりました。

最後にこの完成後のフローも置いておきます。
変更したのはTryスコープだけなので、Tryスコープだけ記載します。

コピペ用

Try 型判定

さいごに

疲れました。

ブログに残しとこ。と思ったら予想以上に時間かかりました。。。

眠い + 書きながら修正 + 眠い
みたいな状態でかいたので日本語おかしかったり、前後の文おかしいとこあるかもです。。。

そこは指摘してもらえると助かります。

動かなかったりもっといい方法あれば教えてくださいー

Power Appsのコンポーネントでカスタムラベルコントロールを作成してみる

はじめに

Power Appsのコンポーネントを利用して縁取りや影を設定できるようなカスタムラベルコントロールを作成してみたので紹介します。

完成イメージはこんな感じです。

カスタムラベルコントロールを作成する

コンポーネントを作成する

コンポーネントの作成方法の概要については、以前私がM365VM2022で登壇したセッションをご覧ください。

今日から始めるPower Appsでのコンポーネント開発 コルネ - Korune - YouTube

www.docswell.com

デザインをカスタムできる文字列を作成する

今回デザインをカスタムするためにSVGを利用します。

SVGについてはこの記事ではあまり詳しくは触れませんが、利用する要素の参考ドキュメントだけ残しておきます。

Textの基本要素
developer.mozilla.org

Textの縦方向の要素
developer.mozilla.org

Textの影の要素
developer.mozilla.org

上記要素を利用して組み立てたSVGがこちらになります(唐突)

data:image/svg+xml,
<svg viewBox='0 0 640 640' xmlns='http://www.w3.org/2000/svg'>
    <defs>
        <filter id='shadow'>
            <feDropShadow 
                dx='10' 
                dy='5' 
                stdDeviation='2'
            />
        </filter>
    </defs>

    <text 
        x='50%' 
        y='50%' 
        text-anchor='middle' 
        dominant-baseline='middle' 
        font-family='Algerian'
        font-size='80'
        stroke-width='3' 
        stroke='#d42314' 
        fill='#18eee0ff'
        filter='url(#shadow)'
    >Sample Text</text>
</svg>

他にもSVG利用すればもっとリッチなテキストを作成することが出来はするのですが、めんどくさいキリがないので、このくらいにします。

こちらをコンポーネント利用時にカスタマイズできるようにパラメータを外だししていこうと思います。

カスタムプロパティを作成する

必要なパラメータは以下になりますね。

  • テキスト
  • フォントのサイズ
  • 文字のフォント
  • 横方向の位置
  • 縦方向の位置
  • 文字の外枠の色
  • 文字の外枠の太さ
  • 文字の色
  • ぼかしのX要素
  • ぼかしのY要素
  • ぼかしの標準偏差

これらのパラメータを作成してきます。
コンポーネント利用者がカスタムできるパラメータを作成したいので、プロパティの型はすべて入力になりますね。

設定値は以下になります。

表示名 名前 説明 プロパティの型 データ型
Text Text テキスト 入力 テキスト
FontSize FontSize フォントサイズ 入力 数値
Font Font フォント 入力 テキスト
Align Align テキストのアライメント 入力 テキスト
VirticalAlign VirticalAlign 垂直方向の配置 入力 テキスト
StrokeFill StrokeFill 縁取りの色(16進数) 入力 テキスト
StrokeWidth StrokeWidth 縁取りの太さ 入力 数値
FontFill FontFill 文字の塗りつぶし色(16進数) 入力 テキスト
ShadowX ShadowX ぼかしのX要素 入力 数値
ShadowY ShadowY ぼかしのY要素 入力 数値
ShadowstdDeviation ShadowstdDeviation ぼかしの標準偏差 入力 数値

「値が変更されたときに OnReset を実行する」はすべてチェックしています。

* カスタムパラメータの作成方法は冒頭に紹介した資料や動画をご確認ください。

次にこのカスタムパラメータを先ほど紹介したSVGに設定していきます。

SVGにカスタムパラメータを設定する

Power AppsでSVGを表示するためには画像コントロールが必要です。

画像コントロールImageSVGの定義を記載します。
ただし記載方法は

"data:image/svg+xml,"& 
EncodeUrl(
    "<svg viewBox='0 0 640 640' xmlns='http://www.w3.org/2000/svg'>
...
    </svg>"
)

のようになります。
EncodeUrl関数が必要な点が注意ですね。

また、配置した画像コントロールコンポーネントのサイズによって変わるように

Image.X

0

Image.Y

0

Image.Width

Parent.Width

Image.Height

Parent.Height

コンポーネントのサイズによって可変になるように設定しましょう。

次にSVGの描画領域(viewBox)も画像コントロールのサイズによって可変になるようにしましょう。

これは

<svg viewBox='0 0 "& Self.Width & " " & Self.Height & "' xmlns='http://www.w3.org/2000/svg'>

のように描画領域を画像のサイズにあわせてあげればよいです。
これはコンポーネント化するとき以外でもPower AppsでSVGを利用するときの基本テクニックなので覚えておきましょう!

さて、Power AppsでSVGを利用するための準備ができましたので、先ほどのSVGにカスタムプロパティを組み込んだ式を設定します。

完成形は以下です。

"data:image/svg+xml,"& 
EncodeUrl(
    "<svg viewBox='0 0 "& Self.Width & " " & Self.Height & "' xmlns='http://www.w3.org/2000/svg'>
        <defs>
            <filter id='shadow'>
                <feDropShadow 
                    dx='"& Parent.ShadowX &"' 
                    dy='"& Parent.ShadowY &"' 
                    stdDeviation='"& Parent.ShadowstdDeviation &"'
                />
            </filter>
        </defs>

        <text 
            x='"&
                Switch(
                    Parent.Align,
                    Align.Left, "0%",
                    Align.Center, "50%",
                    Align.Right, "100%",
                    "50%"
                )
            &"' 
            y='"&
                Switch(
                    Parent.VerticalAlign,
                    VerticalAlign.Top, "0%",
                    VerticalAlign.Middle, "50%",
                    VerticalAlign.Bottom, "100%",
                    "0%"
                )
            &"' 
            text-anchor='"&
                Switch(
                    Parent.Align,
                    Align.Left, "start",
                    Align.Center, "middle",
                    Align.Right, "end",
                    "middle"
                )
            &"' 
            dominant-baseline='"&
                Switch(
                    Parent.VerticalAlign,
                    VerticalAlign.Top, "hanging",
                    VerticalAlign.Middle, "middle",
                    VerticalAlign.Bottom, "auto",
                    "middle"
                )
            &"' 
            font-family='"& Parent.Font &"'
            font-size='"& Parent.FontSize &"'
            stroke-width='"& Parent.StrokeWidth &"' 
            stroke='"& Parent.StrokeFill &"' 
            fill='"& Parent.FontFill &"'
            filter='url(#shadow)'
        >"&
            Parent.Text
        &"</text>
    </svg>"
)

垂直および水平要素はPower Appsで、定義済みパラメータがありますので、利用者はこちらをもとに設定してもらうことにします。(そのほうが混乱がないはず)

ちなみにパラメータは AlignVerticalAlign です。
ドキュメントはこちら

docs.microsoft.com

ただ、その値をSVGでそのまま利用することはできないので、Switch関数で適当な値に変換しています。

色についてもPower Appsの定義済みパラメータを利用したかったのですが、あれは16進数で返してくれないので。。。

キャンバスアプリで使ってみる

冒頭でも画像を載せましたが、Power Appsでこのコンポーネントを読み込むと、以下のような感じでパラメータを弄ることができます。

フォントはPower Appsの定義済みパラメータのプロパティを利用するのでもよいのですが、このように定義済み以外のフォントを設定することもできます。

これは元から存在しているラベルコントロールのフォントでも同じことができますので、知らなかった人は是非試してみてくださいね。

おわりに

だいぶ説明雑ですが以上でカスタムラベルコントロールの作成は以上で終了です。

SVGの説明どこまですればいいのかわからないや。


スポンサードリンク