Kilroy 365

Microsoft365 主にPowerPlatformについての備忘録

複数条件の絞り込みにトグル/チェックボックスを利用する

f:id:KilroyWaaasHere:20220129021641j:plain

はじめに

ギャラリーに表示したアイテムを絞り込む用途って、多いですよね。

Filter関数を使用するのが一般的ですが、Filter関数は

Filter(Table*, Formula1 [, Formula2, ... ] )という感じの構文で、複数条件を指定して絞り込むことができます。

詳しくはDocsを参照してください。

この複数条件の指定については、If関数で条件分けをしがちですが、Ifでの条件分けは条件が多くなればなるほど複雑化して可読性が悪くなっていき、後から参照したり修正したりする際にとても不便です。

選択肢列の絞り込みをする場合だと、Ifを使用しない、シンプルで読みやすい絞り込みの書き方が存在します。詳しい説明は後回しにするとして、まずは例をあげてみます。

データソース

SharePointリストをデータソースに、次のように選択肢列を2つ設定してみます。

f:id:KilroyWaaasHere:20220128080033j:plain

絞り込みの書き方

Filterの条件をこのようにすることで、複数条件の絞り込みを、Ifを使わずシンプルに書くことができます。

f:id:KilroyWaaasHere:20220128002445j:plain

動作は以下。ちゃんと2つのコンボボックスの選択に従ってフィルターされています。

20220128001516

今回の記事はこれの応用で、トグルまたはチェックボックスを使ったフィルターを、この書き方に当てはめる方法を説明していきます。

 

トグルとチェックボックス

トグル(日本語では切り替えコントロール) とチェックボックスの説明は以下。

どちらも、データ型としてはboolean(はい/いいえ)を扱い、コントロールの状態を変化させることによって、true/falseを切り替えます。

トグルならon、offでそれぞれtruefalseを、チェックボックスならチェックを入れるとtrueを、チェックを外した状態ならfalseを返します。

UIが違いますが、目的は同じようなものなので、どちらを使うかは用途よりも使い勝手やデザインの好みによるところが大きいのではないかと思います。

今回の場合だと、データに要素がいくつもあり、それぞれの要素にあてはまるか、そうでないかで絞り込んでいく用途が想像できます。 製品データがあって、多数あるオプションの有無で絞り込みをかけたりする場合などに使えそうです。

オプションの数だけ絞り込み条件数が多くなるので、なおさらシンプルで読みやすい書き方が求められます。

上で紹介した書き方を当てはめ、シンプルに書けると便利ですね!

 

複数条件絞り込みのシンプルな書き方の説明(ざっくりと)

f:id:KilroyWaaasHere:20220128005357j:plain まずは冒頭で上げた例で複数条件をどのように絞り込んでいるか、ざっくりと見ていきます。

コンボボックスの数だけ条件➀、条件②・・・と絞り込み条件を追加していく感じの書き方になっていて、それぞれのコンボボックスでの絞り込み条件の中身は条件A、条件BをORでつないだ形になっています。

条件Aでコンボボックスが空かどうかの判別を、条件Bで選択肢列にコンボボックスの選択と同じものがあるかどうかを判別しているのですが、上の画像中で説明している通り、条件にはデータソースと直接関係のない条件も設定することができます。

この場合、trueならすべてのレコードが該当、falseならすべてのレコードが非該当というように判断されるようです。

これを利用して、条件➀は以下に示すように、コンボボックスが選択なしの状態だとすべてのレコードが、選択している場合は、選択肢と一致するレコードが絞り込まれる、という仕組みになっています。

f:id:KilroyWaaasHere:20220128011035j:plain

そして、Filterの構文上、条件➀、条件②は、ANDでつないだのと同義になっているので、条件➀かつ条件②に当てはまるもの、という感じで絞り込まれていきます。

条件は追加も可能ですので、それぞれの絞り込み条件の中身(条件A、条件B)にORやANDで条件を追加したり、絞り込みの条件(条件➀、条件②)を追加して増やしていくことができます。

 

トグル/チェックボックスへあてはめる

絞り込みの理屈が理解できたところで、トグル/チェックボックスへあてはめる方法を考えていきます。

この書き方のミソは、コントロール(コンボボックス、トグル…)の条件を変えてやることで、すべてのレコードに該当する条件と、絞り込まれる条件とが切り替わるように設定する ことです。

チェックボックスを例にとると、

  • チェックがついていない状態 ➡ すべてのレコード
  • チェックがついている状態 ➡ 絞り込みがかかる

ような条件です。

そんな条件どうやるの???

実のところ、トグル/チェックボックスを普通に使うとこんな条件は作り出せません(思いついていないだけかも知れませんが)。

例えば、データソースにBoolean1というBoolean型の列を設定し、tglBoolean1というトグルコントロールで絞り込みをかけるとします。

コンボボックスの例をまねて、トグルがオフの際に全レコードが適用される、すなわちtrueになる条件と、トグルの値とBoolean1の値が一致する条件というような書き方をしてみます。

