【WPF】自作カレンダー その3(年間カレンダー)

2017年10月17日C#,開発

おはようございます。

自作カレンダーその3です。
今回は、ひとまず目標にしていた年間カレンダーを作成します。

動的に生成できるようにしたのが幸いして?割と簡単に実装することができました。

前回のプログラムはこちら。

スポンサーリンク

スタイルの変更

画面サイズ変更時に各月のカレンダーも一緒にサイズが変わるようにします。

StyleDic.xaml(抜粋)

    <!-- グループ:通常-->
    <Style x:Key="gp-normal" TargetType="GroupBox" >
        <Setter Property="VerticalAlignment" Value="Stretch" />
        <Setter Property="HorizontalAlignment" Value="Stretch" />
        <Setter Property="Background" Value="#FFFFFFFF" />
        <Setter Property="Foreground" Value="#FF777777" />
        <Setter Property="Height" Value="Auto" />
        <Setter Property="Width" Value="Auto" />
        <Setter Property="Margin" Value="2, 2, 2, 2" />
    </Style>
    
    <!-- グリッド:画面リサイズ対応 -->
    <Style x:Key="grid-stretch" TargetType="Grid" >
        <Setter Property="VerticalAlignment" Value="Stretch" />
        <Setter Property="HorizontalAlignment" Value="Stretch" />
        <Setter Property="Height" Value="Auto" />
        <Setter Property="Width" Value="Auto" />
    </Style>
    
    <!-- グリッド:カレンダー格納用-->
    <Style x:Key="grid-calendar-container" TargetType="Grid" BasedOn="{StaticResource grid-stretch}">
        <Setter Property="Background" Value="#FFFFFFFF" />
        <Setter Property="Margin" Value="10, 55, 10, 10" />
    </Style>

    <!-- グリッド:カレンダー -->
    <Style x:Key="grid-calendar" TargetType="Grid"  BasedOn="{StaticResource grid-stretch}">
        <Setter Property="Background" Value="#FFFFFFFF" />
        <Setter Property="Margin" Value="0, 0, 0, 0" />
    </Style>

カレンダー格納グリッドのスタイル追加し、グループボックスとカレンダーグリッドの幅と高さを「Auto」、縦横位置を「Stretch」に変更。

画面の変更

こちらも、サイズ変更の対応と、年月ボックスを年ボックスに変更します。

MainWindow.xaml

<Mah:MetroWindow x:Class="CalendarSample.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:local="clr-namespace:CalendarSample"
        xmlns:Mah="clr-namespace:MahApps.Metro.Controls;assembly=MahApps.Metro"
        GlowBrush="{DynamicResource AccentColorBrush}"
        mc:Ignorable="d"
        WindowStartupLocation="CenterScreen" 
        Title="カレンダーサンプル" Width="1024" Height="768">
    <Window.Resources>
        <ResourceDictionary Source="/Style/StyleDic.xaml"/>
    </Window.Resources>
    <DockPanel >
        <Grid x:Name="MainContainer" Height="Auto" Width="Auto" >
            <Label x:Name="lbYear" Content="対象年:" HorizontalAlignment="Left" Margin="10,20,0,0" VerticalAlignment="Top" />
            <ComboBox x:Name="cbYear" HorizontalAlignment="Left" Margin="61,20,0,0" VerticalAlignment="Top" Width="120" SelectionChanged="cbYear_SelectionChanged"/>
            <Label x:Name="lbDate" Content="選択された日付:" HorizontalAlignment="Left" Margin="222,20,0,0" VerticalAlignment="Top"/>
            <Label x:Name="lbSelectedDate" Content="" HorizontalAlignment="Left" Margin="333,20,0,0" VerticalAlignment="Top"/>
            <Grid x:Name="CalendarContainer" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Height="Auto" Margin="0,53,0,0" Width="Auto"/>
        </Grid>
    </DockPanel>
</Mah:MetroWindow>

全体を「DocPanel」で囲み、画面の枠組みとなるグリッドの幅と高さを「Auto」に、
カレンダー格納グリッドの幅と高さを「Auto」、縦横位置を「Strech」に変更します。

