2019-06-18に更新

【WPF】自作カレンダー その1(とりあえず当月を表示)

読了目安:14分

WPFには、カレンダーコントロールが標準であるんですが、
スケジューラ-みたいなことはできないし、あくまで選択された日付を取得するためのものになっています。

常に表示して、カレンダーに対してアクションするようなコントローラーも
需要はあると思うので、標準で使えるようにしてくれればいいのにと思いながらちょっと自作してみます。

今回は新規で専用にプロジェクトを作成します。

プロジェクト作成

VisualStudio2017を起動し、
新規プロジェクトを作成、名前を「CalendarSample」とします。

作成方法などは下記の記事を参考にしていただければ。
Visual Studio 2017 Community のインストールから Hello World

また、スタイルに「MahApps.Metro」を利用するので、
分からなければこちらの記事を参考にしてください。
【WPF】MahApps.Metro で見た目をスタイリッシュにしてみる

プログラム実装

スタイルの設定

App.xaml

<Application x:Class="CalendarSample.App"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:local="clr-namespace:CalendarSample"
             StartupUri="MainWindow.xaml">
    <Application.Resources>
        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>
                <!-- MahApps.Metro resource dictionaries. Make sure that all file names are Case Sensitive! -->
                <ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Controls.xaml" />
                <ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Fonts.xaml" />
                <ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Colors.xaml" />
                <!-- Accent and AppTheme setting -->
                <ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Accents/Blue.xaml" />
                <ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Accents/BaseLight.xaml" />
            </ResourceDictionary.MergedDictionaries>
        </ResourceDictionary>
    </Application.Resources>
</Application>

MahApps.Metroのスタイル設定を記述します。

StyleDic.xaml

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                    xmlns:Mah="clr-namespace:MahApps.Metro.Controls;assembly=MahApps.Metro"
                    xmlns:local="clr-namespace:WpfApp1.Style">

    <!-- グループ:通常-->
    <Style x:Key="gp-normal" TargetType="GroupBox" >
        <Setter Property="VerticalAlignment" Value="Top" />
        <Setter Property="HorizontalAlignment" Value="Left" />
        <Setter Property="Background" Value="#FFFFFFFF" />
        <Setter Property="Foreground" Value="#FF777777" />
    </Style>
</ResourceDictionary>

プロジェクト直下に「Style」ディレクトリを追加し、上記ファイルを作成します。

画面の作成

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="カレンダーサンプル" Height="350" Width="525">
    <Window.Resources>
        <ResourceDictionary Source="/Style/StyleDic.xaml"/>
    </Window.Resources>

    <Grid>
        <GroupBox x:Name="CalendarGropu1" Header="" HorizontalAlignment="Left" Height="300" Margin="10,10,0,0" VerticalAlignment="Top" Width="497" Style="{StaticResource gp-normal}">
            <Grid x:Name="CalendarGrid" HorizontalAlignment="Left" Height="258" Margin="10,10,0,0" VerticalAlignment="Top" Width="467">
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="1*" />
                    <ColumnDefinition Width="1*" />
                    <ColumnDefinition Width="1*" />
                    <ColumnDefinition Width="1*" />
                    <ColumnDefinition Width="1*" />
                    <ColumnDefinition Width="1*" />
                    <ColumnDefinition Width="1*" />
                </Grid.ColumnDefinitions>
                <Grid.RowDefinitions>
                    <RowDefinition Height="20" />
                    <RowDefinition Height="1*" />
                    <RowDefinition Height="1*" />
                    <RowDefinition Height="1*" />
                    <RowDefinition Height="1*" />
                    <RowDefinition Height="1*" />
                </Grid.RowDefinitions>
                <Rectangle Stroke="Black" StrokeThickness="1" Grid.ColumnSpan="7" Grid.RowSpan="7"/>
                <Rectangle Fill="#ff8000" HorizontalAlignment="Stretch" Grid.Row="0" Grid.Column="0" VerticalAlignment="Stretch" Margin="1 1 0 0" Panel.ZIndex="0" />
                <Rectangle Fill="#ff8000" HorizontalAlignment="Stretch" Grid.Row="0" Grid.Column="1" VerticalAlignment="Stretch" Margin="1 1 0 0" Panel.ZIndex="0"/>
                <Rectangle Fill="#ff8000" HorizontalAlignment="Stretch" Grid.Row="0" Grid.Column="2" VerticalAlignment="Stretch" Margin="1 1 0 0" Panel.ZIndex="0"/>
                <Rectangle Fill="#ff8000" HorizontalAlignment="Stretch" Grid.Row="0" Grid.Column="3" VerticalAlignment="Stretch" Margin="1 1 0 0" Panel.ZIndex="0"/>
                <Rectangle Fill="#ff8000" HorizontalAlignment="Stretch" Grid.Row="0" Grid.Column="4" VerticalAlignment="Stretch" Margin="1 1 0 0" Panel.ZIndex="0"/>
                <Rectangle Fill="#ff8000" HorizontalAlignment="Stretch" Grid.Row="0" Grid.Column="5" VerticalAlignment="Stretch" Margin="1 1 0 0" Panel.ZIndex="0"/>
                <Rectangle Fill="#ff8000" HorizontalAlignment="Stretch" Grid.Row="0" Grid.Column="6" VerticalAlignment="Stretch" Margin="1 1 1 0" Panel.ZIndex="0"/>
                <Rectangle Height="1" Stroke="Black" StrokeThickness="1" VerticalAlignment="Bottom" Grid.ColumnSpan="7"/>
                <Label Content="日" HorizontalAlignment="Center" Grid.Row="0" Grid.Column="0" VerticalAlignment="Center" FontSize="11" Padding="0"/>
                <Label Content="月" HorizontalAlignment="Center" Grid.Row="0" Grid.Column="1" VerticalAlignment="Center" FontSize="11" Padding="0"/>
                <Label Content="火" HorizontalAlignment="Center" Grid.Row="0" Grid.Column="2" VerticalAlignment="Center" FontSize="11" Padding="0"/>
                <Label Content="水" HorizontalAlignment="Center" Grid.Row="0" Grid.Column="3" VerticalAlignment="Center" FontSize="11" Padding="0"/>
                <Label Content="木" HorizontalAlignment="Center" Grid.Row="0" Grid.Column="4" VerticalAlignment="Center" FontSize="11" Padding="0"/>
                <Label Content="金" HorizontalAlignment="Center" Grid.Row="0" Grid.Column="5" VerticalAlignment="Center" FontSize="11" Padding="0"/>
                <Label Content="土" HorizontalAlignment="Center" Grid.Row="0" Grid.Column="6" VerticalAlignment="Center" FontSize="11" Padding="0"/>

            </Grid>
        </GroupBox>

    </Grid>
