#PowerApps 予定表スクリーンの基本と応用 - 年間カレンダーの表示

#はじめに
[前回の投稿](https://qiita.com/h-nagao/items/7d6efd8f682015fd31ab)では、PowerAppsのカレンダースクリーンのテンプレートがどのようにして動作しているのかを解説しました。
今回の投稿では後半戦、カレンダーのテンプレートを応用して、年間カレンダーを表示するアプリの作成方法を解説します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/202779/8d9c02aa-d42e-7392-31ed-276445ea931e.png)

#おさらい
カレンダースクリーンでは3つのギャラリーを利用して、曜日、日付、イベントを表示していました。
このうち日付を表示するMonthdayGalleryについておさらいしておきます。

MonthdayGalleryではGalleryのItemsプロパティに[0,...,41]の0から始まる42の数字がセットされていました。
カレンダーの実際の日付を表示するために、以下の変数が定義されていました。

```
//選択された日付をもとに、その月の初日を求める   
Set(_firstDayOfMonth, DateAdd(Today(), 1 - Day(Today()), Days));

//表示する範囲の初日を決定(1日が週の途中なら前月おわりも表示対象)
Set(_firstDayInView, DateAdd(_firstDayOfMonth, -(Weekday(_firstDayOfMonth) - 2 + 1), Days));

//月の最終日を決定
Set(_lastDayOfMonth, DateAdd(DateAdd(_firstDayOfMonth, 1, Months), -1, Days));
```
～ここまでで基本のおさらい終わり～

#年間カレンダーを作る
年間カレンダーを作成するためには、単純にカレンダースクリーンのパーツ群を12か月分用意します。
そして表示用の変数をx12セット用意すればいいです。
このような変数群をすべて_firstDayOfMonth_Jan～_firstDayOfMonth_Decみたいに用意するのはアプリとしてかなりメンテナンス性が悪いので、ここではCollectionを利用することにします。
利用するCollectionは以下のようなデータです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/202779/fc01ff83-1630-5972-de7c-2a41b1bfbfeb.png)
単純にテンプレートで定義していた変数を列としています。

また、年間カレンダーでは表示する年を変更したいので、変数として ```_selectedYear``` を定義することにします。

##データ準備
毎回ベタ打ちでCollectionの3つの列を作成してもよいのですが、firstDayInViewは年によって変わりますし、うるう年を考慮するとlastDayOfMonthも年によって変わります。
これを踏まえて、以下のステップでCollectionを作成していきます。

1. 表示年を指定
2. 表示年と[1,2,3...,12]のテーブルを利用して**_firstDayOfMonthだけを含む**仮Collectionを作成
3. AddColumns関数を利用して、_firstDayInView **列** と_lastDayOfMonth **列** を追加

_firstDayInView列の定義と、_lastDayOfMonth列の定義はそのままカレンダースクリーンからもらってきます。
空白のアプリを作成して、ボタンを追加し、ボタンのOnSelectに以下のような数式をセットしましょう。

```Button1.OnSelect
//初期の表示年は今年
UpdateContext({_selectedYear:Year(Today())});

//まず2019/1/1~2019/12/1まで、12か月の初日を収めた仮のCollectionを作成
Clear(colTmp);
ForAll([1,2,3,4,5,6,7,8,9,10,11,12],
    Collect(colTmp,{_firstDayOfMonth:Date(_selectedYear,Value,1)})
);
//カレンダーの表示に必要な_firstDayInViewと_lastDayOfMonthを列としてCollectionに追加
ClearCollect(colCalendar,
    AddColumns(colTmp,
        //それぞれの定義はテンプレートと同一
        "_firstDayInView",DateAdd(_firstDayOfMonth, -(Weekday(_firstDayOfMonth) - 2 + 1), Days),
        "_lastDayOfMonth",DateAdd(DateAdd(_firstDayOfMonth, 1, Months), -1, Days)
    )
);
//あとのためカレンダー表示・非表示用の変数を用意
UpdateContext({_calendarVisible:true})
```

※最後の変数はあとで出てきます。

できたCollectionを確認するためにスクリーンにGalleryを追加し、ItemsにcolCalendar (作成した最終的なCollection)を指定すると以下のような表示になることがわかります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/202779/391fe746-779e-6c71-9565-1486d258e699.png)
たしかに目的のCollectionが作成できました。

