【C#】wpf自定义calendar日期选择控件的样式
首先上图,看下样式
原理
1. 总览:
Calendar本质上是一个6x7的列表,这个列表可以用ItemsControl来实现。其填充的每一个item都是一个自定义样式的Button,Button外面是一个圆形的border。根据Button按钮的IsEnabled状态,设置foreground以达到灰色不点击的效果。
2. ItemsControl内容的生成:
主要是想办法生成第一行第一列的日期,其他的日期只需要DateTime.AddDays(i)即可。代码参考:
private void SetCalendar(int year, int month)
{
_days.Clear();//请忽略
DateTime datetime = new DateTime(year, month, 1);
int week = (int)datetime.DayOfWeek;//获取指定月份的1号是周几
datetime = datetime.AddDays(1 - week);
for (int i = 0; i < 42; i++)
{
_days.Add(datetime.AddDays(i));
}
OnPropertyChanged("Days");//请忽略
}
首先,将当前年月传入,获取到当前月份的第一天是周几,然后推算出ItemsControl的第一行第一列的Datetime,最后就是顺次相加即可。
实现
1. 界面的实现
先上代码:
<Grid Grid.RowSpan="4" Margin="0,54,0,0" Visibility="{Binding IsShowCalendar,Converter={StaticResource BoolToVisibilityCollapseConverter}}" Background="White" Width="350" HorizontalAlignment="Right">
<Grid.RowDefinitions>
<RowDefinition Height="50"></RowDefinition>
<RowDefinition Height="auto"></RowDefinition>
<RowDefinition Height="auto"></RowDefinition>
<RowDefinition Height="*"></RowDefinition>
</Grid.RowDefinitions>
<StackPanel HorizontalAlignment="Center" Orientation="Horizontal">
<Button VerticalAlignment="Center" Command="{Binding YearCommand}" CommandParameter="Pre" Style="{StaticResource TransparentButtonStyle}">
<Button.Content>
<TextBlock Text="<" FontSize="20"></TextBlock>
</Button.Content>
</Button>
<TextBlock VerticalAlignment="Center" FontSize="15" Text="{Binding CurrentYear}"></TextBlock>
<Button VerticalAlignment="Center" Command="{Binding YearCommand}" CommandParameter="Next" Style="{StaticResource TransparentButtonStyle}">
<Button.Content>
<TextBlock Text=">" FontSize="20"></TextBlock>
</Button.Content>
</Button>
<Button Margin="50,10,10,10" VerticalAlignment="Center" Command="{Binding MonthCommand}" CommandParameter="Pre" Style="{StaticResource TransparentButtonStyle}">
<Button.Content>
<TextBlock Text="<" FontSize="20"></TextBlock>
</Button.Content>
</Button>
<TextBlock VerticalAlignment="Center" Text="{Binding CurrentMonth}" FontSize="15"></TextBlock>
<Button VerticalAlignment="Center" Command="{Binding MonthCommand}" CommandParameter="Next" Style="{StaticResource TransparentButtonStyle}">
<Button.Content>
<TextBlock Text=">" FontSize="20"></TextBlock>
</Button.Content>
</Button>
</StackPanel>
<UniformGrid Grid.Row="1" Columns="7">
<TextBlock Text="MON" TextAlignment="Center"></TextBlock>
<TextBlock Text="TUE" TextAlignment="Center"></TextBlock>
<TextBlock Text="WED" TextAlignment="Center"></TextBlock>
<TextBlock Text="THU" TextAlignment="Center"></TextBlock>
<TextBlock Text="FRI" TextAlignment="Center"></TextBlock>
<TextBlock Text="SAT" TextAlignment="Center"></TextBlock>
<TextBlock Text="SUN" TextAlignment="Center"></TextBlock>
</UniformGrid>
<ItemsControl Grid.Row="2" ScrollViewer.HorizontalScrollBarVisibility="Auto"
ScrollViewer.VerticalScrollBarVisibility="Auto"
ItemsSource="{Binding Days}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Button Background="{Binding Converter={StaticResource BACKUP_DateTimeToColorConverter}}" Style="{StaticResource CalendarTransparentButtonStyle}"
Content="{Binding Converter={StaticResource BACKUP_DateTimeToDayConverter}}"
Command="{Binding DataContext.ChooseDateCommand,RelativeSource={RelativeSource FindAncestor,AncestorType={x:Type Window}}}" CommandParameter="{Binding}"
MouseDoubleClick="Button_MouseDoubleClick">
<Button.Foreground>
<MultiBinding Converter="{StaticResource BACKUP_DateTimeToForegroundMultiConverter}">
<Binding></Binding>
<Binding Path="DataContext.CurrentMonth" RelativeSource="{RelativeSource FindAncestor,AncestorType={x:Type Window}}"></Binding>
</MultiBinding>
</Button.Foreground>
<Button.IsEnabled>
<MultiBinding Converter="{StaticResource BACKUP_DateTimeToEnableMultiConverter}">
<Binding></Binding>
<Binding Path="DataContext.CutoffDayBegin" RelativeSource="{RelativeSource FindAncestor,AncestorType={x:Type Window}}"></Binding>
<Binding Path="DataContext.CutoffDayEnd" RelativeSource="{RelativeSource FindAncestor,AncestorType={x:Type Window}}"></Binding>
</MultiBinding>
</Button.IsEnabled>
</Button>
</DataTemplate>
</ItemsControl.ItemTemplate>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid Columns="7"></UniformGrid>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
<StackPanel Grid.Row="3" HorizontalAlignment="Center" VerticalAlignment="Bottom" Margin="0,0,0,31" Orientation="Horizontal">
<Button Content="Cancel" Style="{StaticResource btnSecondaryStyle}" Grid.Row="2" Command="{Binding HideCalendarCommand}"></Button>
<Button Content="{Binding CalendarSetText}" Style="{StaticResource btnPrimaryStyle}" Grid.Row="2" Command="{Binding HideCalendarCommand}"></Button>
</StackPanel>
</Grid>
其中,大量用到了Binding和MVVM,如果对方面还不是很扎实,可以参考下刘铁锰的视频.
这段代码中,主要部分是ItemsControl的ItemsSource绑定。这里绑定的是Days,其类型是
public ObservableCollection<DateTime> _days = new ObservableCollection<DateTime>();
public ObservableCollection<DateTime> Days
{
get
{
return _days;
}
}
在设置Button的前景色和IsEnabled状态的时候,你也看到了用的是MultiBinding,并定义了两个Converter:
1. DateTimeToForegroundMultiConverter
class DateTimeToForegroundMultiConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
DateTime dt=new DateTime();
if(values[0] is DateTime)
{
dt = (DateTime)values[0];
}
int currentMonth;
Int32.TryParse(values[1].ToString(), out currentMonth);
if (dt.Month == currentMonth)
{
if(dt.ToShortDateString()==DateTime.Now.ToShortDateString())
{
return new SolidColorBrush(Colors.White);
}
else
{
return new SolidColorBrush(Colors.Black);
}
}
else
{
return new SolidColorBrush(Colors.Gray);
}
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
return null;
}
}
2. DateTimeToEnableMultiConverter
class DateTimeToEnableMultiConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
DateTime currentDay, cutoffDayBegin,cutoffDayEnd;
if(values[0] is DateTime&&values[1] is DateTime&&values[2] is DateTime)
{
currentDay = (DateTime)values[0];
cutoffDayBegin = (DateTime)values[1];
cutoffDayEnd = (DateTime)values[2];
if (DateTime.Compare(currentDay, cutoffDayBegin) >= 0 && DateTime.Compare(currentDay, cutoffDayEnd) <= 0)
{
return true;
}
else
{
return false;
}
}
else
{
return false;
}
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
return null;
}
}
2. 后台ViewModel的实现
其实后台没有什么代码,有一个SetCalendar方法,在月份或者年份发生变动的时候调用一下即可。
private void SetCalendar(int year, int month)
{
_days.Clear();
DateTime datetime = new DateTime(year, month, 1);
int week = (int)datetime.DayOfWeek;
datetime = datetime.AddDays(1 - week);
for (int i = 0; i < 42; i++)
{
_days.Add(datetime.AddDays(i));
}
OnPropertyChanged("Days");
}
还有一个就是Button点击之后的Command命令:
private void exeChooseDate(object obj)
{
DateTime dt;
DateTime.TryParse(obj.ToString(), out dt);
CurrentMonth = dt.Month;
CurrentYear = dt.Year;
}
Button每点击一次,就会执行一次这个方法,CurrentMonth和CurrentYear这两个属性的值就会被更改,一些来决定是否需要重新绘制UI切换的上一个/下一个月份。
CurrentMonth和CurrentYear属性见下:
private int _currentYear = 2010;
public int CurrentYear
{
get
{
return _currentYear;
}
set
{
if (_currentYear != value && value > 1978 && value < 9999)
{
_currentYear = value;
OnPropertyChanged("CurrentYear");
SetCalendar(_currentYear, CurrentMonth);
}
}
}
private int _currentMonth = 1;
public int CurrentMonth
{
get
{
return _currentMonth;
}
set
{
if (_currentMonth != value && value < 13 && value > 0)
{
_currentMonth = value;
OnPropertyChanged("CurrentMonth");
SetCalendar(CurrentYear, _currentMonth);
}
}
}