tglBoolean1.Value=false || Boolean1=tglBoolean1.Value

こんな感じにしてみましょう。先の例と同様に || をまたいで前者を条件A、後者を条件Bとします。

…はい、ダメですね。

条件Aは、トグルがオフの際にtrue、オンの際にfalseとなるので、それぞれ全レコードが該当、非該当ということになり、問題ありませんが、条件Bの方が問題です。

条件Bについて見てみます。

トグルがオフの時、falseが返りますが、これがBoolean1にチェックのついていないレコードに一致してしまい、絞り込みがかかってしまいます。

データソースを見ると、Boolean型の列は一見、チェックのついていないところに何も入っていないように見えますが、実際にはBlankではなく、falseとなっているので、トグルのfalseと一致してしまいます。 f:id:KilroyWaaasHere:20220128105842j:plain

解決策

ではどうやったら、この問題が解決できるか。

少々邪道な方法ですが、解決策を思いついたので、書いていきたいと思います。

問題となっているのは、Boolean型の値がtrue/falseの二者択一で、trueでなければ必ずfalseになってしまうことです。true以外をBlankにして、コンボボックスのfalseと一致させないようにしないと、このような条件に当てはまる式が書けませんが、Boolean型ではどう頑張っても実現不可能でした。

Boolean型を回避

falseが出てこないようにするには、falseを出さなければ良いのです。(進次郎構文みたいですな...)

Boolean型では無理なので、列をテキスト列や選択肢列にしてあげます。今回は1行テキストでやってみます。

変数でtrue/falseを代替文字に変換

変数を使ってトグルのtrue/falseを別の文字に変換してやり、データソース側はtrueに対応する文字(今回の場合は○)を入力し、trueの代わりにしてフィルターします。

なお、falseの変換先の文字(今回は×)は捨て文字にし、使用しません。

トグルをtglTextBoolean1とし、チェックボックスをchbTextBoolean2、それぞれ対応するテキスト列をTextBoolean1、TextBoolean2とします。

//tglTextBoolean1.OnChange
Set(varTrue1,Switch(Self.Value,true,"○",false,"×"))

//chbTextBoolean2.OnCheck
Set(varTrue2,"○") 

//chbTextBoolean2.OnUncheck
Set(varTrue2,"×") 

のように、true/falseを変数で ○/× に変換します。

データソース側は絞り込みをかけたいものに○を入力し、それ以外はBlankのままにします。 f:id:KilroyWaaasHere:20220128150621j:plain

これで、falseを排除できました。以下のように絞り込み条件を書くと、複数条件の絞り込みができます。

Filter(
    FilterTest,
    IsBlank(cbbChoices1.Selected.Value) || Choices1.Value = cbbChoices1.Selected.Value,
    IsBlank(cbbChoices2.Selected.Value) || Choices2.Value = cbbChoices2.Selected.Value,
    tglTextBoolean1.Value=false || TextBoolean1=varTrue1,
    chbTextBoolean2.Value=false||TextBoolean2=varTrue2
)

実際に動かしてみたら以下のような感じで、ちゃんと動作してますね。

20220128154951

ギャラリー・フォームもトグル/チェックボックス

絞り込みはできましたが、編集もできないと片手落ちです。

データソースはテキストなので、そのままにしておくと、true以外の文字が入ったり、falseを書き込まれたりして条件が破綻しかねません。 どちらも絞り込みと同じくトグル/チェックボックスを実装するのがスマートです。

ここからは本題ではないので、サーっと説明していきます。

ギャラリー

ギャラリーは簡単ですね。 トグルとチェックボックスをギャラリーに追加して、

//tglGalTextBoolean1.Default
If(ThisItem.TextBoolean1="○",true,"",false)
//chbGalTextBoolean2.Default
If(ThisItem.TextBoolean2="○",true,"",false)

のように書いてやればOKです。式が単純なのでSwitchで書いてやっても良いです。

それぞれトグルとチェックボックスのDisplayModeをViewに変更しておきたいですが、トグルの場合はViewにするとトグル本体が消えてしまうので、見た目的にちょっともったいないです。 チェックボックスはView、トグルはトグルの見た目が必要な場合はEditに、必要ない場合はトグルを使わずLabelで表示してやればよいのではないでしょうか。

フォーム

それぞれTextBoolean1、TextBoolean2のデータカードにトグル、チェックボックスを挿入します。

f:id:KilroyWaaasHere:20220128162341j:plain

//トグル、チェックボックスのDefaultをそれぞれ次の通り設定
Switch(Parent.Default,"○",true,"",false)

まずこれで、データソースの値が反映されます。

次に、データをEditする場合を考えます。Submitボタンを押したとき、データソースに書き込まれるのはカードのテキストインプットのテキストなので、これをトグル/チェックボックスの値に応じて変えてやれば、変更をSubmitすることができます。

//テキストインプットのDefaultをそれぞれ次の通り設定
Switch(tglFrmTextBoolean1.Value,true,"○",false,"")
Switch(chbFrmTextBoolean2.Value,true,"○",false,"")