##カレンダーを移植する
データの準備はできたので、年間カレンダーを表示していきます。
データに基づいて反復する要素を表示するので、それを表示するためのGalleryをスクリーンに追加しましょう。(ギャラリーの中身もMonthDayGalleryなので、ここでは親ギャラリーを用意して、入れ子にするイメージです)
続けて、新しい画面の挿入から、予定表スクリーンを選択し、Screen2を追加します。
この時点で以下のような画面・アプリ構成になっているはずです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/202779/f5e21f0b-135a-b8ca-5185-5bee60a56296.png)

あとはScreen2からScreen1に3種のGalleryをコピーしたらほとんど完成です。
全部まとめてコピー＆ペーストするとレイアウトが崩壊するので、一個ずつコピーしてください。
最終的には下図のように、親Gallery中に3種のGalleryが挿入された状態になります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/202779/4ca5b6c6-8190-7f0e-6c83-dac844cab568.png)

この状態で、元の変数の依存性を取り除き、作成したCollectionを利用するようにしたいので、Screen2を削除します。
エラーが出ている部分がカレンダースクリーンの変数に依存した箇所ですので、これらを修正していきます。(でもこの時点でほとんどできているのが見てわかるかとおもいます)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/202779/4891633e-1380-b2ac-a615-9c68673208cf.png)

##調節
まずはMonthDayGalleryのラベルのColorプロパティです。これは日付を表示するラベルで、元の画面にあったパーツの色を参照しているので、エラーになっています。
(LblMonthSelected1.Color)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/202779/2e0c1912-845b-1e8d-b986-46a7a84337da.png)
If文の最後であることから、何の条件にも引っかからなかったときの既定色であることがわかります。
ここはColor.Blackに置き換えましょう。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/202779/1e7a3899-ba40-89ac-6a93-72b0aebc024f.png)

MonthDayGalleryでもう一つエラーになっているのは、「その日にイベントがあったら●を表示する」というCircleです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/202779/15c44ad9-1175-659e-8199-da9930bf941b.png)
あとでほかの方法でイベントがわかるようにするので、ここではCircleごと消します。
ここまでで、月の名前表示やイベントの表示を除いて完成です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/202779/7e93c6f2-a119-aafc-a9bf-315ecd021cee.png)

##予定表の取得
各月のイベントを表示するためにはOffice365Outlookコネクターを利用します。このスクリーンは年間カレンダーになっているので、イベントも1年分を取得し、Collectionに格納していきます。

スクリーンにボタンをもう一つ追加して、OnSelectに以下を記載します。

```
//Outlook予定表テーブルを取得してCollectionを作成
ClearCollect(CalTables,Office365Outlook.CalendarGetTables());

//1年分のイベント一覧をCollectionに格納
ClearCollect(Events,
    RenameColumns(
        Office365Outlook.GetEventsCalendarViewV2(
            First(First(CalTables).value).Name, //予定表の最初のテーブル
            Text(DateAdd(Date(_selectedYear,1,1),-1),UTC), //選択した年の1/1 (UTC)の一日前
            Text(Date(_selectedYear,12,31),UTC)//選択した年の12/31
        ).value,
    "Start","StartDate") //ほかの変数・関数と干渉を避けるために列名の変更
)
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/202779/892daeec-efc2-47d6-bc53-66b73656f7e1.png)

これで準備完了です。1年分のイベントが取得できたので、あとは親Gallery (Gallery1)の予定表Gallery (CalendarEventsGallery)にそれぞれの月でフィルターした結果を表示していきましょう。
Outlook予定表から得られたイベントはStart,EndがUTCで入っているので、1日ずつずらします。そのうえで、イベントの開始が月の初日から、月の最終日までのイベントでフィルターをします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/202779/f5a3a189-7e97-a0de-4ccb-70256450eb19.png)

あと2つまでエラーが減りました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/202779/f049c11f-d38f-db2b-e13c-d8d873131098.png)
どちらもStartをStartDateに名前変更した影響なので、
```ThisItem.Start``` を ```ThisItem.StartDate``` に置き換えてください。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/202779/1c06de26-229b-b428-3f4c-25de45919892.png)

エラーも解消されています。
最後に、月の名前を表示するラベルを追加して完成です。

```Label.Text
Text(ThisItem._firstDayOfMonth,"[$-en-US]mmm")
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/202779/6d889a0f-ef83-788f-997f-b25efbdc9a7f.png)

##最後に
以上で、今年一年のカレンダーと各月の予定を表示するアプリが完成しました。
さらに発展させて、年を変更できるようにしたい場合には、ドロップダウンを用意して、そのOnChangeで_selectedYearを設定するようにしてあげれば、表示年を可変にできます。

ご質問は私のTwitterにメンションしてください。