かずきのBlog

C#やJavaやRubyとメモ書き

目次

Blog 利用状況

ニュース

わんくまBlogが不安定になったため、前に書いてたはてなダイアリーにメインを移動します。
かずきのBlog@Hatena
技術的なネタは、こちらにも、はてなへのリンクという形で掲載しますが、雑多ネタははてなダイアリーだけに掲載することが多いと思います。
コメント
プログラマ的自己紹介
お気に入りのツール/IDE
プロフィール
経歴
広告
アクセサリ

書庫

日記カテゴリ

[C#][WPF]WPFでカレンダー表示する部品って無いんだよね

カレンダーが無いなら作ればいいじゃない?
というわけで作ってみよう。

超汎用的なものを作るのはめんどくさいので、仕様はかなり絞ってみようと思う。

  1. 年月日をDateTime型で指定すると、その月のカレンダーを表示して、指定した日を選択状態にする
  2. 曜日は日曜日はじまり
  3. もちろん選択された日付をDateTime型で取得できる

前の月とか次の月や翌年、前月はとりあえずスルー。
とりあえず、日付群の中から1つ選ぶということでListBoxでいいだろう。

30 31 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 1 2

こんな感じに表示させたいので、リストボックスのテンプレートをいじる。
カラムが7つのUniformGridを2つほどStackPanelに乗せる。
1つ目のGridには、日~土までを。
2つ目のGridはIsItemsHostをTrueにして、ListBoxのItemを表示するのに使う。

		<ListBox Name="calendar">
			<ListBox.Resources>
				<Style TargetType="{x:Type TextBlock}">
					<Setter Property="TextAlignment" Value="Right" />
				</Style>
			</ListBox.Resources>
			<ListBox.Template>
				<ControlTemplate TargetType="{x:Type ListBox}">
					<StackPanel>
						<UniformGrid Columns="7">
							<TextBlock Text="日" />
							<TextBlock Text="月" />
							<TextBlock Text="火" />
							<TextBlock Text="水" />
							<TextBlock Text="木" />
							<TextBlock Text="金" />
							<TextBlock Text="土" />
						</UniformGrid>
						<UniformGrid Columns="7" IsItemsHost="True">
						</UniformGrid>
					</StackPanel>
				</ControlTemplate>
			</ListBox.Template>
		</ListBox>

これをWindowに乗せると、こんな感じになる。

image

とりあえずいい感じ。
んで、ListBoxのDataContextにDateTimeを設定すると、その月のカレンダーが表示されるようにしてみようと思う。
というわけで、DateTimeをもとに、その月(と間を埋めるために必要な前月と翌月の数日)の日付の列挙に変換するコンバータを用意する。

namespace WpfCalendar
{
    public class DateTimeConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            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();
        }
    }

}

このコンバータを使ってListBoxのItemsSourceにDataContextをバインドする。

		<ListBox Name="calendar">
			<ListBox.ItemsSource>
				<Binding>
					<Binding.Converter>
						<c:DateTimeConverter xmlns:c="clr-namespace:WpfCalendar" />
					</Binding.Converter>
				</Binding>
			</ListBox.ItemsSource>
			<ListBox.Resources>
				<Style TargetType="{x:Type TextBlock}">
					<Setter Property="TextAlignment" Value="Right" />
				</Style>
			</ListBox.Resources>
			<ListBox.Template>
				<ControlTemplate TargetType="{x:Type ListBox}">
					<StackPanel>
						<UniformGrid Columns="7">
							<TextBlock Text="日" />
							<TextBlock Text="月" />
							<TextBlock Text="火" />
							<TextBlock Text="水" />
							<TextBlock Text="木" />
							<TextBlock Text="金" />
							<TextBlock Text="土" />
						</UniformGrid>
						<UniformGrid Columns="7" IsItemsHost="True">
						</UniformGrid>
					</StackPanel>
				</ControlTemplate>
			</ListBox.Template>
		</ListBox>

この時点で実行すると、↓のような感じになる。

image

