Skip to content

Latest commit

 

History

History
198 lines (150 loc) · 10.9 KB

CustomDateSelector.md

File metadata and controls

198 lines (150 loc) · 10.9 KB

はじめに

Power Appsでアプリを作成していると、日付の入力・選択にDatePickerコントロールを利用することがあります。 このDatePickerコントロール、カレンダーが必要ない場面では微妙に使いにくい印象を受けます。特に月を大きく変更するような日付選択、年の異なる日付選択をする場合です。 これらの課題は、しばしばドロップダウンで年・月・日をそれぞれ選択させることで解決したり、テキスト入力にyyyymmddの形式で入力させて、バリデーション というパターンで対応されます。

今回はギャラリーとボタンを組み合わせて、ちょっときれいな日付選択コントロールの作成方法を紹介します。

作り方

食材

材料 用途
ボタン 3 年・月・日の表示+ギャラリーの表示トリガー
ギャラリー 3 年・月・日のアイテム表示・選択用
ギャラリー内ボタン 各1 数字表示&年月日選択用
日付変数 1 選択日付用
Boolean変数 3 ギャラリーの表示切り替え

準備

初期の選択日付はあってもなくてもよいのですが、ここでは簡単のためにToday()を使って初期選択日付を設定しておきます。

Set(dateSelected,Today())

年 選択

年の選択は比較的簡単です。 ボタンをスクリーンに追加して、このボタンに年を表示、また年変更用のギャラリーを表示させる処理を記載します。

Year(dateSelected)
UpdateContext({visGalYear:true});

次に、スクリーンに縦型ギャラリーを追加し、ボタンをギャラリー内に追加します。 ギャラリーのItemsプロパティには選択範囲の年を頑張って入れます。

[2020,2019,2018,2017,2016]

こんな感じです。 ギャラリーの表示を変数を用いて切り替えたいので、Visibleプロパティに表示制御用の変数を設定します。

visGalYear

次にギャラリー内のボタンですが、Textプロパティにはギャラリーの各アイテムを表示させます。

ThisItem.Value

ボタンをクリックしたときのアクションを設定します。 ボタンをクリックしたら、選択日付の『年』部分だけを入れ替える処理をします。また、選択したらギャラリーを非表示にしたいので、表示制御用の変数更新もしておきます。

Set(dateSelected,
Date(ThisItem.Value, Month(dateSelected),Day(dateSelected)));
UpdateContext({visGalYear:false});

これで年の選択部分ができました。 実際動作させると以下のようになります。

out1.gif

月 選択

月の選択も年とほとんど同じです。 選択月を表示するためのボタン、変更用のギャラリーを追加します。

Upper(Text(dateSelected,"mmm"))
UpdateContext({visGalMonth:true});

ここは趣味というか、月であることがわかりやすいように、表示をJAN, FEB,MARなどの英語表記にしています。特に必要ない場合にはMonth(dateSelected)を代わりに設定してください。

月変更用のギャラリーのプロパティは以下の通りです。

[1,2,3,4,5,6,7,8,9,10,11,12]
visGalMonth

ギャラリー内のボタンの表示も、選択月と合わせておきましょう。

Upper(Text(Date(2000,ThisItem.Value,1),"mmm"))

Text関数でmmmを使って月を英語表記するには入力に日付型を求められるので、フェイクで、2000年X月1日を設定しています。

ボタンをクリックしたときのアクションは、今度は選択日付の『月』部分だけを入れ替えます。

Set(dateSelected,
Date(Year(dateSelected),ThisItem.Value,Day(dateSelected)));
UpdateContext({visGalMonth:false});

実際動作させると以下のようになります。

out2.gif

日 選択

最後に日の表示ですが、ギャラリー側が少し変更をうけます。 日の選択範囲は年と月によって変わるからです。 ほかの部分については同様なので、省略します。(必要であれば巻末のプロパティ一覧を参照してください)

日の変更用ギャラリーのItemsは、それ以外の年・月をもとにして、1~31をフィルターします。

FirstN([1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31],DateDiff(Date(Year(dateSelected),Month(dateSelected),1),Date(Year(dateSelected),Month(dateSelected)+1,1)))

1~31のテーブルをFirstNで絞っているのですが、Nを決定する必要があります。 DateDiff(DateAdd(...)))部分がそれにあたります。

DateDiffは2つの日付の間にある日数をカウントする関数です。ここでやっているのは、

Date(Year(dateSelected),Month(dateSelected),1) まずここで選択した年・月の1日を指定し、Date(Year(dateSelected),Month(dateSelected)+1,1) 次の月の1日との間にある日数、つまり月の日数を出しています。

Set(dateSelected,
Date(Year(dateSelected),Month(dateSelected),ThisItem.Value));
UpdateContext({visGalDay:false});

実際動作させると以下のようになります。

