Xamarin.Forms で Nuget のカレンダーを使う(2)


はじめに

この記事は、Xamarin.Forms で Nuget のカレンダーを使う(1)の続きです。

やりたいこと

用意したカレンダーに対して
1. 日付を選択したら画面遷移したい
2. 祝日とか土日を色分けして表示したい

まとめ

2 については祝日用の Nuget パッケージを新たにインスコする必要がありましたが、1 も 2 もとりあえず期待通り動作させることができました。ただ、ビジュアル面で課題 (当月外の土日も色分けされたまま) が残っていて、恐らくですがカレンダーパッケージのアプデ待ちという感じです。

1.日付選択で画面遷移

MPF は勉強中ということもあるので、とりあえずは最初に覚えた (多分) ベーシックな方法で遷移させます。まず x:name 属性で名前を定義しておきます。で、次に DateClicked 属性でコード内のハンドラを指定しておきます。

Sample.xaml
            <controls:Calendar x:Name="_calendar"
                               Padding="10,0,10,0" StartDay="Sunday" 
                               SelectedBorderWidth="4"
                               DisabledBorderColor="Black"
                               TitleLabelTextColor="Purple" 
                               TitleLabelFormat="yyyy年MM月" 
                               DateClicked="OnDateClickHandler" />

XAML に対応する Sample.cs には、カレンダーのメソッドを利用するので、最初に XamForms.Controls パッケージをインポートしておきます。次に Sample クラスコード内に OnDateClickHandler メソッドを作って、中に処理するコードを書いておきます。今回の目的は別ページに飛ばしたいので、 Navigation 機能を使用します。

Sample.cs
using XamForms.Controls;
:

        private async void OnDateClickHandler(object sender, EventArgs e)
        {
            await this.Navigation.PushModalAsync(new NextPage(_calendar.SelectedDates));
        }

遷移先ページの用意

上記の New されたインスタンスの NextPage は遷移先のページで、ContentPage を継承した NextPage は別に作っておく必要があります。

今回は選択した年月日を引数にして、その内容を表示するだけにしています。Sample.cs に記載した NextPage のコンストラクタ引数になっているのがそれです。

NextPage.xaml
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:viewModels="clr-namespace:App1;"
             x:Class="App1.NextPage">

    <Label x:Name="_calendarDays"
           FontSize="30" HorizontalOptions="Center" />
</ContentPage>
NextPage.cs
using Xamarin.Forms;
using Xamarin.Forms.Xaml;

namespace App1
{
    [XamlCompilation(XamlCompilationOptions.Compile)]
    public partial class NextPage : ContentPage
    {
        public NextPage (List<DateTime> dt)
        {
            InitializeComponent ();

            _calendarDays.Text = dt[0].ToString("yyyy年MM月dd日");
        }
    }
}

実行結果

2/27 を押下した結果です。



2.祝日とか土日を色分けして表示したい

a.祝日の設定

事前準備

1.[ツール]->[NuGetパッケージマネージャ]->[ソリューションの NuGet パッケージの管理]を選択

2.祝日を取得するために、PublicHoliday という Nuget パッケージを取得します。v2.0.1 を選択しました。

SpecialDates

XamForms.Controls.Calendar パッケージは SpecialDates というメソッドを持っていて、特定の日にちをカスタマイズすることができるようです。1 の実行結果で、2/27 の枠やテキスト色がカラフルになっていますが、実は以下のようなコードを書くことでカスタマイズしています。

Sample.cs
            _calendar.SpecialDates = new List<SpecialDate>{
            new SpecialDate(DateTime.Now)
            {
                   TextColor = Color.Green, BorderColor=Color.Green,
                   BorderWidth =8, Selectable = true }
            };

同じように土日や祝日も SpecialDates で色付けしてあげれば良いというのが、このパッケージを使った場合は基本になります。そんな便利 Getter は(多分)カレンダーパッケージにはなさそうなので、Nuget から祝日パッケージを取得。PublicHoliday をインポートした上で、以下のようにメソッドを作ってコンストラクタから呼んであげます。

