LoginSignup
0
4

More than 5 years have passed since last update.

標準のCalendarに1日ごとにアイテムを追加できるようにカスタムする(MultiBindConverter使用)

Last updated at Posted at 2018-11-20

概要

WPFでGoogleカレンダー的な左上に日付があって各日付に何かしら表示できるものが欲しくなったが、標準のカレンダーそのままでは、置くスペースがないため、少しカスタムしてみました。

完成品

こんな感じです。

image.png

xamlは以下の通りです。

MainWindow.xaml
        <Calendar x:Name="rootCalendar" HorizontalAlignment="Center" Language="ja-JP" VerticalAlignment="Center" Margin="5" >
            <Calendar.Background>White</Calendar.Background>
            <Calendar.CalendarDayButtonStyle>
                <Style TargetType="{x:Type CalendarDayButton}">
                    <Setter Property="Template">
                        <Setter.Value>
                            <ControlTemplate TargetType="{x:Type CalendarDayButton}">
                                <Grid Background="White">
                                    <Border BorderThickness="1" BorderBrush="Turquoise">
                                        <StackPanel MinHeight="80" MinWidth="80" >
                                            <TextBlock Text="{Binding StringFormat={}{0:dd}}" Margin="2" FontSize="16"/>
                                            <ItemsControl >
                                                <ItemsControl.ItemsPanel>
                                                    <ItemsPanelTemplate>
                                                        <WrapPanel Margin="2" />
                                                    </ItemsPanelTemplate>
                                                </ItemsControl.ItemsPanel>
                                            </ItemsControl>
                                        </StackPanel>
                                    </Border>
                                </Grid>
                            </ControlTemplate>
                        </Setter.Value>
                    </Setter>
                </Style>
            </Calendar.CalendarDayButtonStyle>
        </Calendar>

日付の下に文字列を表示させてみた

xamlのの下にを追加し、MultiBindingを追加することで画像や文字列を表示させることができます。

こんな感じです。
左に日ごとに表示されたカレンダーと右にカレンダーにバインドされているコレクションの一覧を表示しています。

image.png

image.png

image.png

ただ、今のままだと複数同じ日に表示された場合、下にもぐってしまうため、スクロールバーを付けるなどの対策が必要です。

以下に上記の画像で使用したソースを置いておきます。

MainWindow.xaml
<Window x:Class="CalendarCustom.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:CalendarCustom"
        mc:Ignorable="d"
        Title="MainWindow" Height="754" Width="1014">

    <Window.DataContext>
        <local:TestViewModel />
    </Window.DataContext>

    <Window.Resources>
        <local:MultiBindingSample x:Key="MultiBindingSample" />
    </Window.Resources>
    <Grid x:Name="rootGrid" >
        <Grid.ColumnDefinitions >
            <ColumnDefinition Width="2*" />
            <ColumnDefinition Width="*" />
        </Grid.ColumnDefinitions>


        <Calendar x:Name="rootCalendar" HorizontalAlignment="Center" VerticalAlignment="Center" Margin="5" >
            <Calendar.Background>White</Calendar.Background>
            <Calendar.CalendarDayButtonStyle>
                <Style TargetType="{x:Type CalendarDayButton}">
                    <Setter Property="Template">
                        <Setter.Value>
                            <ControlTemplate TargetType="{x:Type CalendarDayButton}">
                                <Grid Background="White">
                                    <Border BorderThickness="1" BorderBrush="DimGray">
                                        <StackPanel MinHeight="80" MinWidth="80" >
                                            <TextBlock Text="{Binding StringFormat={}{0:dd}}" Margin="2" FontSize="16"/>
                                            <ItemsControl Background="Azure" Height="50" >
                                                <ItemsControl.ItemsSource>
                                                    <MultiBinding Converter="{StaticResource MultiBindingSample}" UpdateSourceTrigger="PropertyChanged" Mode="OneWay">
                                                        <Binding Path="Date" UpdateSourceTrigger="PropertyChanged"/>
                                                        <Binding Path="DataContext.TestCollection" ElementName="rootGrid"  />
                                                    </MultiBinding>
                                                </ItemsControl.ItemsSource>
                                                <ItemsControl.ItemsPanel>
                                                    <ItemsPanelTemplate>
                                                        <WrapPanel Margin="2" Width="70" />
                                                    </ItemsPanelTemplate>
                                                </ItemsControl.ItemsPanel>
                                                <ItemsControl.ItemTemplate>
                                                    <DataTemplate DataType="{x:Type local:TestModel}">
                                                        <!--<Image Source="{Binding Image, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}" Height="20" Stretch="Uniform" />-->
                                                        <TextBlock Text="{Binding DisplayItem, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}" FontSize="16" Foreground="Black" />
                                                    </DataTemplate>
                                                </ItemsControl.ItemTemplate>
                                            </ItemsControl>
                                        </StackPanel>
                                    </Border>
                                </Grid>
                            </ControlTemplate>
                        </Setter.Value>
                    </Setter>
                </Style>
            </Calendar.CalendarDayButtonStyle>
        </Calendar>


        <DataGrid Grid.Column="1" Margin="5,40" ItemsSource="{Binding TestCollection}" AutoGenerateColumns="False" IsReadOnly="True">
            <DataGrid.Columns>
                <DataGridTextColumn Header="名前" Binding="{Binding DisplayItem , UpdateSourceTrigger=PropertyChanged}" />
                <DataGridTextColumn Header="日付" Binding="{Binding DisplayDate, StringFormat={}{0:yyyy/MM/dd(ddd)}}"/>
            </DataGrid.Columns>
        </DataGrid>

    </Grid>
