locked
DatePickerコントロールの外観のカスタマイズについて RRS feed

  • 質問

  • お世話になります。Silverlight4, VisualStudio2010で開発しています。

    DatePickerコントロールをカスタマイズして、

    DatePicker内のCalendarオブジェクトの任意の日付の背景色を変えようとしています。(休日、土日など)

    ネットで漁ったサンプルソースなどを参考に、背景色を自由に変化させることはできたのですが、

    DatePickerオブジェクトのSelectedDateChangedイベントが動作しなくなってしまいました。

    日付を選んでもテキストボックスの値は更新されないし、Calendarオブジェクトの表示も消えません。

    どうしたらSelectedDateChangedイベントを生かしたままカスタマイズできるのか悩んでいます。

    もし良いアイディアをお持ちの方がいらっしゃったらご教示願います。

    //---------------- XAML ----------------

    <UserControl x:Class="SilverlightApplication1.MainPage"
    
    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:ext="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls" 
    
    xmlns:prim="clr-namespace:System.Windows.Controls.Primitives;assembly=System.Windows.Controls" 
    
    xmlns:local="clr-namespace:SilverlightApplication1"
    
    xmlns:vsm="clr-namespace:System.Windows;assembly=System.Windows"
    
    mc:Ignorable="d"
    
    d:DesignHeight="380" d:DesignWidth="400" Loaded="UserControl_Loaded" HorizontalAlignment="Left">
    
    <UserControl.Resources>
    
    <local:DatePickerBackgroundConverter x:Key="DatePickerBackgroundConverter" />
    
    <Style TargetType="ext:Calendar" x:Key="CalendarStyle1">
    
    <Setter Property="Template">
    
    <Setter.Value>
    
    <ControlTemplate TargetType="ext:Calendar">
    
    <StackPanel HorizontalAlignment="Center" x:Name="Root">
    
    <ext:Calendar x:Name="Calendar" SelectedDate="{TemplateBinding SelectedDate}" Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" >
    
    <ext:Calendar.CalendarDayButtonStyle>
    
    <Style TargetType="prim:CalendarDayButton">
    
    <Setter Property="Template">
    
    <Setter.Value>
    
    <ControlTemplate TargetType="prim:CalendarDayButton">
    
    <Grid Background=
    
    "{Binding Converter={StaticResource DatePickerBackgroundConverter}, Path=Date}">
    
    <vsm:VisualStateManager.VisualStateGroups>
    
    <vsm:VisualStateGroup x:Name="CommonStates">
    
    <vsm:VisualStateGroup.Transitions>
    
    <vsm:VisualTransition GeneratedDuration="0:0:0.1" />
    
    </vsm:VisualStateGroup.Transitions>
    
    <vsm:VisualState x:Name="Normal" />
    
    <vsm:VisualState x:Name="MouseOver">
    
    <Storyboard>
    
    <DoubleAnimation Storyboard.TargetName="Background" Storyboard.TargetProperty="Opacity" To=".5" Duration="0" />
    
    </Storyboard>
    
    </vsm:VisualState>
    
    <vsm:VisualState x:Name="Pressed">
    
    <Storyboard>
    
    <DoubleAnimation Storyboard.TargetName="Background" Storyboard.TargetProperty="Opacity" To=".5" Duration="0" />
    
    </Storyboard>
    
    </vsm:VisualState>
    
    <vsm:VisualState x:Name="Disabled">
    
    <Storyboard>
    
    <DoubleAnimation Storyboard.TargetName="Background" Storyboard.TargetProperty="Opacity" To="0" Duration="0" />
    
    <DoubleAnimation Storyboard.TargetName="Content" Storyboard.TargetProperty="Opacity" To=".35" Duration="0" />
    
    </Storyboard>
    
    </vsm:VisualState>
    
    </vsm:VisualStateGroup>
    
    <vsm:VisualStateGroup x:Name="SelectionStates">
    
    <vsm:VisualStateGroup.Transitions>
    
    <vsm:VisualTransition GeneratedDuration="0" />
    
    </vsm:VisualStateGroup.Transitions>
    
    <vsm:VisualState x:Name="Unselected" />
    
    <vsm:VisualState x:Name="Selected">
    
    <Storyboard>
    
    <DoubleAnimation Storyboard.TargetName="SelectedBackground" Storyboard.TargetProperty="Opacity" To=".75" Duration="0" />
    
    </Storyboard>
    
    </vsm:VisualState>
    
    </vsm:VisualStateGroup>
    
    <vsm:VisualStateGroup x:Name="CalendarButtonFocusStates">
    
    <vsm:VisualStateGroup.Transitions>
    
    <vsm:VisualTransition GeneratedDuration="0" />
    
    </vsm:VisualStateGroup.Transitions>
    
    <vsm:VisualState x:Name="CalendarButtonFocused">
    
    <Storyboard>
    
    <ObjectAnimationUsingKeyFrames Storyboard.TargetName="FocusVisual" Storyboard.TargetProperty="Visibility" Duration="0">
    
    <DiscreteObjectKeyFrame KeyTime="0" Value="Visible" />
    
    </ObjectAnimationUsingKeyFrames>
    
    </Storyboard>
    
    </vsm:VisualState>
    
    <vsm:VisualState x:Name="CalendarButtonUnfocused">
    
    <Storyboard>
    
    <ObjectAnimationUsingKeyFrames Storyboard.TargetName="FocusVisual" Storyboard.TargetProperty="Visibility" Duration="0">
    
    <DiscreteObjectKeyFrame KeyTime="0" Value="Collapsed" />
    
    </ObjectAnimationUsingKeyFrames>
    
    </Storyboard>
    
    </vsm:VisualState>
    
    </vsm:VisualStateGroup>
    
    <vsm:VisualStateGroup x:Name="ActiveStates">
    
    <vsm:VisualStateGroup.Transitions>
    
    <vsm:VisualTransition GeneratedDuration="0" />
    
    </vsm:VisualStateGroup.Transitions>
    
    <vsm:VisualState x:Name="Active" />
    
    <vsm:VisualState x:Name="Inactive">
    
    <Storyboard>
    
    <ColorAnimation Storyboard.TargetName="Content" Storyboard.TargetProperty="(ContentControl.Foreground).(GradientBrush.GradientStops)[2].(GradientStop.Color)" To="#FF777777" Duration="0" />
    
    <ColorAnimation Storyboard.TargetName="Content" Storyboard.TargetProperty="(ContentControl.Foreground).(GradientBrush.GradientStops)[3].(GradientStop.Color)" To="#FF777777" Duration="0" />
    
    </Storyboard>
    
    </vsm:VisualState>
    
    </vsm:VisualStateGroup>
    
    <vsm:VisualStateGroup x:Name="DayStates">
    
    <vsm:VisualStateGroup.Transitions>
    
    <vsm:VisualTransition GeneratedDuration="0" />
    
    </vsm:VisualStateGroup.Transitions>
    
    <vsm:VisualState x:Name="RegularDay" />
    
    <vsm:VisualState x:Name="Today">
    
    <Storyboard>
    
    <DoubleAnimation Storyboard.TargetName="TodayBackground" Storyboard.TargetProperty="Opacity" To="1" Duration="0" />
    
    <DoubleAnimation Storyboard.TargetName="Content" Storyboard.TargetProperty="(ContentControl.Foreground).(GradientBrush.GradientStops)[1].(GradientStop.Offset)" To="1" Duration="0" />
    
    <DoubleAnimation Storyboard.TargetName="Content" Storyboard.TargetProperty="(ContentControl.Foreground).(GradientBrush.GradientStops)[2].(GradientStop.Offset)" To="1" Duration="0" />
    
    </Storyboard>
    
    </vsm:VisualState>
    
    </vsm:VisualStateGroup>
    
    <vsm:VisualStateGroup x:Name="BlackoutDayStates">
    
    <vsm:VisualStateGroup.Transitions>
    
    <vsm:VisualTransition GeneratedDuration="0" />
    
    </vsm:VisualStateGroup.Transitions>
    
    <vsm:VisualState x:Name="NormalDay" />
    
    <vsm:VisualState x:Name="BlackoutDay">
    
    <Storyboard>
    
    <DoubleAnimation Storyboard.TargetName="BlackoutVisual" Storyboard.TargetProperty="Opacity" To=".2" Duration="0" />
    
    </Storyboard>
    
    </vsm:VisualState>
    
    </vsm:VisualStateGroup>
    
    </vsm:VisualStateManager.VisualStateGroups>
    
    <Rectangle x:Name="TodayBackground" RadiusX="1" RadiusY="1" Opacity="0" Fill="#FFAAAAAA" />
    
    <Rectangle x:Name="SelectedBackground" RadiusX="1" RadiusY="1" Opacity="0" Fill="{TemplateBinding Background}" />
    
    <Rectangle x:Name="Background" RadiusX="1" RadiusY="1" Opacity="0" Fill="{TemplateBinding Background}" />
    
    <ContentControl x:Name="Content" IsTabStop="False" Content="{TemplateBinding Content}" ContentTemplate="{TemplateBinding ContentTemplate}" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}" FontSize="{TemplateBinding FontSize}" Margin="5,1,5,1">
    
    <ContentControl.Foreground>
    
    <LinearGradientBrush>
    
    <GradientStop Offset="0" Color="#FFFFFFFF" />
    
    <GradientStop Offset="0" Color="#FFFFFFFF" />
    
    <GradientStop Offset="0" Color="#FF333333" />
    
    <GradientStop Offset="1" Color="#FF333333" />
    
    </LinearGradientBrush>
    
    </ContentControl.Foreground>
    
    </ContentControl>
    
    <Path x:Name="BlackoutVisual" Opacity="0" Margin="3" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" RenderTransformOrigin="0.5,0.5" Fill="#FF000000" Stretch="Fill" Data="M8.1772461,11.029181 L10.433105,11.029181 L11.700684,12.801641 L12.973633,11.029181 L15.191895,11.029181 L12.844727,13.999395 L15.21875,17.060919 L12.962891,17.060919 L11.673828,15.256231 L10.352539,17.060919 L8.1396484,17.060919 L10.519043,14.042364 z" />
    
    <Rectangle x:Name="FocusVisual" Visibility="Collapsed" IsHitTestVisible="false" RadiusX="1" RadiusY="1" Stroke="#FF6DBDD1" />
    
    </Grid>
    
    </ControlTemplate>
    
    </Setter.Value>
    
    </Setter>
    
    </Style>
    
    </ext:Calendar.CalendarDayButtonStyle>
    
    </ext:Calendar>
    
    </StackPanel>
    
    </ControlTemplate>
    
    </Setter.Value>
    
    </Setter>
    
    </Style>
    
    </UserControl.Resources>
    
    <Grid x:Name="LayoutRoot" Background="White" Height="Auto">
    
    <ext:DatePicker Height="23" Name="datePicker1" VerticalAlignment="Top" CalendarStyle="{StaticResource CalendarStyle1}" HorizontalAlignment="Left" SelectedDateChanged="datePicker1_SelectedDateChanged" />
    
    </Grid>
    
    </UserControl>
    
    

    //---------------- C# ----------------

    using System;
    
    using System.Collections.Generic;
    
    using System.Linq;
    
    using System.Net;
    
    using System.Windows;
    
    using System.Windows.Controls;
    
    using System.Windows.Documents;
    
    using System.Windows.Input;
    
    using System.Windows.Media;
    
    using System.Windows.Media.Animation;
    
    using System.Windows.Shapes;
    
    
    
    namespace SilverlightApplication1
    
    {
    
    
    
    public partial class MainPage : UserControl
    
    
    
    {
    
    
    
    private DateTime[] dt;
    
    
    
    public MainPage()
    
    {
    
    DatePickerBackgroundConverter.Holidays = new DateTime[0]; 
    
    InitializeComponent();
    
    }
    
    
    
    private void UserControl_Loaded(object sender, RoutedEventArgs e)
    
    {
    
    dt = new DateTime[0];
    
    }
    
    
    
    private void datePicker1_SelectedDateChanged(object sender, SelectionChangedEventArgs e)
    
    {
    
    MessageBox.Show("A"); //←何も起きない
    
    }
    
    }
    
    
    
    public class DatePickerBackgroundConverter
    
    : System.Windows.Data.IValueConverter
    
    {
    
    public static IList<DateTime> Holidays { get; set; }
    
    
    
    public object Convert(object value,
    
    Type targetType,
    
    object parameter,
    
    System.Globalization.CultureInfo culture)
    
    {
    
    DateTime date = (DateTime)value;
    
    if (date.DayOfWeek == DayOfWeek.Sunday ||
    
    (Holidays != null && Holidays.Contains(date)))
    
    {
    
    return new SolidColorBrush(Colors.Red);
    
    }
    
    else if (date.DayOfWeek == DayOfWeek.Saturday)
    
    {
    
    return new SolidColorBrush(Colors.Blue);
    
    }
    
    return new SolidColorBrush(Colors.White);
    
    }
    
    
    
    public object ConvertBack(object value, Type targetType,
    
    object parameter, System.Globalization.CultureInfo culture)
    
    {
    
    return null;
    
    }
    
    }
    
    }
    
    

     




    2011年8月9日 1:57