トグル、チェックボックスの値が正常にテキストインプットに反映されるのを確認したら、Visibleをfalseにして、テキストインプットを隠します。

これで、すべて終了です。動作を見てみましょう。

20220129003912

トグル、チェックボックスでの編集、新規作成、フィルターすべてOKです。

注意点

本来はBoolean型を扱うトグル・チェックボックスをテキスト型で使用する、ちょっと乱暴な使いかたですので、注意点があります。

アプリで触る分にはユーザーにテキスト入力をさせないようにできてますので、問題ないと思いますが、リストを直接触ることがあるなら破綻してしまう可能性があります。

SharePointのフォームをPowerAppsで作り、同じやり方で入力制限したり、ソース側でも入力の制限をかけてあげないと使っていくのは厳しいと思います。

やるならあくまで自己責任で、的な感じです。

おわりに

実のところ、初めはfalseの回避に変数を使用せず、Booleanのtrueをテキストの"true"として評価させるという、さらに無茶苦茶な方法を取っていました。

こちらでもできなくはないのですが(実際できた)、リストのテキスト列の挙動がおかしく、なんだかやばそうでした。

具体的にはテキスト列ににtrueと入力すると表示がTrueとなり、これをさらにtrueに書き換えると、値は入っているものの、表示がされなくなりました。

誤動作防止でそういう仕様になっているのか、エラーでそうなっているのかはわかりませんが、気持ち悪かったので、変数を使うことにしました。

Power Appsでの通貨表示

f:id:KilroyWaaasHere:20220112013000j:plain あけましておめでとうございます(もう遅い…)。 2020年の年末にPower Platformに出会い、約1年間 Power Appsを中心に勉強を続けてきました。

ある程度基本的なところは触ってきたかなという気がしますが、Output が全然できてないせいで、一度通過したことを調べ直すシチュエーションも多く、効率悪いなーと感じることの多いこの頃です。

2022年はもう一度初心に還って、基本的なことから備忘的にOutput をしていきたいなと思います。 僕自身、ノンプロの市民開発者ですし、技術的BGもほとんどないので、大半が調べたことを自分の備忘用に記載する内容が中心になると思いますが、なるべく週に1本程度の更新を目標に再開していきたいと思います。 基本的なところはできるだけ深堀して理解度を上げるというのも一つの目標です。

今年の第一弾はPower Apps での通貨型のデータの扱い(表示)について書いていきます。

通貨型のデータ

SharePointリストやDataverseなどでは通貨型のデータを設定し、扱うことができますが、Power Apps(ここではキャンバス。モデル駆動とかポータルについては調べてません)では、以下のような位置づけのようです。

浮動小数点数に格納される通貨値。 Currency 値は、通貨フォーマット オプションを使用した数値と同じです。

Power Apps のデータ型 - Power Apps | Microsoft Docs より。

浮動小数点数はさておき、結局のところ、直接的に通貨型のデータを扱えるわけではなく、数値型と同様に扱い、見た目をフォーマットすることで通貨表示にするという運用になっています。

フォーマットの書き方

Text関数を用いて記述します。

Power Apps での Text 関数 - Power Apps | Microsoft Docs 参照。

通貨の表示の際に使用するText関数の構文は、

Text( Number, CustomFormat, [ResultLanguageTag ] )

となっており、

第1引数:数値、第2引数:フォーマットの形式(プレースホルダー)、第3引数:言語タグ の形です。

第1引数(Number)

通貨の場合は数値型のデータを入れます。見た目が数値でもテキスト型のデータの場合もあるので、そういう場合はValue関数を使って数値型に直してやる必要があります。

第2引数(Custom Format)

ここにプレースホルダーを設定して、表示の形式を設定します。 使用するプレースホルダーは通貨の場合、以下のものを使います。

  • 数値:#、0

  • 言語:[$-Language Tag] ←言語のプレースホルダ

  • 通貨記号:$

  • 区切り:ピリオドとコンマ(言語によってはスペースなども桁区切りとして使用可)

数値

例として通貨っぽく数値を色々なパターンで記述してみると以下のようになります。小数点以下が存在する通貨の場合は、#と0の使い分けに注意ですね。例では小数点以下第2位まで記述していますが、第3位以降がある少数を入力した場合は第3位が四捨五入されて第2位までの表示となります。 ↓↓↓見にくくてすみません、クリックして拡大してやってください…。

f:id:KilroyWaaasHere:20220107023001j:plain

言語のプレースホルダ

米ドルを例にとると、 [$-en-US] という記述になります。$-以降が Language Tag で、言語-リージョン の形式です。 en-US なら 英語-アメリカ、en-GB なら 英語-イギリス といった感じです。 Custom formatの言語を特定する役割があり、 言語のプレースホルダーを設定していないと、自動的に自身の環境の言語プレースホルダーが設定されるようです。 そして、アプリの実行時にプレースホルダーが存在しない場合、[$-en-US]とみなされてしまうとのこと。

