2
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

WPFのDatePickerに選択可能な日付を設定する

Last updated at Posted at 2021-02-06

実現したいこと

WPF標準のDatePickerはユーザーにカレンダーから日付を選ばせることができるイカしたコントロールですが、そのまま使うと全ての日付が選択できてしまいます。
そこで、BlackoutDatesプロパティに選べない日付を設定することで制御できますが、実際のところ選べる日付をBinding等で設定したいときもあり、今回はそれを実現します。

どうやってやるのか

添付プロパティを使います。以上。
なお私の手元ではMaterialDesignInXAMLを導入しているので、標準のコントロールと見た目が異なりますが、本質的にやっていることは同じです。気になる方はこちらから導入してみてください。

添付プロパティを作成する

ここではプロジェクトのViewsフォルダの下にBehaviorsフォルダを作って、その中にDatePickerAssistという名前のクラスを作成します。そこにDatePickerにくっつける添付プロパティSelectableDatesを定義します。ちなみにいつも全部書くのは大変なので、Visual Studioのpropaスニペットを使っています。

DatePickerAssist.cs
namespace DatePickerSample.Views.Behaviors
{
    public class DatePickerAssist
    {
        // Getter
        public static IEnumerable GetSelectableDates(DependencyObject obj)
        {
            return (IEnumerable)obj.GetValue(SelectableDatesProperty);
        }

        // Setter
        public static void SetSelectableDates(DependencyObject obj, IEnumerable value)
        {
            obj.SetValue(SelectableDatesProperty, value);
        }

        // 添付プロパティを登録
        public static readonly DependencyProperty SelectableDatesProperty = DependencyProperty.RegisterAttached(
            "SelectableDates",
            typeof(IEnumerable),
            typeof(DatePickerAssist),
            new PropertyMetadata(null, OnSelectableDatesChanged));

        // コールバック関数の定義
        private static void OnSelectableDatesChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            if (d is DatePicker datePicker && e.NewValue is IEnumerable<DateTime> dates)
            {
                var minDate = dates.Min();
                var maxDate = dates.Max();

                // 区間の最初と最後を設定
                datePicker.DisplayDateStart = minDate;
                datePicker.DisplayDateEnd = maxDate;

                // 選択できる日付の区間内は、1日ずつ判定して、含まれていなければ BlackoutDates に追加
                for (int i = 1; i < (maxDate - minDate).Days; i++)
                {
                    if (!dates.Contains(minDate.AddDays(i)))
                    {
                        datePicker.BlackoutDates.Add(new CalendarDateRange(minDate.AddDays(i)));
                    }
                }
            }
        }
    }
}

添付プロパティを設定する

今回はViewModelでObservableCollection<DateTime>型のプロパティとして選択可能な日付を定義して、そいつを↑で作った添付プロパティにBindingしてあげます。

MainWindowViewModel.cs
namespace DatePickerSample.ViewModels
{
    public class MainWindowViewModel
    {
        public ObservableCollection<DateTime> Dates { get; }

        public MainWindowViewModel()
        {
            Dates = new ObservableCollection<DateTime>()
            {
                new DateTime(2021, 2, 2),
                new DateTime(2021, 2, 3),
                new DateTime(2021, 2, 4),
                new DateTime(2021, 2, 10),
                new DateTime(2021, 2, 16),
            };
        }
    }
}

とりあえず5日分だけ定義してあげました。こいつをBindingします。以下ではBehaviors名前空間をbで呼び出しています。

MainWindow.xaml
<Window x:Class="DatePickerSample.Views.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:md="http://materialdesigninxaml.net/winfx/xaml/themes"
        xmlns:b="clr-namespace:DatePickerSample.Views.Behaviors"
        xmlns:vm="clr-namespace:DatePickerSample.ViewModels"
        mc:Ignorable="d"
        Title="MainWindow" Height="400" Width="300">
    <Window.DataContext>
        <vm:MainWindowViewModel/>
    </Window.DataContext>
    <Grid>
        <DatePicker Width="100" HorizontalAlignment="Left" VerticalAlignment="Top" Margin="10,10,0,0"
                    md:CalendarAssist.IsHeaderVisible="False"  b:DatePickerAssist.SelectableDates="{Binding Dates}"/>
    </Grid>
</Window>

実行するとこんな感じ。Datesプロパティで設定した日付だけがきちんと選べるようになっています。この実装だと2020/2/1以前と2020/2/17以降はそもそも表示されず、期間内で選べない日はグレーアウトになります。
※デフォルトのDatePickerコントロールでは、選べない日付には×(バツ)マークが付きます。
image.png

まとめ

  • 添付プロパティはめちゃ便利
  • 結局 MaterialDesignInXAML がすごく綺麗
2
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?