回答

  • >
    <ControlTemplate TargetType="ext:Calendar">
    
    <StackPanel HorizontalAlignment="Center" x:Name="Root">
    
    <ext:Calendar x:Name="Calendar" SelectedDate="{TemplateBinding SelectedDate}" Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" >
    
    <ext:Calendar.CalendarDayButtonStyle>
    



    Calendarのテンプレートを編集するにあたって、CalenderItemのテンプレートの指定等他の重要なStyleを端折っていますね。
    この辺が原因だと思います。

    スタイルとテンプレートの編集はネットのソースを拾うのでは無くExpression Blendを使用して行うことをお勧めします。
    その方が確実ですし、細かい変更にも対応できて後々便利です。

    今回の場合は
    CalendarStyleと、CalendarDayButtonStyleの2つをコピーして編集する必要があり、

    CalendarDayButtonStyleは質問文にあるやつまんまで良いと思いますが、
    CalendarStyleは素のスタイル(Blendでコピーしたスタイル)に
                <Setter Property="CalendarDayButtonStyle" Value="{StaticResource 質問文のスタイル}" />
    を追加するしてあげると、想定した動きになると思います。(質問文にあるようにその場で定義もできますが、分けた方がメンテ上良いと思います。)

    # 別な方法では、CalendarDayButtonStyleをキー無しで定義するとCalendarStyleは弄らなくて良くなります。
    # が、定義場所によっては他のCalendarに影響を与えてしまうので注意してください。
    • 回答としてマーク monger_monger 2011年8月12日 7:35
    • 回答としてマークされていない monger_monger 2011年8月12日 7:51
    • 回答としてマーク monger_monger 2011年8月25日 4:26
    2011年8月11日 22:13