out3.gif

補正

大体これで完成なのですが、1つ面倒な問題があります。 年月日は選ぶ順番が前後することがあるので、例えば3/31を選択してから、月を2月に変更すると、2/31→3/3になってしまいます。これはなかなか気持ちの悪いもので、月を2にしたはずなのに、ギャラリーが閉じられたら3に変わっているんです。あまりよくないですよね...

これを緩和するために、『もし選択した日付が次の月に入ってしまう場合には、月の最終日にセットする』という処理を追加します。 先ほどの例でいえば、3/31を選択した状態で、2月にすると、選択日付としては2/28が選ばれるような挙動です。

悪くはないですよね。 この処理は年・月の変更時にのみ入れればよいです。

If(
    /*選択した日付がカレンダーで正しく存在するかどうかをチェック*/
    Text(Date(Year(dateSelected),ThisItem.Value,Day(dateSelected)),"[$-en-US]yyyy/m/d")
     =Year(dateSelected)&"/"&ThisItem.Value&"/"&Day(dateSelected),

    /*存在すればdateSelectedをそのまま更新*/
    Set(dateSelected,Date(Year(dateSelected),ThisItem.Value,Day(dateSelected))),
    
    /*存在しなければ、一番近い月末を選択 例:3/31を選んでから、月を2月に変更→dateSelectedは2/28*/
    Set(dateSelected,DateAdd(Date(Year(dateSelected),ThisItem.Value+1,1),-1,Days))
);
UpdateContext({showMonth:false});

年のほうも同様です。これを入れておかないと、ユーザーが混乱する可能性がありますので、少しの気遣い、すてきなUIの精神で入れておきましょう。

完成したものはこちら

完成したものがこちらになります。

#PowerApps Birthday Selector UI pic.twitter.com/KfJF0xY5QV

— Hiro (@mofumofu_dance) December 22, 2019
<script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

おわりに

Power AppsでUIを作る場合、どうしても既定のパーツでは使いにくいシチュエーションが出てきます。 凝った仕組みとかアニメーションを入れる必要は全くないと思いますが、少しでも使いやすいようにする努力はローコードで早く作れる分、かけてもいい時間なのかなと思います。

付録 - プロパティ一覧

ここではパーツごとに設定したプロパティを記載します。 テーブルなので、いささか長くなりますが、参考にしてみてください。

パーツ種 パーツ名 プロパティ名
ボタン btnDispMonth Text Upper(Text(dateSelected,"[$-en-US]mmm"))
OnSelect UpdateContext({visGalYear:false,visGalMonth:true,visGalDay:false})
ボタン btnDispDay Text Day(dateSelected)
OnSelect UpdateContext({visGalYear:false,visGalMonth:false,visGalDay:true})
ボタン btnDispYear Text Year(dateSelected)
OnSelect UpdateContext({visGalYear:true,visGalMonth:false,visGalDay:false})
ギャラリー galMonth Visible visGalMonth
Items [1,2,3,4,5,6,7,8,9,10,11,12]
ボタン btnMonthSelect Text Upper(Text(Date(2000,ThisItem.Value,1),"[$-en-US]mmm"))
OnSelect If(Text(Date(Year(dateSelected),ThisItem.Value,Day(dateSelected)),"[$-en-US]yyyy/m/d")=Year(dateSelected)&"/"&ThisItem.Value&"/"&Day(dateSelected),Set(dateSelected,Date(Year(dateSelected),ThisItem.Value,Day(dateSelected))),Set(dateSelected,DateAdd(Date(Year(dateSelected),ThisItem.Value+1,1),-1,Days)));UpdateContext({visGalMonth:false});Reset(galMonth)
ギャラリー galDay Visible visGalDay
Items FirstN([1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31],DateDiff(Date(Year(dateSelected),Month(dateSelected),1),Date(Year(dateSelected),Month(dateSelected)+1,1)))
ボタン btnDaySelect Text ThisItem.Value
OnSelect Set(dateSelected,Date(Year(dateSelected),Month(dateSelected),ThisItem.Value));UpdateContext({visGalDay:false});Reset(galDay)
ギャラリー galYear Visible visGalYear
Items [2020,2019,2018,217,2016,2015,2014,2013,2012,2011]]
ボタン btnYearSelect Text ThisItem.Value
OnSelect If(Text(Date(ThisItem.Value,Month(dateSelected),Day(dateSelected)),"[$-en-US]yyyy/m/d")=ThisItem.Value&"/"&Month(dateSelected)&"/"&Day(dateSelected),Set(dateSelected,Date(ThisItem.Value,Month(dateSelected),Day(dateSelected))),Set(dateSelected,DateAdd(Date(ThisItem.Value,Month(dateSelected)+1,1),-1)));UpdateContext({visGalYear:false});Reset(galYear)