通貨記号

プレースホルダーとして第2引数に$を置くと、設定した言語により、通貨記号の表示を調整してくれ、日本語環境なら¥マークを表示してくれます。

『設定した言語』とは第3引数で指定した言語です。

この言語は第3引数で言語タグを使用することで設定できますが、省略した場合はユーザーの環境の言語が適用されます。

ですので、日本語の環境で日本円を表示する場合などは、これによって第3引数を省略することができます。

ただし、ブラウザやOSの設定を英語に変更しているなど、言語環境が異なると違う通貨記号になってしまうことがあるため、ユーザーの言語環境が不明な場合は第3引数をきっちり設定し、言語を固定しておいた方が無難だと思います。

区切り

基本的にはコンマがグルーピング(桁区切り)、ピリオドが小数点ですが、言語によってはスペースで区切ったり、小数点がコンマだったりします。これは第3引数で設定した言語のルールが適用されるようです。

第3引数(Result Language Tag)

その名称通り、Text関数の結果を調整するようで、通貨のフォーマットを書いている場合は桁区切りや、小数点、通貨のプレースホルダーを設定した言語のものにしてくれます。

デフォルトはユーザーのアプリを動かしている環境の言語で、これを上書きして固定したい場合に設定します。

表記は en-US のように、言語‐リージョン の形です。

通貨表示はカオス

ここまで読んで頂いた方は気づいたかと思いますが、第2引数と第3引数両方にLanguage Tag が出てきます。

第2引数では Language Placeholder の中に、第3引数ではそのままの形で。

二重に言語指定が出てくるうえに、自動設定があるし、プレースホルダーに至っては空だと勝手に en-US に設定されてしまう。

自身の環境も併せると、あいまいな表記の場合、どの言語がどう設定されているのかがわかりづらく、混乱のもとになっています。

しかも、通貨のプレースホルダーとして使用した$は第2第3引数を同じ言語に設定した時にはそのまま$表記のままで、第3引数のみにLanguage Tagを設定した際にのみ正常に働いてくれる謎の挙動を示します。

桁区切りにしても、第3引数が強いようで、Language Placeholder不要なんじゃないの?と思ってしまいます。

恐らく作る機会はそうそうないかもですが、グローバルなアプリを作る際にはLanguage Placeholderなしで、Text関数の外に通貨記号を直書きするのが良いのでは?と思います。

以下は見にくいですが、桁区切りがスペースで、通貨記号も特殊なノルウェークローナと、英ポンドを例に色々やってみた結果です。

↓↓またもやクリック拡大お願いします。

f:id:KilroyWaaasHere:20220110102355p:plain

それぞれの通貨で検証する必要はあると思いますが、

日本円だと、通貨のプレースホルダが利用できるので、

Text(10000000,"$#,###","ja-JP") ➡ ¥10,000,000

のような書き方できれいに表示でき、

ノルウェークローナの場合は桁区切りをスペースにしようとした場合第2第3引数双方にLanguage Tagを入れねばならず、その際が機能しないので、以下のようにText関数の外側に通貨記号をStringとしてつけてやるような形で表記する必要があります。

Text(10000000,"[$-nb-NO]# ###","nb-NO")&"kr" ➡ 10 000 000kr

ややこしいですね!

おまけ

f:id:KilroyWaaasHere:20220112005206j:plain

グローバルアプリ

Text関数のDocsのグローバルアプリの項目で出てくる、フランス語の場合の構文のセミコロンの意味が分からなかったけど、色々調べているうちに発見。 いくつかの言語では小数点の表記がコンマなので、その場合、数式のセパレータがセミコロンになるらしい…。

以下参照。こちらもややこしい!

数式の区切り記号とチェーン演算子

COVID-19 ワクチン接種記録アプリとギャラリーの時系列ビュー

f:id:KilroyWaaasHere:20210923014721p:plain

COVID-19ワクチン接種記録アプリ

Power Apps触ってると、やっぱりみんな作りますよね、ワクチン接種記録アプリ。
僕も半分遊びで作りましたが、なんやかんや自分で作ると記録するし、記録できてると色々気づくことが多いです。記録することって大事ですね!
ワクチンの接種記録アプリは用途がはっきりしてますし、記録をスマホで行うことになるので、Power Apps の自動作成アプリっぽいものが基本で、だいたいはそれに+αな感じのアプリになるのじゃないかな?と思いますが、ほかの人はどんなものを作った(作る)のか気になるところです。僕がつくったものも同様に特殊なものでなく、下記のような画面構成で、

  • TOP画面(なくても良いけど、雰囲気だけ)
  • 体調記録のギャラリー画面
  • 体調記録のフォーム画面
  • 接種データ記録のフォーム画面
  • グラフ表示画面(体温推移)
  • 体調記録の時系列ギャラリー画面

TOP画面のメニューもしくは、プルダウンメニューから各画面に行って記録および閲覧を行うシンプルなもの。
特に目新しい機能もないので、大半の解説は省略しますが、+αに当たる、ギャラリーの時系列ビューの作り方について書こうかなと思います。