すべての返信

  • 自己レスですが、5時間ほど試行錯誤して次善の策を見つけました。

    XAMLでユーザーコントロールのリソースを定義しているところでカレンダーを配置していますが、

    このカレンダーの SelectedDatesChanged イベントは生きているようなので、イベントハンドラを追加します。

    最初の質問のXAMLソース

    <ext:Calendar x:Name="Calendar" SelectedDate="{TemplateBinding SelectedDate}" Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" >
    
    
    
    

    イベントハンドラを追加したXAMLソース

    <ext:Calendar x:Name="Calendar" SelectedDate="{TemplateBinding SelectedDate}" Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" SelectedDatesChanged="Calendar_SelectedDatesChanged">
    
    

    C#側のイベントハンドラには、次のように記述します。

     private void Calendar_SelectedDatesChanged(object sender, SelectionChangedEventArgs e)
    
     {
    
    	datePicker1.SelectedDate = (DateTime)((Calendar)sender).SelectedDate;	//これでテキストボックスの中身も更新される。
    	datePicker1.IsDropDownOpen = false;	//カレンダーを非表示にする。
     }
    
    

    これで、普通のDatePickerコントロールと似た動きを再現することができます。

    ただし、本来はマウスの左ボタンを離したときにCalendarが隠されるべきなのに、左ボタンを押した時点でCalendarが隠されてしまいます。

    また、既に選択済みの日付をクリックした場合にCalendarが隠れません。

    Calendarの内部イベント"DayButtonMouseUp"を拾えればこの点は改善できますが、現在その方法がわかりません。

    このように、この対処法はなんとなくうまく行っているというだけですので、引き続き正しい作法にのっとった、

    XAMLの記述によってイベントが無効になったりしないようなDatePickerの外観のカスタマイズ方法を募集したいと思います。

    もし良いアイディアをお持ちの方がいらっしゃったらご教示願います。








    2011年8月9日 7:16
  • >
    <ControlTemplate TargetType="ext:Calendar">
    
    <StackPanel HorizontalAlignment="Center" x:Name="Root">
    
    <ext:Calendar x:Name="Calendar" SelectedDate="{TemplateBinding SelectedDate}" Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" >
    
    <ext:Calendar.CalendarDayButtonStyle>
    



    Calendarのテンプレートを編集するにあたって、CalenderItemのテンプレートの指定等他の重要なStyleを端折っていますね。
    この辺が原因だと思います。

    スタイルとテンプレートの編集はネットのソースを拾うのでは無くExpression Blendを使用して行うことをお勧めします。
    その方が確実ですし、細かい変更にも対応できて後々便利です。

    今回の場合は
    CalendarStyleと、CalendarDayButtonStyleの2つをコピーして編集する必要があり、

    CalendarDayButtonStyleは質問文にあるやつまんまで良いと思いますが、
    CalendarStyleは素のスタイル(Blendでコピーしたスタイル)に
                <Setter Property="CalendarDayButtonStyle" Value="{StaticResource 質問文のスタイル}" />
    を追加するしてあげると、想定した動きになると思います。(質問文にあるようにその場で定義もできますが、分けた方がメンテ上良いと思います。)

    # 別な方法では、CalendarDayButtonStyleをキー無しで定義するとCalendarStyleは弄らなくて良くなります。
    # が、定義場所によっては他のCalendarに影響を与えてしまうので注意してください。
    • 回答としてマーク monger_monger 2011年8月12日 7:35
    • 回答としてマークされていない monger_monger 2011年8月12日 7:51
    • 回答としてマーク monger_monger 2011年8月25日 4:26
    2011年8月11日 22:13
  • Tetsuaki Uchida様

    お返事ありがとうございます。お恥ずかしいことにExpression Blendの存在を知りませんでした。

    不勉強で申し訳ありません。そしてご教示ありがとうございました。

    明日から盆休みに入ってしまうので、フィードバックなどは再来週以降になると思います。皆様申し訳ありません。

     

    フィードバックできる品質のものが出来上がり次第追加発言し、Tetsuaki Uchida様のお返事を回答としてマークさせて頂きます。

     

    2011/08/25 追記

    Expression Blendは有料のツールなので手が出ません。

    フィードバックは断念して、Tetsuaki Uchida様のお返事を回答としてマークさせて頂きます。

    2011年8月12日 7:29