</Window>

TestModel.cs
using System;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Runtime.CompilerServices;

namespace CalendarCustom
{

    public class TestViewModel : INotifyPropertyChanged
    {
        public ObservableCollection<TestModel> TestCollection { get; private set; }

        public TestViewModel()
        {
            this.TestCollection = new ObservableCollection<TestModel>()
            {
                {new TestModel("first", new DateTime(2018, 11, 20)) },
                {new TestModel("second", new DateTime(2018, 11, 20)) },
                {new TestModel("third", new DateTime(2018, 11, 23)) },
                {new TestModel("forth", new DateTime(2018, 11, 28)) },
                {new TestModel("fifth", new DateTime(2018, 12, 20)) },
                {new TestModel("sixth", new DateTime(2018, 10, 20)) },
            };

            this.RaisePropertyChanged(nameof(this.TestCollection));

        }

        public event PropertyChangedEventHandler PropertyChanged;
        private void RaisePropertyChanged([CallerMemberName] string propertyName = null)
        {
            this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
    }

    /// <summary>
    /// テストサンプル
    /// </summary>
    public class TestModel : INotifyPropertyChanged
    {
        private string _displayItem;
        /// <summary>
        /// 表示するもの(今回は文字列)
        /// </summary>
        public string DisplayItem
        {
            get { return _displayItem; }
            private set
            {
                _displayItem = value;
                this.RaisePropertyChanged(nameof(this.DisplayItem));
            }
        }

        private DateTime _displayDate;
        /// <summary>
        /// 表示したい日付
        /// </summary>
        public DateTime DisplayDate
        {
            get { return _displayDate; }
            private set
            {
                _displayDate = value;
                this.RaisePropertyChanged(nameof(this.DisplayDate));
            }
        }

        /// <summary>
        /// ctor
        /// </summary>
        public TestModel(string displayItem, DateTime displayDate)
        {
            this.DisplayItem = displayItem;
            this.DisplayDate = displayDate;
        }

        public TestModel()
        {
        }

        public event PropertyChangedEventHandler PropertyChanged;

        private void RaisePropertyChanged([CallerMemberName] string propertyName = null)
        {
            this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }

    }
}

MultiBindingSample.cs
using System;
using System.Collections.ObjectModel;
using System.Globalization;
using System.Linq;
using System.Windows;
using System.Windows.Data;

namespace CalendarCustom
{
    public class MultiBindingSample : IMultiValueConverter
    {
        public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
        {
            var targetDate = (DateTime)values[0];

            if (values[1] is ObservableCollection<TestModel> testCollection)
            {

                var chunkItem = testCollection.Where(x => x.DisplayDate.Date == targetDate.Date);

                return chunkItem;
            }

            return DependencyProperty.UnsetValue;
        }


        public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
        {
            throw new NotImplementedException();
        }

    }
}

MultiBindingについて

以下がMainWindow.xamlでのMultiBindingの箇所です。

MainWindow.xaml
<MultiBinding Converter="{StaticResource MultiBindingSample}" UpdateSourceTrigger="PropertyChanged" Mode="OneWay">
    <Binding Path="Date" UpdateSourceTrigger="PropertyChanged"/>
    <Binding Path="DataContext.TestCollection" ElementName="rootGrid"  />
</MultiBinding>

ここで記載している順番にobject型の配列に格納されていきます。
なので、1つ目にはDateプロパティ(カレンダーの日付)
2つ目にはDataContext.TestCollectionが格納されます。
ここで格納された配列が、MultiBindingのConverterプロパティで指定されているクラス(MultiBindingSample)のConvertメソッドのvaluesに当たるものになります。

MultiBindingSample.cs
 public class MultiBindingSample : IMultiValueConverter
    {
                                       // ↓ここです
        public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
        {
            var targetDate = (DateTime)values[0];

 // ----------------省略----------------

まとめ

カレンダーをカスタムするのは、初心者が下手に手を出すと大変だということがわかりました。

参考にさせて頂いたサイト

動作環境

Windows 10
Visual Studio 2017
.NET Framework 4.6

0
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
4