</Mah:MetroWindow>

コードビハインドの実装

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;

        public MainWindow()
        {
            InitializeComponent();

            CalendarGropu1.Header = String.Format("{0:yyyy年MM月}", DateTime.Now); 

            // 当月の月初を取得
            var firstDate = new DateTime(DateTime.Now.Year, DateTime.Now.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;

                // 土日は文字色を変更する
                Color color = Colors.Black;
                if (x == 0)
                {
                    color = Colors.Red;
                }
                else if (x == 6)
                {
                    color = Colors.Blue;
                }

                // テキストブロックを生成してグリッドに追加
                var tb = new TextBlock()
                {
                    Text = string.Format("{0}", day),
                    FontSize = 12,
                    Foreground = new SolidColorBrush(color),
                    Padding = new Thickness(0, 10, 10, 0),
                    HorizontalAlignment = HorizontalAlignment.Right,
                    VerticalAlignment = VerticalAlignment.Top
                };
                this.CalendarGrid.Children.Add(tb);
                tb.SetValue(Grid.ColumnProperty, x);
                tb.SetValue(Grid.RowProperty, y + 1);

                // 四角形を生成してグリッドに追加
                // セルの枠線などを表示し、イベントをハンドリングする用
                var rec = new Rectangle();
                rec.HorizontalAlignment = HorizontalAlignment.Stretch;
                rec.VerticalAlignment = VerticalAlignment.Stretch;
                // 背景色を設定しないとイベントを検知できないらしいので透過色を設定
                rec.Fill = Brushes.Transparent;
                // 枠線を調整
                rec.Margin = (x == 6) ? new Thickness(0.0, -1.0, 0.0, 0.0) : new Thickness(0.0, -1.0, -1.0, 0.0);
                // イベント設定
                rec.MouseLeftButtonDown += date_MouseLeftButtonDown;
                this.CalendarGrid.Children.Add(rec);
                rec.SetValue(Grid.ColumnProperty, x);
                rec.SetValue(Grid.RowProperty, y + 1);

            }
        }

        /// <summary>
        /// セル(日)をクリックした際のイベントハンドラ.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void date_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            // 既に選択されたセルがある場合は初期化
            if (selectedRec != null)
            {
                selectedRec.StrokeDashArray = null;
                selectedRec.StrokeThickness = 0;
            }

            // 枠線に点線をセット
            Rectangle rec = sender as Rectangle;
            rec.Stroke = Brushes.Black;
            DoubleCollection dbc = new DoubleCollection();
            dbc.Add(1);
            dbc.Add(1);
            rec.StrokeDashArray = dbc;
            rec.StrokeThickness = 1;

            // 選択セルの保持
            selectedRec = rec;
        }
    }
}

ポイントは、
「Grid」を使い枠組みを作成してセルに日付用の「Rectangle」、「TextBlock」を配置することです。

起動してみる

image

image

日付セルをクリックすると、枠に点線が表示されるようにしました。

まとめ

今回はここまでとなります。

次回以降、このカレンダーを使って色々試してみたいと思います。

ではでは。

Originally published at www.doraxdora.com
ツイッターでシェア
みんなに共有、忘れないようにメモ

view_list WPFで自作のカレンダーを作ってみる
第1回 【WPF】自作カレンダー その1(とりあえず当月を表示)
第2回 【WPF】自作カレンダー その2(動的生成)
第3回 【WPF】自作カレンダー その3(年間カレンダー)

doraxdora

IT関係の仕事をしています/1985年生まれの東京在住/便利なサービスやツール漁りや料理などが好き/2017年~ブログやってます/自分でサービスとか作ってリリースしたい/何かありましたらお気軽にDMどうぞ

Crieitは誰でも投稿できるサービスです。 是非記事の投稿をお願いします。どんな軽い内容でも投稿できます。

また、「こんな記事が読みたいけど見つからない!」という方は是非記事投稿リクエストボードへ!

有料記事を販売できるようになりました!

こじんまりと作業ログやメモ、進捗を書き残しておきたい方はボード機能をご利用ください。
ボードとは?

コメント