時系列ビュー

ギャラリーは同じ構成を繰り返すという、特性がありますが、これがとても汎用性高いですよね。
一連の情報を表示するだけではなく、例えばメニュータブであるとか、同じ図形を連続で配置するようなものを作るときは図形をコピペしていくつも配置するより、ギャラリーに図形を埋め込んで連続表示させてやる方が手軽かつメンテ性もよいです。
この辺りは色々とユースケースあるので、知らなかった!という方は調べてみると見るといいかと思います。
今回作った時系列ビューもこの特性を活かし、ギャラリー内に配置した図形がレコード追加とともに積み重なり、タイムラインのように見えるようにしたもので、いたって簡単な作りですが、時系列をわかりやすく表示するにはとても効果的で気に入っています。
構造は以下の図のようになります。
f:id:KilroyWaaasHere:20210925014130p:plain
簡単ですね。四角形2つと円またはボタンコントロール2つの合計4つの図形をギャラリー内に配置してやるだけです。
上の図は縦方向のに時系列を表示する感じで、垂直ギャラリーに配置する例ですが、横方向に配置したい場合は水平ギャラリーに、90度回転したような感じに横向きに配置してください。
いずれの場合も、縦方向の場合は上の四角形の上端および下の四角形の下端、横方向の場合は左の四角形の左端、右の四角形の右端がそれぞれギャラリーのTemplateにピッタリくっつくように配置します。
また、テンプレートのパッディングが0より大きいと隙間ができ、連続したように見えませんので、0に設定します。

始点・終点の処理

上記のように図形を配置すると、図中赤で示した図形が連続することになりますが、時系列のポイントを円で表現していますので、始点及び終点は円から始まる形にすると、ここから始まった、ここで終わった、というのがわかりやすくなります。
そのために、図中に示したように、ギャラリーの最初・最後に表示されるアイテムを特定し、そのアイテムの場合、上または下の四角形が表示されないように処理してやることが重要です。

これはデータの構造やデータの絞り込み方(フィルターや並べ替えの条件)次第で条件を変えてやる必要があります。時系列表示なので、時間を条件にする場合が多いのかなと思いますが、時系列が登録順そのままのようなものは図中に示したようにIDの値で判断してもよいかと思います。(SharePointListなど登録順にIDが附番される場合)

気を付けるべきは絞り込みや並べ替えを行っている場合で、絞り込みや並べ替えを行い、出てきた結果に対して最初・最後の特定を行わなければいけないので、Min,Max関数はギャラリーにつけたのと同じ絞り込み・並べ替え条件を付けたデータソースに対して適用してやる必要があります。

最初・最後のレコードが明確に特定できれば良いので、例えば、従業員の異動履歴を表示するような場合、絞り込み・並べ替えを行ったレコードの最初のレコードにはおそらく入社、最後のレコードには退職を示すデータが入っていると思われますので、その場合はその情報を基に最初、最後を特定してやればよいので、必ずしもMin,Max関数を使用する必要はなく、条件に合わせてシンプルな特定方法を考えてやればよいと思います。

実装例➀

まずは今回のアプリの時系列ギャラリー。

最初・最後はシンプルに、IDで判別しています。
上の四角形のVisibleのコードは以下のようにしています。

If(ThisItem.ID=Min(Filter('COVID-19 ワクチン接種後体調記録',NumberOfShot=drpSC4.Selected.Value),ID), false , true )

『COVID-19 ワクチン接種後体調記録』というデータソースが元ですが、ギャラリー自体に接種回数で絞込をかけているので、その条件も同様に記述しています。  

実装例②

次に、従業員の異動の履歴を時系列表示したものです。

こちらは、 上の四角形のVisibleを

If(ThisItem.Category.Value="入社",false,true)

のように記述しています。下の四角形では『入社』を『退職』としています。 絞り込みや並べ替えをした後にも、最初・最後が明確にわかる条件がある場合はそれを書いてやればシンプルになりますね。 なお、こちらはギャラリーの末尾でも退職していなければ四角形が消えないので、雇用関係の継続も見た目でわかる様になっています。

時系列ビューの作り方でした。

Power Apps フォームの複数行データカードの高さを動的に変化させる方法

f:id:KilroyWaaasHere:20210918000928p:plain

 

発見の経緯

現在、仕事で定例の会議の仕切りをやっていて、各セクションから提出された事前資料のまとめと配布、アジェンダの作成、司会進行、議事録の作成等々、会議前後は毎回そこそこの作業量があります。


会議の内容はほぼ定型で、まとめるのはやりやすく、過去の振り返りや、進捗の管理などもあるため、いっそのこと、全部ひとまとめにしてさぼって改善してやれ、ということで、会議専用のアプリを作ることにしました。

 

その中に議事録のフォームがあり、議事を項目ごとに複数行テキストの形式で保存しているのですが、議事のボリュームがまちまちでデータカードの高さを統一できない問題が発生しました。