プログラム修正

新規クラス追加

年コンボボックス用のクラスを追加します。

YearInfo.cs

    using System;
    using System.Collections.Generic;
    using System.IO;
    using System.Linq;
    using System.Text;
    
    namespace CalendarSample
    {
        /// <summary>
        /// 年情報
        /// </summary>
        public class YearInfo
        {
    
            /// <summary>
            /// コンストラクタ
            /// </summary>
            /// <param name="yearMonth"></param>
            public YearInfo(String year)
            {
                this.Year = year;
                this.YearWithKanji = year + "年";
            }
    
            /// <summary>
            /// 年
            /// </summary>
            public String Year { set; get; }
    
            /// <summary>
            /// 年(YYYY年)
            /// </summary>
            public String YearWithKanji { set; get; }
        }
    }

 

月、日付情報クラスの修正

月情報クラスを年情報クラスの派生クラスに変更し、それに伴い日付情報クラスも変更します。

MonthInfo.cs

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    
    namespace CalendarSample
    {
        /// <summary>
        /// 年月情報
        /// </summary>
        public class MonthInfo : YearInfo
        {
            /// <summary>
            /// コンストラクタ
            /// </summary>
            /// <param name="yearMonth"></param>
            public MonthInfo(String year, String month) : base(year)
            {
                this.Month = month;
                this.YearMonth = year + month;
                this.YearMonthWithKanji = year + "年" + month + "月";
            }
    
            public String Month { set; get; }
            /// <summary>
            /// 年月
            /// </summary>
            public String YearMonth { set; get; }
    
            /// <summary>
            /// 年月(YYYY年MM月)
            /// </summary>
            public String YearMonthWithKanji { set; get; }
    
        }
    }
    

DateInfo.cs

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    
    namespace CalendarSample
    {
        /// <summary>
        /// 日付情報
        /// </summary>
        public class DateInfo : MonthInfo
        {
            /// <summary>
            /// コンストラクタ
            /// </summary>
            /// <param name="yearMonth"></param>
            /// <param name="day"></param>
            public DateInfo(String year, String month,  String day) : base(year, month)
            {
                this.YearMonthDay = year+ month + day;
                this.Date = new DateTime(int.Parse(Year), int.Parse(Month), int.Parse(day));
            }
    
            public DateTime Date { set; get; }
    
            /// <summary>
            /// 年月日
            /// </summary>
            public String YearMonthDay { set; get; }
    
            /// <summary>
            /// 年月日(YYYY年MM月DD日)
            /// </summary>
            public String getYearMonthDayWithKanji()
            {
                return String.Format("{0:yyyy年MM月dd日(ddd)}", Date);
            }
    
            /// <summary>
            /// 日を返します.
            /// </summary>
            /// <returns></returns>
            public String getDay()
            {
                if (String.IsNullOrEmpty(YearMonthDay)) return "";
                return YearMonthDay.Substring(6, 2);
            }
        }
    }
    

コードビハインドの修正

次の変更を実施

  • 年間カレンダー作成メソッドの追加
  • グリッドのマトリクス設定メソッドの追加
  • 年月コンボ選択変更イベントを年コンボ選択変更イベントメソッドに変更
  • 初期化処理にて、年コンボ用の項目を生成、セットした後に当年のカレンダーを作成して画面にセットする