低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
-
上一篇
【C#】简单解决PathTooLong的Exception
原文: 【C#】简单解决PathTooLong的Exception 前提 windows系统路径的最大长度限制是260个字符(听说.Net 4.6.2,取消了这个限制),而Linux或者Unix系统的好像是4K个字符。所以如果一个文件夹由Linux系统创建,然后由windows系统读取处理的话,就会出现PathTooLong的异常。 解决方法 其实很简单 在VS中添加如下Nuget包: 在用到的地方把系统的File和Directory替换为LongPathFile和LongPathDirectory即可。如: var items = LongPathDirectory.EnumerateFileSystemEntries(path); 其它 之前还用过一个Delimon.Win32.IO 这个包,首先向Delimon大神表示敬意,这个包确实很强大,无奈没有EnumerateFileSystemEntries()方法,放弃了。
-
下一篇
【C#】WindowsAPICodePack-Shell使用教程
原文: 【C#】WindowsAPICodePack-Shell使用教程 1.首先在项目中添加WindowsAPICodePack的Nuget包。 点击安装即可。 2.获取《我的电脑》的名字和电脑图标 ShellObject so = ShellFileSystemFolder.FromParsingName(KnownFolders.Computer.ParsingName); displayName = so.Name; Bitmap bm = so.Thumbnail.SmallBitmap; bm.MakeTransparent(System.Drawing.Color.Black); 其中,so.Name就是“我的电脑”的名字,注意不是机器名哦,机器名可以直接通过Environment.MachineName获取。 电脑图标可以通过so的Thumbnail获取,可以获得SmallBitmap,Bitmap,LargeBitmap,SmallIco,Ico,LargeIco等。不过由于获取到的Bitmap的背景色由之前的透明色变成了黑色,可以是因为api的原因,所以需要我们用...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- SpringBoot2整合MyBatis,连接MySql数据库做增删改查操作
- Docker使用Oracle官方镜像安装(12C,18C,19C)
- Springboot2将连接池hikari替换为druid,体验最强大的数据库连接池
- MySQL数据库在高并发下的优化方案
- Docker快速安装Oracle11G,搭建oracle11g学习环境
- SpringBoot2编写第一个Controller,响应你的http请求并返回结果
- CentOS7,8上快速安装Gitea,搭建Git服务器
- CentOS6,7,8上安装Nginx,支持https2.0的开启
- CentOS8编译安装MySQL8.0.19
- Dcoker安装(在线仓库),最新的服务器搭配容器使用