高さを大きく取ると少ない時に空白が大きくなり、小さく取ると、スクロールバーが出てきて見栄えが悪い。
なんとかデータカードの高さを議事の内容に応じて動的に変化させられないか?と考えたのが発見のきっかけでした。

 

AutoHeight

はじめは、複数行の行数を取得して、それに応じて、、、みたいなことを考えていましたが、どうも難しそうでした。『Power Apps Multilines text Datacard Height Dynamically』みたいな感じで検索をかけると、出ました。
盲点でしたが、LabelにAuto Heightあるやないですか。
これに同じ文を打ち込んで、高さを取ってやり、データカードのDataCardValueの高さに適用してやればいけそうですね!

 

実装

 

では、実装していきます。サンプルのアプリです。

Iphone13、発表されましたね。
僕の心の声をアプリが表現してくれています。

さておき、デフォルトではデータカード内のテキスト入力には高さ自動調整の機能が無いため、行数が増えるとOverflowした分は表示されず、スクロールバーが現れます。

※準備として、データソース・インプット・追加するLabelはそれぞれ複数行に設定しておいてください。

 

f:id:KilroyWaaasHere:20210918014235p:plain

カードにLabelを追加します。追加するカード内のなにがしかのコントロールを選択した状態で追加するとカード内に入ってくれます。

 

 

f:id:KilroyWaaasHere:20210918014408p:plain

 

次に、LabelのX・Y・WidthをDataCardValueと同じに指定してやります。
この例でしたら、Label1のX=DataCardValue3.X などです。

 

 

 

f:id:KilroyWaaasHere:20210918014643p:plain

次に、LabelのTextを、Parent.Defaultと指定してやります。これでLabelにカードのデフォルトの値(DatacardValue)が指定されたので、入力値と同じテキストがLabelに表示されます。

 

 

 

f:id:KilroyWaaasHere:20210918014854p:plain

Labelの高さの自動調整(AutoHeight)をONにします。デフォルトはOFFになっています。
テキストの行数に合わせてLabelの高さが変わりました。
テキストが重なって少し太くなったように表示されているのがわかると思います。ほぼぴったり重なっています。


f:id:KilroyWaaasHere:20210918015320p:plain

Labelを追加した時点では、Labelがカード内で一番上に来ていると思いますので、DataCardValueに入力できるよう、最背面に移動させます。

Labelは高さを取るためだけにあるので、表示させたくないのですが、Visibleをfalseにして完全に消してしまうと、AutoHeightの値が取れなくなってしまうので、最背面に、また、テキストの色も念のためTransParent(透明)に指定してやります。
Labelのフォント、サイズ・WidthがDataCardValueのものといっしょであれば、改行・折り返しなど、全く同じ位置になります。


 

f:id:KilroyWaaasHere:20210918020945p:plain

ですので、あとはDataCardValueのHeightにLabelのHeightを指定してやれば、DataCardValueもAutoHeightの機能を手に入れることになります。
好みで、少し余白を入れたりすると見栄えが良くなると思います。
同じく、DataCardそのもののHeightも、Labelの高さ+αを指定してやると、めでたく実装成功です。

 

実装結果

 

実装後はこのように、入力内容に応じてデータカードが動的に高さを変え、見やすいフォームが出来上がります。やったね♪

 

なにがしかの参考になれば幸いです。

コンポーネントでのCollectionの利用について(PowerAppsでHSLAカラーピッカーコンポーネントを作ってみました。 その3)

f:id:KilroyWaaasHere:20210915212823p:plain

コンポーネント内でのコレクション利用はサポート外

コンポーネントでのコレクションの使用は、オフィシャルには、不可となっています。

f:id:KilroyWaaasHere:20210915180229p:plain

https://docs.microsoft.com/ja-jp/powerapps/maker/canvas-apps/create-component#known-limitations


また、今回の話と逆の方向ですが、コンポーネントの設定にて、アクセスアプリスコープをONにするとコンポーネント内から、アプリのコレクションやグローバル変数にアクセスできるとなっていますが、これも現在の環境では設定が無いっぽい。

f:id:KilroyWaaasHere:20210915181441p:plain

https://docs.microsoft.com/ja-jp/powerapps/maker/canvas-apps/create-component#scope


まだコンポーネントライブラリはプレビュー段階ですし、中には試験段階の機能もあるので、この辺りは致し方ないところでしょう。

 

いや、使えるっぽい

カラーピッカーのコンポーネントを作った際、なんとか作った色と、付随情報をレコードとして保存して利用したいと思ったのですが、情報をストアするとなったら、やっぱりコレクションが使いたくて(コレクションがサポート外だというのは知っていました)、ダメもとでやってみると、あれ?使える・・・?という感じでした。
ただ、警告は出ます。アプリにインポートした際、名前が競合する場合があるので、避けてください、とのことでした。非推奨ってことですね。
確かに、同じ名前つける、はありそうですし、同じコンポーネントを複数使用した時など、エラーが起きそうな気はします。
使用の際は、自己責任で、オフィシャルなアプリには使わないほうがよさそうです。