段々それっぽくなってきてるかな。
後は、ListBoxのItemTemplateとItemContainerStyleで日付部分を右詰めで表示してあげる。

		<ListBox Name="calendar"
		    xmlns:sys="clr-namespace:System;assembly=mscorlib">
			<ListBox.ItemsSource>
				<Binding>
					<Binding.Converter>
						<c:DateTimeConverter xmlns:c="clr-namespace:WpfCalendar" />
					</Binding.Converter>
				</Binding>
			</ListBox.ItemsSource>
			<ListBox.Resources>
				<Style TargetType="{x:Type TextBlock}">
					<Setter Property="TextAlignment" Value="Right" />
				</Style>
			</ListBox.Resources>
			<ListBox.Template>
				<ControlTemplate TargetType="{x:Type ListBox}">
					<StackPanel>
						<UniformGrid Columns="7">
							<TextBlock Text="日" />
							<TextBlock Text="月" />
							<TextBlock Text="火" />
							<TextBlock Text="水" />
							<TextBlock Text="木" />
							<TextBlock Text="金" />
							<TextBlock Text="土" />
						</UniformGrid>
						<UniformGrid Columns="7" IsItemsHost="True">
						</UniformGrid>
					</StackPanel>
				</ControlTemplate>
			</ListBox.Template>
			<ListBox.ItemContainerStyle>
				<Style TargetType="{x:Type ListBoxItem}">
					<Setter Property="HorizontalContentAlignment" Value="Right" />
				</Style>
			</ListBox.ItemContainerStyle>
			<ListBox.ItemTemplate>
				<DataTemplate DataType="{x:Type sys:DateTime}">
					<TextBlock Text="{Binding Day}" />
				</DataTemplate>
			</ListBox.ItemTemplate>
		</ListBox>

実行すると、カレンダーっぽくなってきた。

image

今日は眠いのでここまで。

投稿日時 : 2008年1月20日 3:12

Feedback

# [C#][WPF]カレンダーを作ってみよう 2008/01/20 12:03 かずきのBlog

