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

2017年10月15日C#,開発

おはようございます。

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

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

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

スポンサーリンク

プロジェクト作成

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

作成方法などは下記の記事を参考にしていただければ。

また、スタイルに「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」を配置することです。

起動してみる

起動後画面
日付クリック時の表示

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

まとめ

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

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

ではでは。

スポンサーリンク


関連するコンテンツ

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

Posted by doradora