f:id:KilroyWaaasHere:20210915182931p:plain

コンポーネント外へのデータの受渡し

これにはちょっとハマりました。コレクションに保存した色情報をコンポーネント内のギャラリーに表示して、カラーサンプルとして使用する目論見はうまくいったのですが、やはりコレクションなので、アプリを立ち上げている間だけしか情報が保存されません。アプリのコントロールなどに適用した色の情報を長期的に保持したいなら、コレクション内の情報をコンポーネント外に出力して保存する必要があります。

コンポーネントの出力には、テーブル型のデータを設定できるので、コレクションが使えるならコレクションをそのまま指定してやれば…と思いましたが、コンポーネント内ではエラーが出ないのに、コンポーネントをアプリにインポートしてやると、エラーが出て中身が読めません。(列情報は取得できる)

コレクションという形をとっているのがまずい(多分これが原因)のかと、コレクションの情報をコンポーネント内でテーブル化し、引き出そうとしたりしたのですが、結局のところ、コレクションからデータを引き出す関数のデータソースにコレクションを指定することになるので、エラーがでてしまい、どうにもうまくいきませんでした。

しかし、ありました。回避策!

コレクションからデータが引き出された形になっているものがコンポーネント内に存在していました。それも目の前に。そう、ギャラリーです。
ギャラリー内には、コレクションから吸い出されたデータがありますので、テーブル型のアウトプットに、『ギャラリー名.AllItems』と指定してやると、エラーを出さずにコレクション内の情報をテーブルとして出力できました。これでコンポーネント内のデータをコンポーネント外に出力できるようになりました。
かなり苦戦しましたが、発見した時は、おっしゃ~!と思わず声が出てしまいました。


まとめ(発見したこと)

  • コンポーネントでのコレクションの利用はサポート外(オフィシャルには使用不可)だが、非推奨であるだけで、使用は可能(自己責任で)。
  • コレクションの中身をコレクション外にエクスポートするときは、データ型をテーブルとし、ギャラリーのAllItemsプロパティを使うとうまくいく(コレクションをデータソースとして指定する記述はエラーが出る)。

PowerAppsでHSLAカラーピッカーコンポーネントを作ってみました。 その2

f:id:KilroyWaaasHere:20210915003038p:plain

今回は、前回のおはなし

PowerAppsでHSLAカラーピッカーコンポーネントを作ってみました。 その1 - Kilroy 365

のつづきその2です。
前回はHSLAをRGBAに変換するところまで行きましたが、今回はそれをコンポーネント化しました。そのコンポーネント本体について説明していきます。
作ったものの見た目はこんな感じ。

f:id:KilroyWaaasHere:20210915004341p:plain

コンポーネントの用途

コンポーネントは一般的に、アプリに追加してアプリの部品としての利用が多いと思いますが、これはアプリ開発時の開発ツールとしての利用を想定しています。
アプリ開発する画面に挿入して、その他のコントロールの色を設定する際の補助に使用します。

主な機能は以下。
・HSLAを用いて色の作成
・表示色による比色
・作成した色のRGBA値の利用(ラベルのテキストをコピペ)
・作成した色のHSLA地の表示(HSLA値がわかれば同じ色を作成できます)
・作成した色の一時保存と削除(コレクション利用)
・保存した色の切り替え表示
・作成した色の情報の出力

 

動作はこんな感じ

 

右が作ったコンポーネント。ちょっとまどろっこしいですが、再生モードにしてRGBAのテキストをコピー、編集画面に戻して貼り付けです。
コンポーネントからは出力として、

  • 表示中の色(RGBAの形式)
  • RGBAのテキスト
  • HSLAのテキスト
  • 保存した色情報(コレクションに保存した色のレコード)

を出せるようにしてあるので、必要であれば、保存した情報を別途外部に記録することもできるようにしてあります。上の動作サンプル動画では、コンポーネントにストアしたコレクションのデータを取得し、左のギャラリーに表示しています。

また、おまけのような機能ですが、コンポーネントの特徴として、中身の大きさが変わらない、というのがあるので、動画でやっているように、サイズを調整して上の色表示部だけを表示させてあちこち移動し、カラーサンプルとして配色を検討する、なんてこともアリです。


達人はもっとすごい

とてもタイムリーですが、MVPのHiroさんが同様に、色をテーマにした開発ツール(カスタムコネクタ)を開発されていました。いやはやすごい…。
興味がある方はこちらも参照されてみてはいかがでしょうか。

mofumofupower.hatenablog.com

PowerAppsでHSLAカラーピッカーコンポーネントを作ってみました。 その1

f:id:KilroyWaaasHere:20210914034532p:plain

はじめに

Twitterを流し読みしていると、こんなツイートが流れてきました。

いつもPower Pointを中心にいろいろなデザイン関連のツイートをされているネスコプラズムさんのツイートでした。HSL、便利そう!

 

Power Appsでの色指定