MainWindo.xaml.cs

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Data;
    using System.Windows.Documents;
    using System.Windows.Input;
    using System.Windows.Media;
    using System.Windows.Media.Imaging;
    using System.Windows.Navigation;
    using System.Windows.Shapes;
    
    using MahApps.Metro.Controls;
    
    namespace CalendarSample
    {
        /// <summary>
        /// MainWindow.xaml の相互作用ロジック
        /// </summary>
        public partial class MainWindow : MetroWindow
        {
            private Rectangle selectedRec;
            private Style selectedRecStyle;
    
            public MainWindow()
            {
                InitializeComponent();
    
                // 前月から6カ月分のリストを作成し、当月を選択させる
                List<YearInfo> list = new List<YearInfo>();
                DateTime now = DateTime.Now;
                DateTime dt = now.AddYears(-1);
                for (int i = 0; i < 6; i++ )
                {
                    list.Add(new YearInfo(String.Format("{0:yyyy}", dt)));
                    dt = dt.AddYears(1);
                }
                this.cbYear.ItemsSource = list;
                this.cbYear.DisplayMemberPath = "YearWithKanji";
                this.cbYear.SelectedIndex = 1;
    
                setGridMatrix(CalendarContainer, 3, 4);
    
                // 年間カレンダー作成
                createYearsCalendar(this.cbYear.SelectedItem as YearInfo);
    
            }
    
            /// <summary>
            /// 年コンボの選択変更イベントハンドラー.
            /// </summary>
            /// <param name="sender"></param>
            /// <param name="e"></param>
            private void cbYear_SelectionChanged(object sender, SelectionChangedEventArgs e)
            {
                // 年間カレンダー作成
                createYearsCalendar(this.cbYear.SelectedItem as YearInfo);
            }
    
            /// <summary>
            /// 年間カレンダーを作成.
            /// </summary>
            /// <param name="yearInfo"></param>
            private void createYearsCalendar(YearInfo yearInfo)
            {
                CalendarContainer.Children.Clear();
    
                for (int i = 0; i < 12; i++)
                {
                    // 横位置
                    int x = i % 4;
                    // 縦位置
                    int y = i / 4;
                    // カレンダー作成
                    String year = (this.cbYear.SelectedItem as YearInfo).Year;
                    MonthInfo mi = new MonthInfo(year, String.Format("{0:00}", (i + 1)));
                    GroupBox gb = createCalendar(mi);
                    CalendarContainer.Children.Add(gb);
                    gb.SetValue(Grid.ColumnProperty, x);
                    gb.SetValue(Grid.RowProperty, y);
                }
            }
    
            /// <summary>
            /// 指定された年月のカレンダーを作成.
            /// </summary>
            /// <param name="monthInfo"></param>
            private GroupBox createCalendar(MonthInfo monthInfo) {
    
                // グループボックス
                GroupBox calendarGroup = new GroupBox();
                calendarGroup.Header = monthInfo.YearMonthWithKanji;
                calendarGroup.Style = FindResource("gp-normal") as Style;
    
                // グリッド
                Grid calendarGrid = new Grid();
                setGridMatrix(calendarGrid, 7, 7);
                calendarGrid.Style = FindResource("grid-calendar") as Style;
                // 1行目のみ高さ固定とする
                calendarGrid.RowDefinitions.First().Height = new GridLength(20);
    
                // グリッド枠線
                Rectangle border = new Rectangle();
                border.Style = FindResource("rec-border") as Style;
                calendarGrid.Children.Add(border);
    
                for (int col = 0; col < 7; col++)
                {
                    Rectangle week = new Rectangle();
                    week.Style = (col == 6) ? FindResource("rec-week-sat") as Style : FindResource("rec-week") as Style;
                    week.SetValue(Grid.ColumnProperty, col);
                    calendarGrid.Children.Add(week);
    
                    Label lbWeek = new Label();
                    lbWeek.Style = FindResource("lb-week") as Style;
                    lbWeek.Content = ("日月火水木金土").Substring(col, 1);
                    lbWeek.SetValue(Grid.ColumnProperty, col);
                    calendarGrid.Children.Add(lbWeek);
                }
    
                // ヘッダ行下線
                Rectangle underlining = new Rectangle();
                underlining.Style = FindResource("rec-underlining") as Style;
                calendarGrid.Children.Add(underlining);
    
                // グループボックスにグリッドを追加
                calendarGroup.Content = calendarGrid;
                //CalendarContainer.Children.Add(calendarGroup);
    
                // 当月の月初を取得
                var firstDate = new DateTime(int.Parse(monthInfo.Year), int.Parse(monthInfo.Month), 1);
    
                // 曜日番号の取得
                int dayOfWeek = (int)firstDate.DayOfWeek;
    
                // 月末を取得
                int lastDay = firstDate.AddMonths(1).AddDays(-1).Day;
    
                // 1日から月末までを走査
                for (int day = 1; day <= lastDay; day++)
                {
                    // セル位置
                    int index = (day - 1) + dayOfWeek;
                    // 横位置
                    int x = index % 7;
                    // 縦位置
                    int y = index / 7;
    
                    // テキストブロックを生成してグリッドに追加
                    var tb = new TextBlock();
                    tb.Text = String.Format("{0}", day);
                    // 土日は文字色を変更する
                    if (x == 0)
                    {
                        tb.Style = FindResource("txb-date-sun") as Style;
                    }
                    else if (x == 6)
                    {
                        tb.Style = FindResource("txb-date-sat") as Style;
                    }
                    else
                    {
                        tb.Style = FindResource("txb-date") as Style;
    
                    }
                    calendarGrid.Children.Add(tb);
                    tb.SetValue(Grid.ColumnProperty, x);
                    tb.SetValue(Grid.RowProperty, y + 1);
    
                    // 四角形を生成してグリッドに追加
                    // セルの枠線などを表示し、イベントをハンドリングする用
                    var rec = new Rectangle();
                    DateInfo dt = new DateInfo(monthInfo.Year, monthInfo.Month, String.Format("{0:00}", day));
                    rec.DataContext = dt;
                    // 枠線を調整
                    rec.Style = (x == 6) ? FindResource("rec-date-sat") as Style : FindResource("rec-date") as Style;
                    // イベント設定
                    rec.MouseLeftButtonDown += date_MouseLeftButtonDown;
                    calendarGrid.Children.Add(rec);
                    rec.SetValue(Grid.ColumnProperty, x);
                    rec.SetValue(Grid.RowProperty, y + 1);
                }
    
                return calendarGroup;
            }
    
            /// <summary>
            /// グリッドのマトリックスのひな形を設定
            /// </summary>
            /// <param name="grid"></param>
            /// <param name="rowCount"></param>
            /// <param name="columnCount"></param>
            private void setGridMatrix(Grid grid, int rowCount, int columnCount)
            {
                // 行のセット
                for (int i = 0; i < rowCount; i++)
                {
                    grid.RowDefinitions.Add(new RowDefinition() { Height = new GridLength(1, GridUnitType.Star) });
                }
    
                // 列のセット
                for (int i = 0; i < columnCount; i++)
                {
                    grid.ColumnDefinitions.Add(new ColumnDefinition() { Width = new GridLength(1, GridUnitType.Star) });
                }
            }
    
            /// <summary>
            /// セル(日)をクリックした際のイベントハンドラ.
            /// </summary>
            /// <param name="sender"></param>
            /// <param name="e"></param>
            private void date_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
            {
                // 既に選択されたセルがある場合はスタイルを戻す
                if (selectedRec != null)
                {
                    selectedRec.Style = selectedRecStyle;
                }
    
                // 選択されたセルの取得
                Rectangle rec = sender as Rectangle;
    
                // 選択セルの保持
                selectedRec = rec;
                selectedRecStyle = rec.Style;
    
                // 選択時のスタイルに変更
                rec.Style = FindResource("rec-date-selected") as Style;
    
                // ラベルに日付をセット
                lbSelectedDate.Content = (rec.DataContext as DateInfo).getYearMonthDayWithKanji();
            }
        }
    }
    

 

起動してみる

起動後画面

年月の時と同様、6年分のリストを作成しデフォルト表示を当年としました。

年を変更

年を変更した際の表示です。

選択した際の表示

セルを選択するとちゃんと日付が表示されます。

サイズを小さくした表示

少し分かりにくいですが、画面サイズを縮小した際の表示です。

画面サイズを大きくした表示

こちらは分かりやすいですかね。

まとめ

ひとまず目標のところまで出来たので一旦完了とします。
(また何か思いついたら追加で実装するかもしれません。)

ではでは。

スポンサーリンク


関連するコンテンツ

2017年10月17日C#,開発C#,Calendar,WPF,プログラミング

Posted by doradora