CalendarDayButtonStyle:
<Style x:Key="CalendarDayButtonStyle1"
TargetType="System_Windows_Controls_Primitives:CalendarDayButton">
<Setter Property="Background"
Value="#FFBADDE9" />
<Setter Property="FontSize"
Value="10" />
<Setter Property="HorizontalContentAlignment"
Value="Center" />
<Setter Property="VerticalContentAlignment"
Value="Center" />
<Setter Property="MinWidth"
Value="5" />
<Setter Property="MinHeight"
Value="5" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="System_Windows_Controls_Primitives:CalendarDayButton">
<Grid>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualStateGroup.Transitions>
<VisualTransition GeneratedDuration="0:0:0.1" />
</VisualStateGroup.Transitions>
<VisualState x:Name="Normal" />
<VisualState x:Name="MouseOver">
<Storyboard>
<DoubleAnimation Duration="0"
To=".5"
Storyboard.TargetProperty="Opacity"
Storyboard.TargetName="Background" />
</Storyboard>
</VisualState>
<VisualState x:Name="Pressed">
<Storyboard>
<DoubleAnimation Duration="0"
To=".5"
Storyboard.TargetProperty="Opacity"
Storyboard.TargetName="Background" />
</Storyboard>
</VisualState>
<VisualState x:Name="Disabled">
<Storyboard>
<DoubleAnimation Duration="0"
To="0"
Storyboard.TargetProperty="Opacity"
Storyboard.TargetName="Background" />
<DoubleAnimation Duration="0"
To=".35"
Storyboard.TargetProperty="Opacity"
Storyboard.TargetName="Content" />
</Storyboard>
</VisualState>
</VisualStateGroup>
<VisualStateGroup x:Name="SelectionStates">
<VisualStateGroup.Transitions>
<VisualTransition GeneratedDuration="0" />
</VisualStateGroup.Transitions>
<VisualState x:Name="Unselected" />
<VisualState x:Name="Selected">
<Storyboard>
<DoubleAnimation Duration="0"
To=".75"
Storyboard.TargetProperty="Opacity"
Storyboard.TargetName="SelectedBackground" />
</Storyboard>
</VisualState>
</VisualStateGroup>
<VisualStateGroup x:Name="CalendarButtonFocusStates">
<VisualStateGroup.Transitions>
<VisualTransition GeneratedDuration="0" />
</VisualStateGroup.Transitions>
<VisualState x:Name="CalendarButtonFocused">
<Storyboard>
<ObjectAnimationUsingKeyFrames Duration="0"
Storyboard.TargetProperty="Visibility"
Storyboard.TargetName="FocusVisual">
<DiscreteObjectKeyFrame KeyTime="0"
Value="Visible" />
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<VisualState x:Name="CalendarButtonUnfocused">
<Storyboard>
<ObjectAnimationUsingKeyFrames Duration="0"
Storyboard.TargetProperty="Visibility"
Storyboard.TargetName="FocusVisual">
<DiscreteObjectKeyFrame KeyTime="0"
Value="Collapsed" />
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
</VisualStateGroup>
<VisualStateGroup x:Name="ActiveStates">
<VisualStateGroup.Transitions>
<VisualTransition GeneratedDuration="0" />
</VisualStateGroup.Transitions>
<VisualState x:Name="Active" />
<VisualState x:Name="Inactive">
<Storyboard>
<ColorAnimation Duration="0"
To="#FF777777"
Storyboard.TargetProperty="(ContentControl.Foreground).(GradientBrush.GradientStops)[2].(GradientStop.Color)"
Storyboard.TargetName="Content" />
<ColorAnimation Duration="0"
To="#FF777777"
Storyboard.TargetProperty="(ContentControl.Foreground).(GradientBrush.GradientStops)[3].(GradientStop.Color)"
Storyboard.TargetName="Content" />
</Storyboard>
</VisualState>
</VisualStateGroup>
<VisualStateGroup x:Name="DayStates">
<VisualStateGroup.Transitions>
<VisualTransition GeneratedDuration="0" />
</VisualStateGroup.Transitions>
<VisualState x:Name="RegularDay" />
<VisualState x:Name="Today">
<Storyboard>
<DoubleAnimation Duration="0"
To="1"
Storyboard.TargetProperty="Opacity"
Storyboard.TargetName="TodayBackground" />
<DoubleAnimation Duration="0"
To="1"
Storyboard.TargetProperty="(ContentControl.Foreground).(GradientBrush.GradientStops)[1].(GradientStop.Offset)"
Storyboard.TargetName="Content" />
<DoubleAnimation Duration="0"
To="1"
Storyboard.TargetProperty="(ContentControl.Foreground).(GradientBrush.GradientStops)[2].(GradientStop.Offset)"
Storyboard.TargetName="Content" />
</Storyboard>
</VisualState>
</VisualStateGroup>
<VisualStateGroup x:Name="BlackoutDayStates">
<VisualStateGroup.Transitions>
<VisualTransition GeneratedDuration="0" />
</VisualStateGroup.Transitions>
<VisualState x:Name="NormalDay" />
<VisualState x:Name="BlackoutDay">
<Storyboard>
<DoubleAnimation Duration="0"
To=".2"
Storyboard.TargetProperty="Opacity"
Storyboard.TargetName="BlackoutVisual" />
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Rectangle x:Name="TodayBackground"
Fill="#FFAAAAAA"
Opacity="0"
RadiusY="1"
RadiusX="1" />
<Rectangle x:Name="SelectedBackground"
Fill="{TemplateBinding Background}"
Opacity="0"
RadiusY="1"
RadiusX="1" />
<Rectangle x:Name="Background"
Fill="{TemplateBinding Background}"
Opacity="0"
RadiusY="1"
RadiusX="1" />
<ContentControl x:Name="Content"
ContentTemplate="{TemplateBinding ContentTemplate}"
Content="{TemplateBinding Content}"
FontSize="{TemplateBinding FontSize}"
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
IsTabStop="False"
Margin="5,1,5,1"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
Style="{StaticResource ContentControlStyle1}">
<ContentControl.Foreground>
<LinearGradientBrush>
<GradientStop Color="#FFFFFFFF"
Offset="0" />
<GradientStop Color="#FFFFFFFF"
Offset="0" />
<GradientStop Color="#FF333333"
Offset="0" />
<GradientStop Color="#FF333333"
Offset="1" />
</LinearGradientBrush>
</ContentControl.Foreground>
</ContentControl>
<Path x:Name="BlackoutVisual"
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"
Fill="#FF000000"
HorizontalAlignment="Stretch"
Margin="3"
Opacity="0"
RenderTransformOrigin="0.5,0.5"
Stretch="Fill"
VerticalAlignment="Stretch" />
<Rectangle x:Name="FocusVisual"
IsHitTestVisible="false"
RadiusY="1"
RadiusX="1"
Stroke="#FF6DBDD1"
Visibility="Collapsed" />
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
这次先看看折叠版:
只有ContentControl是需要关注的核心,再看下Blend中的视图
注意到content的datacontext属性,不再是集成的Calendar,而是DateTime!也就是每一格的日期!而且至今我仍未有找到这个格子可绑定Observable<>属性的Source。
因为格子里既有原生的日期,也有需要自定义的部分,所以我们将Content用另一个模板加载:
<Style x:Key="ContentControlStyle1"
TargetType="ContentControl">
<Setter Property="Foreground" Value="#FF000000" />
<Setter Property="HorizontalContentAlignment" Value="Left" />
<Setter Property="VerticalContentAlignment" Value="Top" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ContentControl">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"></RowDefinition>
<RowDefinition Height="*"></RowDefinition>
</Grid.RowDefinitions>
<ContentPresenter Cursor="{TemplateBinding Cursor}" ContentTemplate="{TemplateBinding ContentTemplate}" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" Margin="{TemplateBinding Padding}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}">
<TextBlock FontFamily="Georgia"
FontSize="15"
Text="{TemplateBinding Content}"></TextBlock>
</ContentPresenter>
<TextBlock Grid.Row="1"
FontSize="8.5"
Foreground="#aa161616">
<TextBlock.Text>
<Binding Converter="{StaticResource LunaDateConverter}"/>
</TextBlock.Text>
</TextBlock>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
这个模板定义了content的样式,同时,利用一个ValueConverter实现了公历日期(DataContext)向农历日期的转换。
本来这样也就OK了,可用ValueConverter的时候只有一个日期参数,有时我们需要相同的日期显示不同的内容,例如不同的班组会有不同的班次安排等。
自然的,一开始便想用ConverterParameter将ViewModel实例作为参数传进去,例如:
<TextBlock Grid.Row="1"
FontSize="8.5"
Text='{Binding Converter={StaticResource DateToShiftContentConverter},ConverterParameter={Binding DataContext}, Mode=OneWay}'
Foreground="#aa161616"/>
但正如我之前所言,DayButton的Content绑定的DataContext不再是继承于Calendar,而是当前格子的日期,因此此处的Binding是找不到通向ViewModel的路径的。后来我想用TemplateBinding尝试将ViewModel从父级一层层传下来(如利用Tag属性)但还是无法找到父级,获取到的一直是null,终究作罢:
<TextBlock Grid.Row="1"
FontSize="8.5"
Text='{Binding Converter={StaticResource DateToShiftContentConverter},ConverterParameter={TemplateBinding Tag}, Mode=OneWay}'
Foreground="#aa161616"/>
期间我在网上各种查资料,试图设法将属性绑定到DayButton上,比如Josh Smith的 Attaching a Virtual Branch to the Logical Tree in WPF 就是一种通过所谓“逻辑树虚拟枝”技术将ViewModel间接保存下来供任意内容读取。但实现有点略复杂,试了试也没调好,遂作罢,但这篇文章给了我很好的思路。
最后,我通过ValueConverter的声明想到,可以尝试通过一个中间的静态资源实现ViewModel的传递:
<UserControl.Resources>
<ViewModelConverter:DateToShiftContentConverter x:Key='DateToShiftContentConverter' />
<ViewModel:CalendarMonthViewModel x:Key='CalendarMonthViewModel' />
......
</UserControl.Resources>
<UserControl.DataContext>
<Binding Source='{StaticResource CalendarMonthViewModel}' />
</UserControl.DataContext>
<TextBlock Grid.Row="1"
FontSize="8.5"
Foreground="#aa161616"
Text='{Binding Converter={StaticResource DateToShiftContentConverter},ConverterParameter={StaticResource CalendarMonthViewModel}, Mode=OneWay}'
FontFamily="Microsoft YaHei" />
如此,就完美解决的参数的传递问题。
在对Calendar个性化设置的路上,虽然走了不少弯路,但最终实现了功能的同时,自己对Xaml和Wpf的一些特性有了更深入的了解和学习。