Power Appsでの色指定は、Color列挙体(組み込み色の名前で指定)、ColorValue(16進数値で指定)、RGBA(Red,Green,Blueの3原色ブレンド+不透明度で指定)の3種類のやり方があります(以下参照)。

docs.microsoft.com

僕は上記のdocsを参照し、代表的な色を色名で直接指定するか、その色のRGBAを取ってきて微調整するか、というやりかたがほとんどでしたが、微妙な色の調整やトーンの統一など、とてもやりにくいというのが本音でした。


HSLAの利点

HSLAは上記のツイートにあるように、Hue(色相)をまず選び、Saturation(彩度)、Lightness(輝度)を調整して同系色で異なるトーンの色を作ったり、その逆でトーンを固定し、同じトーンの異なる色味の色を作るなど、直感的に色を作る、調整することがとても簡単です。これをPower Appsで利用できたらいいのに!ということで、実装できる方法はないのか調べてみました。

調べると、HSL➡RGBの計算式があるようで、沢山ヒットしますし、他言語でのコードサンプルも結構出てきます。
単純に計算式そのものが載っているこちらのページの情報を参考に、実装していくことにしました。

www.peko-step.com

 

HSLA→RGBAの計算

With関数を使えば、ほぼそのまんま記述できます。

H、S、L、Aそれぞれの値をスライダーで調整することとし、場合分けをちょっと整理して、コードはこんな感じです。

  RGBA(
        Round(
            With(
                {
                    MAX: If(
                        sldL.Value <= 49,
                        2.55 * (sldL.Value + sldL.Value * sldS.Value / 100),
                        sldL.Value >= 50,
                        2.55 * (sldL.Value + (100 - sldL.Value) * sldS.Value / 100)
                    ),
                    MIN: If(
                        sldL.Value <= 49,
                        2.55 * (sldL.Value - sldL.Value * sldS.Value/100),
                        sldL.Value >= 50,
                        2.55 * (sldL.Value - (100 - sldL.Value) * sldS.Value/100)
                    )
                },
                If(
                    sldH.Value <= 59 || sldH.Value >= 300,
                    MAX,
                    sldH.Value >= 60 && sldH.Value <= 119,
                    (120 - sldH.Value) / 60 * (MAX - MIN) + MIN,
                    sldH.Value >= 120 && sldH.Value <= 239,
                    MIN,
                    sldH.Value >= 240 && sldH.Value <= 299,
                    (sldH.Value - 240) / 60 * (MAX - MIN) + MIN
                )
            ),
            0
        ),
        Round(
            With(
                {
                    MAX: If(
                        sldL.Value <= 49,
                        2.55 * (sldL.Value + sldL.Value * sldS.Value / 100),
                        sldL.Value >= 50,
                        2.55 * (sldL.Value + (100 - sldL.Value) * sldS.Value / 100)
                    ),
                    MIN: If(
                        sldL.Value <= 49,
                        2.55 * (sldL.Value - sldL.Value * sldS.Value/100),
                        sldL.Value >= 50,
                        2.55 * (sldL.Value - (100 - sldL.Value) * sldS.Value/100)
                    )
                },
                If(
                    sldH.Value <= 59,
                    sldH.Value / 60 * (MAX - MIN) + MIN,
                    sldH.Value >= 60 && sldH.Value <= 179,
                    MAX,
                    sldH.Value >= 180 && sldH.Value <= 239,
                    (240 - sldH.Value) / 60 * (MAX - MIN) + MIN,
                    sldH.Value >= 240,
                    MIN
                )
            ),
            0
        ),
        Round(
            With(
                {
                    MAX: If(
                        sldL.Value <= 49,
                        2.55 * (sldL.Value + sldL.Value * sldS.Value / 100),
                        sldL.Value >= 50,
                        2.55 * (sldL.Value + (100 - sldL.Value) * sldS.Value / 100)
                    ),
                    MIN: If(
                        sldL.Value <= 49,
                        2.55 * (sldL.Value - sldL.Value * sldS.Value/100),
                        sldL.Value >= 50,
                        2.55 * (sldL.Value - (100 - sldL.Value) * sldS.Value/100)
                    )
                },
                If(
                    sldH.Value <= 119,
                    MIN,
                    sldH.Value >= 120 && sldH.Value <= 179,
                    (sldH.Value - 120) / 60 * (MAX - MIN) + MIN,
                    sldH.Value >= 180 && sldH.Value <= 299,
                    MAX,
                    sldH.Value >= 300,
                    (360 - sldH.Value) / 60 * (MAX - MIN) + MIN
                )
            ),
            0
        ),
        sldA.Value / 100
    )

※sld~はスライダー。
RGBの値をそれぞれ整数にするのに、Roundを入れていますが、なくてもOKです。

不透明度のAはRGBA、HSLAどちらも同じなのでそのまま。Sliderの値が整数なので、0~100のスライダー値を100で割って出してます。
HSLAとRGBAの比較は以下のような感じ。

今日はここまで、次回に続きます。