[C#][WPF]カレンダーを作ってみよう

# re: [C#][WPF]WPFでカレンダー表示する部品って無いんだよね 2008/01/21 1:28 nsharp

はじめまして。( ゚д゚)ノ

日付の列挙に関して:
このサンプルの場合、クエリー実行は後まわしにした方がロジックがすっきりしますよ。

こんな感じです。

  var date = (DateTime) value;

  // 該当月の1日と末日
  var firstDay = new DateTime(date.Year, date.Month, 1);
  var lastDay = new DateTime(date.Year, date.Month, DateTime.DaysInMonth(date.Year, date.Month));

  // カレンダー表示の開始日と終了日
  var startDate = firstDay.AddDays(DayOfWeek.Sunday - firstDay.DayOfWeek);
  var endDate = lastDay.AddDays(DayOfWeek.Saturday - lastDay.DayOfWeek);

  return Enumerable.Range(0, (endDate - startDate).Days + 1).Select(n => startDate.AddDays(n));

# re: [C#][WPF]WPFでカレンダー表示する部品って無いんだよね 2008/01/21 6:35 かずき

>nsharpさん
確かにそうですね~。
断然見やすくなりますね(^^

# re: [C#][WPF]WPFでカレンダー表示する部品って無いんだよね 2008/01/21 14:02 nsharp

LINQフル活用で、ということでしたら、無限リストを相手にしてみるのもおもしろいかもしれません。

  static IEnumerable<DateTime> Past(this DateTime d) {
    while (true) {
      d = d.AddDays(-1);
      yield return d;
    }
  }

  static IEnumerable<DateTime> Future(this DateTime d) {
    while (true) {
      d = d.AddDays(1);
      yield return d;
    }
  }

んで、

  var date = (DateTime) value;

  // 該当月の1日と末日
  var firstDay = new DateTime(date.Year, date.Month, 1);
  var lastDay = firstDay.AddMonths(1).AddDays(-1);

  var past = date.Past().TakeWhile(d => d >= firstDay || d.DayOfWeek != DayOfWeek.Saturday).Reverse();
  var present = Enumerable.Repeat(date, 1);
  var future = date.Future().TakeWhile(d => d <= lastDay || d.DayOfWeek != DayOfWeek.Sunday);

  return past.Concat(present).Concat(future);

まあ、お遊びということで・・・。

# ???C#????????????????????????????????????????????????1???WPF??? | ???????????? 2011/05/31 10:45 Pingback/TrackBack

???C#????????????????????????????????????????????????1???WPF??? | ????????????

# sac longchamp pas cher 2012/10/17 23:14 http://www.sacslongchamppascher2013.com

You have brought up a very superb details , thankyou for the post.

# uDPIVLJOCkxnXdMQuyy 2014/08/28 1:31 http://crorkz.com/

lA3wCM Hello, you used to write great, but the last several posts have been kinda boring??? I miss your great writings. Past several posts are just a little out of track! come on!

# re: [WPF][C#]テキストボックスをフォーカスがくると全選択状態にしたい 2016/01/29 16:35 wengdongdong

20161.29wengdongdong
http://www.truereligion-outlet.net.co
http://www.michaelkorshandbags.cc
http://coach.eshopping.us.com
http://www.chiflatiron.in.net
http://www.nike-airmax.org.uk
http://www.ralphlaurenuk.me.uk
http://www.nike-airmax90.me.uk
http://www.coachoutlet-storeonline.in.net
http://www.quality-outlet.com
http://www.get-coachoutletonline.org
http://www.louboutinshoes.cc
http://www.mymichaelkorssoutlets.org
http://www.mizunorunningshoes.in.net
http://jordan.outletmalls.us.com
http://canadagoose.adsmall.us.com
http://www.basketballshoes.net.co
http://www.louisvuittonoutlets.name
http://www.toms.in.net
http://www.coach-factory-outlet-online.us.com
http://www.hermesuk.org.uk
http://www.tomsshoes.us.com
http://www.longchampoutlet.net.co
http://www.adidas-shoes.me.uk
http://www.saclongchamp-solde.fr
http://www.coachoutlet-storeonline.us.com
http://www.fitflops.us
http://www.ugg-boots-australia.com
http://www.nikefreeruns.uk
http://tiffany.outletmalls.us.com
http://www.truereligion-outlet.com
http://www.katespadeoutletsale.in.net
http://www.louisvuitton-borse.it
http://www.sac-longchamppliage.fr
http://www.michael-kors-handbags.uk
http://www.nikerosherunwomen.me.uk
http://www.canadagooseuk.me.uk
http://oakley.shopgioia.com
http://christianlouboutin.shopgioia.com
http://www.jordan13.org
http://www.michaelkorsoutletoff.in.net
http://www.chaussure-louboutin.fr
http://www.canadagooseoutlet.cc
http://www.adidas-trainers.me.uk
http://www.michael-korsuk.me.uk
http://www.michaelkors-outletonlines.in.net
http://www.coach-factoryoutlet.com.co
http://truereligion.adsmall.us.com
http://www.outlet-toms.cc
http://toms.adsmall.us.com
http://www.michaelkors-bags.me.uk
http://www.airforce1-nike.fr
http://www.oakleysunglassesfake.in.net
http://www.chaussurelouboutin-pascher.fr
http://www.nikesb.uk
http://www.vipmichaelkors-outletonline.com
http://www.nike-blazerlow.fr
http://www.rolex-replicawatches.com.co
http://www.ugg-boots-store.com
http://www.ugg-sale.in.net
http://www.jordan-femmepascher.fr
http://christianlouboutin.outletmalls.us.com
http://www.toms-outlet.cc
http://www.kate-spadeoutlet.us
http://www.mcmhandbags.name
http://www.true-religionjeans.net.co
http://www.cheap-uggsonsale.in.net
http://www.nikestoreuk.me.uk
http://www.ralph-lauren.me.uk
http://www.niketrainers.com.co
http://www.timberlanduk.me.uk
http://www.kate-spadeoutlet.com.co
http://www.sunglass-outlet.us
コメント

タイトル  
名前  
Url
コメント