【C#】wpf自定义calendar日期选择控件的样式

简介: 原文:【C#】wpf自定义calendar日期选择控件的样式 首先上图看下样式 原理总览 ItemsControl内容的生成 实现界面的实现 后台ViewModel的实现 首先上图,看下样式 原理 1. 总览: Calendar本质上是一个6x7的列表,这个列表可以用ItemsControl来实现。
原文: 【C#】wpf自定义calendar日期选择控件的样式

首先上图,看下样式

静态样式
gif动图

原理

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="&lt;" 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="&gt;" 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="&lt;" 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="&gt;" 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);
                }
            }
        }
目录
相关文章
|
2月前
|
SQL 开发框架 .NET
C#一分钟浅谈:数据绑定与数据源控件
在Web开发中,数据绑定和数据源控件是实现动态网页的关键技术。本文从基础概念入手,详细讲解数据绑定的原理及其在ASP.NET中的应用,并介绍常见数据绑定方式:手动绑定和自动绑定。接着,文章重点介绍了ASP.NET中的数据源控件,如`SqlDataSource`、`ObjectDataSource`、`XmlDataSource`和`LinqDataSource`,并通过具体示例演示如何使用`SqlDataSource`和`GridView`进行数据绑定。最后,还列举了一些常见问题及其解决办法,帮助读者更好地理解和应用这些技术。
82 4
|
4月前
|
C#
|
1月前
|
C# Python
使用wxpython开发跨平台桌面应用,对wxpython控件实现类似C#扩展函数处理的探究
【10月更文挑战第30天】使用 `wxPython` 开发跨平台桌面应用时,可以通过创建辅助类来模拟 C# 扩展函数的功能。具体步骤包括:1. 创建辅助类 `WxWidgetHelpers`;2. 在该类中定义静态方法,如 `set_button_color`;3. 在应用中调用这些方法。这种方法提高了代码的可读性和可维护性,无需修改 `wxPython` 库即可为控件添加自定义功能。但需要注意显式调用方法和避免命名冲突。
|
2月前
|
C#
C# 技巧:如何确定日期时间范围的日周月季度半年
C# 技巧:如何确定日期时间范围的日周月季度半年
38 1
C# WPF 中 外部图标引入iconfont,无法正常显示问题 【小白记录】
本文介绍了在C# WPF应用程序中引入外部iconfont图标时可能遇到的显示问题及其解决方法:1) 检查资源路径和引入格式是否正确,确保字体文件引用格式为“#xxxx”,并正确指向字体文件位置;2) 确保图标资源被包含在程序集中,通过设置字体文件的生成操作为Resource(资源)来实现。
C# WPF 中 外部图标引入iconfont,无法正常显示问题 【小白记录】
|
3月前
|
编解码 C# 数据库
C# + WPF 音频播放器 界面优雅,体验良好
【9月更文挑战第18天】这是一个用 C# 和 WPF 实现的音频播放器示例,界面简洁美观,功能丰富。设计包括播放/暂停按钮、进度条、音量控制滑块、歌曲列表和专辑封面显示。功能实现涵盖音频播放、进度条控制、音量调节及歌曲列表管理。通过响应式设计、动画效果、快捷键支持和错误处理,提升用户体验。可根据需求扩展更多功能。
137 3
|
4月前
|
开发框架 .NET 编译器
总结一下 C# 如何自定义特性 Attribute 并进行应用
总结一下 C# 如何自定义特性 Attribute 并进行应用
114 1
|
4月前
|
C# 开发者 Windows
一款基于Fluent设计风格、现代化的WPF UI控件库
一款基于Fluent设计风格、现代化的WPF UI控件库
108 1
|
4月前
|
开发者 C# 存储
WPF开发者必读:资源字典应用秘籍,轻松实现样式与模板共享,让你的WPF应用更上一层楼!
【8月更文挑战第31天】在WPF开发中,资源字典是一种强大的工具,用于共享样式、模板、图像等资源,提高了应用的可维护性和可扩展性。本文介绍了资源字典的基础知识、创建方法及最佳实践,并通过示例展示了如何在项目中有效利用资源字典,实现资源的重用和动态绑定。
111 0
|
4月前
|
C# 前端开发 UED
WPF数据验证实战:内置控件与自定义规则,带你玩转前端数据验证,让你的应用程序更上一层楼!
【8月更文挑战第31天】在WPF应用开发中,数据验证是确保输入正确性的关键环节。前端验证能及时发现错误,提升用户体验和程序可靠性。本文对比了几种常用的WPF数据验证方法,并通过示例展示了如何使用内置验证控件(如`TextBox`)及自定义验证规则实现有效验证。内置控件结合`Validation`类可快速实现简单验证;自定义规则则提供了更灵活的复杂逻辑支持。希望本文能帮助开发者更好地进行WPF数据验证。
146 0