カレンダー,どうかく?
前回:http://blogs.wankuma.com/kazuki/archive/2008/01/20/118336.aspx
前回、なんとなく表示されるまで作ったカレンダーだけど、デザイナ上で例外が出たとかいって表示されない。
これは、コンバータの手抜き実装が原因でif文を1ついれてあげるだけでとりあえずOK。namespace WpfCalendar { public class DateTimeConverter : IValueConverter { public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { // 今までnullを気にしてなかった if (value == null) { return new List<DateTime>(); } DateTime date = (DateTime)value; var days = (from day in Enumerable.Range(1, DateTime.DaysInMonth(date.Year, date.Month)) select new DateTime(date.Year, date.Month, day)).ToList(); var first = days.First(); for (int i = 0; i < (int)first.DayOfWeek; i++) { days.Insert(0, days.First().AddDays(-1)); } var last = days.Last(); for (int i = 0; i < 6 - (int)last.DayOfWeek; i++) { days.Add(days.Last().AddDays(1)); } return days; } public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) { throw new NotImplementedException(); } } }
お題が面白かったので for loop を消してみる実験.
(追記) コメント追加してみた.
using System; using System.Linq; using System.Windows.Data; using System.Globalization; namespace WpfCalendar { public class DateTimeConverter : IValueConverter { public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { if (value == null) return Enumerable.Empty<DateTime>().ToList(); var days = from date in Enumerable.Repeat((DateTime)value, 1) // 指定された月に含まれる日付全部 let thisMonth = from day in Enumerable.Range(1, DateTime.DaysInMonth(date.Year, date.Month)) select new DateTime(date.Year, date.Month, day) let first = thisMonth.First() let last = thisMonth.Last() // 最初の週を前の日曜日まで(後ろ向きに)補完(してReverse) let prev = Enumerable.Range(1, 6).Select(offset => first.AddDays(-offset).Date) .TakeWhile(day => day.DayOfWeek != DayOfWeek.Saturday) .Reverse() // 最後の週を次の土曜日まで補完 let next = Enumerable.Range(1, 6).Select(offset => last.AddDays(offset).Date) .TakeWhile(day => day.DayOfWeek != DayOfWeek.Sunday) // 全部連結 from day in prev.Concat(thisMonth).Concat(next) select day; return days.ToList(); } public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) { throw new NotImplementedException(); } } }
(追記)以下も追記.
やっていることは同じだけど複数の文で書いたバージョン.
using System; using System.Linq; using System.Windows.Data; using System.Globalization; namespace WpfCalendar { public class DateTimeConverter : IValueConverter { public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { if (value == null) return Enumerable.Empty<DateTime>().ToList(); var date = (DateTime)value; // 指定された月に含まれる日付全部 var daysInThisMonth = from day in Enumerable.Range(1, DateTime.DaysInMonth(date.Year, date.Month)) select new DateTime(date.Year, date.Month, day); // 指定された月の最初の日 var firstDay = daysInThisMonth.First(); // 指定された月の最後の日 var lastDay = daysInThisMonth.Last(); // 最初の週を前の日曜日まで(後ろ向きに)補完(してReverse) var daysInPrevMonth = Enumerable.Range(1, 6) .Select(offset => firstDay.AddDays(-offset).Date) .TakeWhile(day => day.DayOfWeek != DayOfWeek.Saturday) .Reverse(); // 最後の週を次の土曜日まで補完 var daysInNextMonth = Enumerable.Range(1, 6) .Select(offset => lastDay.AddDays(offset).Date) .TakeWhile(day => day.DayOfWeek != DayOfWeek.Sunday); // 全部連結 return daysInPrevMonth.Concat(daysInThisMonth).Concat(daysInNextMonth).ToList(); } public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) { throw new NotImplementedException(); } } }
(追記2)以下も追記.
こっちは最初からデザインし直した別解.
using System; using System.Linq; using System.Windows.Data; using System.Globalization; namespace WpfCalendar { public class DateTimeConverter : IValueConverter { public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { if (value == null) return Enumerable.Empty<DateTime>().ToList(); var date = (DateTime)value; // 指定された日を含む週の日曜日を返す関数 Func<DateTime, DateTime> getSundayOfTheWeek = theDay => theDay.AddDays(-(int)theDay.DayOfWeek).Date; // 指定された月に含まれる全て週について,その週の日曜日を列挙 var sundays = ( from day in Enumerable.Range(1, DateTime.DaysInMonth(date.Year, date.Month)) select getSundayOfTheWeek(new DateTime(date.Year, date.Month, day)) ).Distinct(); // 各日曜日から始めて一週間を作り,それを連結 return from sunday in sundays from offset in Enumerable.Range(0, 7) select sunday.AddDays(offset).Date; } public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) { throw new NotImplementedException(); } } }