本文通过自定义calendar的开发,通过分析Silverlight自带的Calendar的样式、模板的分析,实现日历的定制化开发。
WPF中,自带的Calendar控件是这样子的:
而我们想要的结果是这样子的:
这个控件有两种方法去做。
一个是自己重新绘制图形,用一个7*6的表格来放日期,再根据当前月第一天的星期,计算出第一行第一列的日期,然后依次填充即可。详情可参考 Kelly Elias的这篇文章An Editable WPF Calendar Control,但这个控件其中用到了很重要的一个类UniformGrid在Silverlight中是没有的,需要自己重写,不要急,Jeff Wilco在博文UniformGrid for Silverlight中提供了解决方案。
另一个则是基于自带的Calendar,利用XAML强大的样式(Style)和模板(Template)功能完成扩展,也就是今天要讲的。
第一种方式实现简单容易扩展,可快速满足各种需求。但是功能太少,一些基本的日历功能都需要重写,比如月份更换、日期选择和反选等等。相反,第二种因为是基于自带的Calendar,不用引用过多的外部资源,即可拥有许多强大的功能,还能和其他控件融为一体等等。所以不用说,肯定用第二种的好。
关于Calendar的样式和模板,MSDN给了完整的示例。本文也是基于官方示例和非官方英文文献撰写。
<Grid x:Name="LayoutRoot" Background="White"> <ViewBox> <sdk:Calendar Name='calendar1' /> </ViewBox> </Grid>其中,ViewBox是用来实现Calendar自适应大小的。
一个Calendar可设置的样式包括Style、CalendarItemStyle、CalendarButtonStyle和CalendarDayButtonStyle
Style是用来设置Calendar整体的样式:
<Style x:Key="CalendarStyle1" targettype="sdk:Calendar"> <Setter Property="IsTabStop" Value="False" /> <Setter Property="Background"> <Setter.Value> <LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0"> <GradientStop Color="#FFD3DEE8" Offset="0" /> <GradientStop Color="#FFD3DEE8" Offset="0.16" /> <GradientStop Color="#FFFCFCFD" Offset="0.16" /> <GradientStop Color="#FFFFFFFF" Offset="1" /> </LinearGradientBrush> </Setter.Value> </Setter> -------------------------------------------------------------------------------------------------------------------------- <Setter Property="Template"> <Setter.Value> <ControlTemplate targettype="sdk:Calendar"> <Grid x:Name="Root" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"> <System_Windows_Controls_Primitives:CalendarItem VerticalAlignment="Stretch" VerticalContentAlignment="Stretch" HorizontalAlignment="Stretch" HorizontalContentAlignment="Stretch" x:Name="CalendarItem" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" Style="{StaticResource CalendarItemStyle1}" /> </Grid> </ControlTemplate> </Setter.Value> -------------------------------------------------------------------------------------------------------------------------- </Setter> </Style>
CalendarItemStyle是用来设置CalendarItem(即月份选择部分和日期排列),CalendarItem的设置也可以嵌套到Style中设置(Style代码中横线夹住部分)
<Style x:Key="CalendarItemStyle1" targettype="System_Windows_Controls_Primitives:CalendarItem"> <Setter Property='VerticalAlignment'Value='Stretch' /> <Setter Property='VerticalContentAlignment' Value='Stretch' /> <Setter Property='HorizontalAlignment' Value='Stretch' /> <Setter Property='HorizontalContentAlignment' Value='Stretch' /> <Setter Property="Template"> <Setter.Value> <ControlTemplate targettype="System_Windows_Controls_Primitives:CalendarItem"> <Grid HorizontalAlignment="Stretch" VerticalAlignment="Stretch"> <Grid.Resources> <SolidColorBrush x:Key="disabledBrush" Color="#8CFFFFFF" /> </Grid.Resources> <visualstatemanager.VisualStateGroups> <VisualStateGroup x:Name="CommonStates"> <VisualState x:Name="normal" /> <VisualState x:Name="disabled"> <Storyboard> <DoubleAnimation Duration="0" To="1" Storyboard.TargetProperty="Opacity" Storyboard.TargetName="disabledVisual" /> </Storyboard> </VisualState> </VisualStateGroup> </visualstatemanager.VisualStateGroups> <Border HorizontalAlignment="Stretch" VerticalAlignment="Stretch" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" CornerRadius="5" Margin="2"> <Border HorizontalAlignment="Stretch" VerticalAlignment="Stretch" BorderBrush="#FFFFFFFF" BorderThickness="2" CornerRadius="5" Padding="5"> <Grid HorizontalAlignment="Stretch" VerticalAlignment="Stretch"> <Grid.Resources> <ControlTemplate x:Key="HeaderButtonTemplate" targettype="Button"> <Grid Cursor="Hand"> <visualstatemanager.VisualStateGroups> <VisualStateGroup x:Name="CommonStates"> <VisualState x:Name="normal" /> <VisualState x:Name="MouSEOver"> <Storyboard> <ColorAnimation Duration="0" To="#FF73A9D8" Storyboard.TargetProperty="(ContentControl.Foreground).(SolidColorBrush.Color)" Storyboard.TargetName="Content" /> </Storyboard> </VisualState> <VisualState x:Name="disabled"> <Storyboard> <DoubleAnimation Duration="0" To=".5" Storyboard.TargetProperty="Opacity" Storyboard.TargetName="Content" /> </Storyboard> </VisualState> </VisualStateGroup> </visualstatemanager.VisualStateGroups> <ContentControl x:Name="Content" ContentTemplate="{TemplateBinding ContentTemplate}" Content="{TemplateBinding Content}" Foreground="#FF333333" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" IsTabStop="False" Margin="1,5,1,9" VerticalAlignment="{TemplateBinding VerticalContentAlignment}" /> </Grid> </ControlTemplate> <DataTemplate x:Name="DayTitleTemplate"> <TextBlock FontWeight="Bold" Margin="5" FontSize="12" HorizontalAlignment="Left" Text="{Binding}" VerticalAlignment="Center"></TextBlock> </DataTemplate> <ControlTemplate x:Key="PrevIoUsButtonTemplate" targettype="Button"> <Grid Cursor="Hand"> <visualstatemanager.VisualStateGroups> <VisualStateGroup x:Name="CommonStates"> <VisualState x:Name="normal" /> <VisualState x:Name="MouSEOver"> <Storyboard> <ColorAnimation Duration="0" To="#FF73A9D8" Storyboard.TargetProperty="(Path.Fill).(SolidColorBrush.Color)" Storyboard.TargetName="IconPath" /> </Storyboard> </VisualState> <VisualState x:Name="disabled"> <Storyboard> <DoubleAnimation Duration="0" To=".5" Storyboard.TargetProperty="(Path.Fill).(SolidColorBrush.Opacity)" Storyboard.TargetName="IconPath" /> </Storyboard> </VisualState> </VisualStateGroup> </visualstatemanager.VisualStateGroups> <Rectangle Fill="#11E5EBF1" Opacity="1" Stretch="Fill" /> <Grid> <Path x:Name="IconPath" Data="M288.75,232.25 L288.75,240.625 L283,236.625 z" Fill="#FF333333" HorizontalAlignment="Left" Height="10" Margin="14,-6,0" Stretch="Fill" VerticalAlignment="Center" Width="6" /> </Grid> </Grid> </ControlTemplate> <ControlTemplate x:Key="NextButtonTemplate" targettype="Button"> <Grid Cursor="Hand"> <visualstatemanager.VisualStateGroups> <VisualStateGroup x:Name="CommonStates"> <VisualState x:Name="normal" /> <VisualState x:Name="MouSEOver"> <Storyboard> <ColorAnimation Duration="0" To="#FF73A9D8" Storyboard.TargetProperty="(Path.Fill).(SolidColorBrush.Color)" Storyboard.TargetName="IconPath" /> </Storyboard> </VisualState> <VisualState x:Name="disabled"> <Storyboard> <DoubleAnimation Duration="0"To=".5" Storyboard.TargetProperty="(Path.Fill).(SolidColorBrush.Opacity)" Storyboard.TargetName="IconPath" /> </Storyboard> </VisualState> </VisualStateGroup> </visualstatemanager.VisualStateGroups> <Rectangle Fill="#11E5EBF1" Opacity="1" Stretch="Fill" /> <Grid> <Path x:Name="IconPath" Data="M282.875,231.875 L282.875,240.375 L288.625,236 z" Fill="#FF333333" HorizontalAlignment="Right" Height="10" Margin="0,14,0" Stretch="Fill" VerticalAlignment="Center" Width="6" /> </Grid> </Grid> </ControlTemplate> </Grid.Resources> <Grid.ColumnDe@R_502_4327@ns> <ColumnDe@R_502_4327@n Width="Auto" /> <ColumnDe@R_502_4327@n Width="Auto" /> <ColumnDe@R_502_4327@n Width="Auto" /> </Grid.ColumnDe@R_502_4327@ns> <Grid.RowDe@R_502_4327@ns> <RowDe@R_502_4327@n Height="Auto" /> <RowDe@R_502_4327@n Height="*" /> </Grid.RowDe@R_502_4327@ns> <Button x:Name="PrevIoUsButton" HorizontalAlignment="Left" Height="20" Template="{StaticResource PrevIoUsButtonTemplate}" Visibility="Collapsed" Width="28" /> <Button x:Name="HeaderButton" Grid.Column="1" FontWeight="Bold" FontSize="10.5" HorizontalAlignment="Stretch" HorizontalContentAlignment="Center" Template="{StaticResource ButtonControlTemplate1}" VerticalAlignment="Center" /> <Button x:Name="NextButton" Grid.Column="2" HorizontalAlignment="Right" Height="20" Template="{StaticResource NextButtonTemplate}" Visibility="Collapsed" Width="28" /> <Grid x:Name="MonthView" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" Grid.ColumnSpan="3" Grid.Row="1" Visibility="Collapsed"> <Grid.ColumnDe@R_502_4327@ns> <ColumnDe@R_502_4327@n Width="Auto" /> <ColumnDe@R_502_4327@n Width="Auto" /> <ColumnDe@R_502_4327@n Width="Auto" /> <ColumnDe@R_502_4327@n Width="Auto" /> <ColumnDe@R_502_4327@n Width="Auto" /> <ColumnDe@R_502_4327@n Width="Auto" /> <ColumnDe@R_502_4327@n Width="Auto" /> </Grid.ColumnDe@R_502_4327@ns> <Grid.RowDe@R_502_4327@ns> <RowDe@R_502_4327@n Height="Auto" /> <RowDe@R_502_4327@n Height="Auto" /> <RowDe@R_502_4327@n Height="Auto" /> <RowDe@R_502_4327@n Height="Auto" /> <RowDe@R_502_4327@n Height="Auto" /> <RowDe@R_502_4327@n Height="Auto" /> <RowDe@R_502_4327@n Height="Auto" /> </Grid.RowDe@R_502_4327@ns> </Grid> <Grid x:Name="YearView" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Grid.ColumnSpan="3" Grid.Row="1" Visibility="Collapsed"> <Grid.ColumnDe@R_502_4327@ns> <ColumnDe@R_502_4327@n Width="Auto" /> <ColumnDe@R_502_4327@n Width="Auto" /> <ColumnDe@R_502_4327@n Width="Auto" /> <ColumnDe@R_502_4327@n Width="Auto" /> </Grid.ColumnDe@R_502_4327@ns> <Grid.RowDe@R_502_4327@ns> <RowDe@R_502_4327@n Height="Auto" /> <RowDe@R_502_4327@n Height="Auto" /> <RowDe@R_502_4327@n Height="Auto" /> </Grid.RowDe@R_502_4327@ns> </Grid> </Grid> </Border> </Border> <Rectangle x:Name="disabledVisual" Fill="{StaticResource disabledBrush}" Margin="0,2,2" Opacity="0" RadiusY="2" RadiusX="2" Stretch="Fill" stroke="{StaticResource disabledBrush}" strokeThickness="1" Visibility="Collapsed" /> </Grid> </ControlTemplate> </Setter.Value> </Setter> </Style>
我们分析一下这个长长的XAML,Blend是分析样式和模板的一个非常好用的工具,在Blend中编辑该模板:
右边列出那长串XAML所包含的树形结构,折叠后的XAML即:
PrevIoUsButtonTemplate定义了“上一个月”按钮的样式,NextButton指定是“下一个月”按钮,而HeaderButton就是“月份”以及“日历名称”的部分。至于后面的MonthView和YearView,则分别定义了正常和点击当前月份后日历的显示。
在这里说一下,CalendarItem是在Calendar逻辑树下的,所以Calendar的DataContext是可以被CalendarItem获取到的,所以,在设置Calendar的各个Button时候,是可以绑定viewmodel(MVVM模式中VM概念)中的属性的。例如:
<ControlTemplate x:Key="ButtonControlTemplate" targettype="Button"> <Grid Cursor="Hand"> <Grid.RowDe@R_502_4327@ns> <RowDe@R_502_4327@n></RowDe@R_502_4327@n> <RowDe@R_502_4327@n></RowDe@R_502_4327@n> </Grid.RowDe@R_502_4327@ns> <ContentControl x:Name="Content" ContentTemplate="{TemplateBinding ContentTemplate}" Content="{TemplateBinding Content}" Foreground="#FF333333" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" IsTabStop="False" Margin="1,9" VerticalAlignment="{TemplateBinding VerticalContentAlignment}" /> <TextBlock Foreground="#ff666666" FontSize="9" Margin="-20" Grid.Row="1" VerticalAlignment="Center" HorizontalAlignment="Center" Text='{Binding CalendarModel.ResourceName}' /> </Grid> </ControlTemplate>其中,Text='{Binding CalendarModel.CalendarName}'是可以找到UserControl.DataContext绑定的Calendarviewmodel实例的。所以,通过这个Button模板,可以轻易动态修改日历名称。
下节中,本文将继续讨论CalendarDayButtonStyle的设置以及DayButton内容的绑定和设置
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 [email protected] 举报,一经查实,本站将立刻删除。