Sample.cs
using PublicHoliday;
:
namespace App1
{
    [XamlCompilation(XamlCompilationOptions.Compile)]
    public partial class Sample : ContentPage
    {
        public Sample()
        {
            InitializeComponent();

            _calendar.SpecialDates = new List<SpecialDate>{
            new SpecialDate(DateTime.Now)
            {
                   TextColor = Color.Green, BorderColor=Color.Green,
                   BorderWidth =8, Selectable = true }
            };

            SetHoliday(2018);
        }

        /// <summary>
        /// 祝日の色設定
        /// </summary>
        /// <param name="year">対象年</param>
        private void SetHoliday(int year)
        {
            IList<DateTime> result = new JapanPublicHoliday().PublicHolidays(year);

            foreach (var holiday in result)
            {
                _calendar.SpecialDates.Add(new SpecialDate(holiday)
                {
                    TextColor = Color.Red
                });
            }
        }

2018 年の日本の祝日を SpecialDates として割り付けています。

実行結果

2018 年の祝日、この画面の例だと GW 連休を見ることができます。ただこれだと 2017 年に移動すると祝日が無いままなんですよね。実運用を考えるなら DateTime.Now の年から前後 x 年分祝日を割り当てて、カレンダーでそれより前後への移動はできないようにするとかでしょうか?

b.土日の設定

さて土日の設定ですが、週末を取得するためのいい感じのパッケージが見つからないので、自作するしか無さそうです。でも閏年とかあるのにどうやってその月の最終日を判別するんだ…と悩んでいたのですが、なんのとこはない、上で直前まで使っていた DateTime という素敵な型があるんでしたね。

以下は多少ごり押し感はありますが、土日の設定メソッド。土曜日を青文字、日曜日を赤文字にしています。DateTime.AddDays でインクリメントしていけば勝手に 1/31 の次は 2/1 という風にやってくれるので便利。これもコンストラクタから呼びだします。

Sample.cs
        /// <summary>
        /// 土日の色設定
        /// </summary>
        /// <param name="year">対象年</param>
        private void SetWeekend(int year)
        {
            DateTime startDate = new DateTime(year, 1, 1);
            DateTime endDate = new DateTime(year, 12, 31);

            /* 元旦から大晦日まで1日ずつ曜日確認 */
            for (var day = startDate.Date; day.Date <= endDate.Date; day = day.AddDays(1))
            {
                if (DayOfWeek.Saturday == day.DayOfWeek)
                {
                    _calendar.SpecialDates.Add(new SpecialDate(day)
                    {
                        TextColor = Color.Blue
                    });
                }
                else if (DayOfWeek.Sunday == day.DayOfWeek)
                {
                    _calendar.SpecialDates.Add(new SpecialDate(day)
                    {
                        TextColor = Color.Red
                    });
                }
            }
        }

実行結果

こんな感じ。サラッとかけるしそりゃパッケージなんてないわ。
なお、SpecialDates は先勝ち設定ぽいので、色の優先度が高い方を先に呼んでください。土曜日は青文字ですが、祝日なので赤文字に上書きしたいなら SetWeekend より先に SetHoliday メソッドを呼ぶ感じです。

課題

今回はコードビハインド多めで書いていますが、SpecialDates なんかは XAML の属性に記載してデータバインディングする方が MPF っぽい感じがします。

また、選択月以外の土日祝日文字もカラーになっているのが正直イマイチなのですが、 DatesTextColorOutsideMonth 属性をどのタイミングで書いてもSpecialDates に上書きされてしまうので、もしかしたらまだ非対応なのかも知れません。

issue にも似たようなポストがあったのですが、反応なかったり微妙なやり取りだったりで未解決。SpecialDates.Select なんてメソッド、v1.1.1 でもないんですけどね。.NET Framework の互換性の問題とかなんでしょうか。

https://github.com/rebeccaXam/XamForms.Controls.Calendar/issues/46
https://github.com/rebeccaXam/XamForms.Controls.Calendar/issues/65