整合营销服务商

电脑端+手机端+微信端=数据同步管理

免费咨询热线:

自定义日历(一)

自定义日历(一)

、概要

最近一直比较忙,根本没时间记录自己想写的东西,趁着国庆小长假,我自己也列出了想记录的一些小东西,方便他人借鉴,也方便我自己学习。废话不多说,我先来贴上我自己的demo展示图,这个demo比较复杂,我可能会分两篇博客来讲述。与其说是因为demo复杂,还不如说是这个月份是按照完全不同的两种思路来展示。

月份展示效果

如上图所示,这是两种日历展示形式,上方的日历是用label拼成,下方的日历是用一个窗口绘制而成,完全不同的两种路线,各有优缺点。接下来我将会分别介绍这两种月份的实现过程。

2、优缺点比较

这两种日历的优缺点我总结了一个简单的表格,如下表:


label平凑

widget自绘

优点

实现思路简单容易理解,每天都是一个label

绘制不同于其他天比较方便(蓝点)

实现稍微复杂,每天的位置需要自己计算

相对于第一种相率高,内存占用率低,事件处理层级少

窗口放大缩小效率高

背景色渐变容易实现

缺点

每天都是一个label,对性能有影响

窗口大小变化时麻烦

背景色渐变难以实现

widget每次整个重绘,每一天上的内容绘制比较难以计算

两种月份优缺点比较

本片文字我重点介绍下第二种日历的绘制,也就是整个窗口重新绘制,这种日历的实现难点就在于窗口事件的处理上,设计了好的数据结构,我们的程序处理才会变得简单。

3、数据结构设计

1 struct tDayFlag
2 {
3     signed char m_chFlagM; // -1 pre 0 cur 1 next
4     unsigned short m_chFlagD; // day num
5 };

这个结构负责存储每一天的信息,m_chFlagM这个字段表示是否是当前月份的,-1:上一个月,1:下一个月,0表示当前月份;m_chFlagD存储是一个月中的那一天。
接下来是一个impl接口类
DrawDateTimePrivate,这个类中存储了大量的计算信息,负责日历的数据计算和比较

  1 struct DrawDateTime::DrawDateTimePrivate
  2 {
  3 public:
  4     DrawDateTimePrivate(DrawDateTime * s)
  5         : m_Self(s)
  6         //, m_pOnChanged(NULL)
  7     {
  8         m_aRect=new QRect[m_column_count * m_row_count];
  9         m_aDayFlag=new tDayFlag[m_column_count * m_row_count];
 10         m_sOverIndex=-1;
 11 
 12         SYSTEMTIME st;
 13         GetLocalTime(&st);
 14         m_wYear=st.wYear;
 15         m_wMonth=st.wMonth;
 16         m_wDay=st.wDay;
 17     }
 18 
 19     ~DrawDateTimePrivate()
 20     {
 21         delete []m_aRect;
 22         delete []m_aDayFlag;
 23     }
 24 
 25 public:
 26     unsigned int GetColumnLeft(int column)
 27     {
 28         unsigned int left=(width - leftBorder - rightBorder + columnSpace) / m_column_count * column + leftBorder;
 29 
 30         return left;
 31     }
 32 
 33     unsigned int GetColumnRight(int column)
 34     {
 35         if (column < 0 || column > m_column_count)
 36         {
 37             column=0;
 38         }
 39 
 40         //总宽度-左border-由border-(m_column_count-1)*列间隙
 41         unsigned int itemWidth=(width - leftBorder - rightBorder - (m_column_count - 1) * columnSpace) / m_column_count;
 42         unsigned int right=GetColumnLeft(column) + itemWidth;
 43 
 44         return right;
 45     }
 46 
 47     unsigned int GetRowTop(int row)
 48     {
 49         QFontMetrics fm(weekFont);
 50         int weekHeight=fm.height();
 51 
 52         unsigned int top=(height - topBorder - bottomBorder - weekHeight + rowSpace) / m_row_count * row
 53             + space + weekHeight;
 54 
 55         return top;
 56     }
 57 
 58     unsigned int GetRowBottom(int row)
 59     {
 60         if (row < 0 || row > m_row_count)
 61         {
 62             row=0;
 63         }
 64 
 65         QFontMetrics fm(weekFont);
 66         int weekHeight=fm.height();
 67 
 68         //总高度-上border-下border-(m_row_count-1)*行间隙-week高
 69         unsigned int itemHeight=(height - topBorder - bottomBorder - weekHeight - space - (m_row_count - 1) * rowSpace) / m_row_count;
 70         unsigned int bottom=GetRowTop(row) + itemHeight;
 71 
 72         return bottom;
 73     }
 74 
 75 public:
 76     int columnSpace=5;
 77     int rowSpace=5;
 78 
 79     int leftBorder=10;
 80     int rightBorder=10;
 81     int topBorder=5;
 82     int bottomBorder=5;
 83 
 84     int width=100;
 85     int height=80;
 86 
 87     int space=10;//周名称和天之间距离
 88 
 89     int m_column_count=7;//列数
 90     int m_row_count=6;//行数
 91 
 92     QFont weekFont=QFont(STR("微软雅黑", 14));
 93     QFont dayFont;
 94 
 95 public:
 96     DrawDateTime * m_Self;
 97 
 98     //    IDateInfoChangedNotify * m_pOnChanged;
 99 
100     unsigned short m_wYear;
101     unsigned short m_wMonth;
102     unsigned short m_wDay;
103 
104     tDayFlag * m_aDayFlag;    // 各个按钮日期号
105     QRect * m_aRect;        // 各个按钮区域
106     short m_sOverIndex;    // 热点按钮下标
107 
108     bool MatchRealDate(tDayFlag df)
109     {
110         if (df.m_chFlagD==m_wDay && 0==df.m_chFlagM)
111         {
112             return true;
113         }
114         return false;
115     }
116 
117     //重置当前月份上的日期flag及显示的数据
118     void ResetDayFlag()
119     {
120         unsigned short  preY, preM;
121         GetPreviousMonth(preY, preM);
122 
123         int nPreMonDays=DayofMonth(preY, preM);
124         int nCurMonDays=DayofMonth(m_wYear, m_wMonth);
125         int week=CalDayofWeek(m_wYear, m_wMonth, 1);
126 
127         int index=0;
128 
129         for (int i=0; i < week; ++i, index++)
130         {
131             m_aDayFlag[index].m_chFlagM=-1;
132             m_aDayFlag[index].m_chFlagD=(nPreMonDays - week + 1) + i;
133         }
134 
135         for (int i=0; i < nCurMonDays; ++i, index++)
136         {
137             m_aDayFlag[index].m_chFlagM=0;
138             m_aDayFlag[index].m_chFlagD=i + 1;
139         }
140 
141         m_row_count=index / 7 + (index % 7==0 ? 0 : 1);
142 
143         for (int j=1; index < m_column_count * m_row_count; ++j, ++index)
144         {
145             m_aDayFlag[index].m_chFlagM=1;
146             m_aDayFlag[index].m_chFlagD=j;
147         }
148     }
149 
150     //获取上一个月的年和月份
151     void GetPreviousMonth(unsigned short & preYear, unsigned short & preMonth)
152     {
153         if (m_wMonth > 1)
154         {
155             preYear=m_wYear;
156             preMonth=m_wMonth - 1;
157         }
158         else
159         {
160             preYear=m_wYear - 1;
161             preMonth=12;
162         }
163     }
164 
165     //获取下一个月的年和月份
166     void GetNextMonth(unsigned short & nextYear, unsigned short & nextMonth)
167     {
168         if (m_wMonth >=12)
169         {
170             nextYear=m_wYear + 1;
171             nextMonth=1;
172         }
173         else
174         {
175             nextYear=m_wYear;
176             nextMonth=m_wMonth + 1;
177         }
178     }
179 };
GetColumnLeft:获取指定列的左边界
GetColumnRight:获取指定列的右边界
GetRowTop:获取指定行的上边界
GetRowBottom:获取指定行的下边界
MatchRealDate:检测给定日期是否是当前天
ResetDayFlag:重置impl中的内存数据
GetPreviousMonth:获取上一个月份的年和日
GetNextMonth:获取下一个月份的年和日

4、区域生成

区域生成顾名思义就是生成日期的绘制区域,这个需要根据当前窗口的大小、列间距、行间距等信息来计算每一天的矩形区域,当有月份切换时需要重新计算该信息,如果觉着这个过程对性能没有影响可以在每次整个绘制的时候都重新计算,这样有助于程序在出错时自动恢复。关于区域自动生成,在上一个小节我们已经给出了接口解释,接下来我将贴出实现代码,并做相应解释

 1 //重置当前月份上的日期flag及显示的数据
 2     void ResetDayFlag()
 3     {
 4         unsigned short  preY, preM;
 5         GetPreviousMonth(preY, preM);
 6 
 7         int nPreMonDays=DayofMonth(preY, preM);
 8         int nCurMonDays=DayofMonth(m_wYear, m_wMonth);
 9         int week=CalDayofWeek(m_wYear, m_wMonth, 1);
10 
11         int index=0;
12 
13         for (int i=0; i < week; ++i, index++)//重置上一个月份的日期
14         {
15             m_aDayFlag[index].m_chFlagM=-1;
16             m_aDayFlag[index].m_chFlagD=(nPreMonDays - week + 1) + i;
17         }
18 
19         for (int i=0; i < nCurMonDays; ++i, index++)//重置本月份的日期
20         {
21             m_aDayFlag[index].m_chFlagM=0;
22             m_aDayFlag[index].m_chFlagD=i + 1;
23         }
24 
25         m_row_count=index / 7 + (index % 7==0 ? 0 : 1);//更新行数
26 
27         for (int j=1; index < m_column_count * m_row_count; ++j, ++index)//重置下一个月份的日期
28         {
29             m_aDayFlag[index].m_chFlagM=1;
30             m_aDayFlag[index].m_chFlagD=j;
31         }
32     }

5、点击位置是哪一天

在自绘制日历的时候,点击位置或者hover位置是哪一个日期判断是非常重要的,这个涉及到整个日历是否是有一个友好的交互。在开始做这个基于widget绘制日的时候我也迷茫过,觉得判断点击位置是哪一天非常困难,实时也是这样的,判断起来是非常困难的,直到后来我看到了一个网友的数据结构设计,原来这个判断是如此的简单,不过简单的判断都是基于一个优秀的结构设计,判断代码如下:

 1 int DrawDateTime::GetIndex(const QPoint & point)
 2 {
 3     int id=-1;
 4     for (int i=0; i < _ptr->m_row_count * _ptr->m_column_count + 4; ++i)
 5     {
 6         QRect & rc=_ptr->m_aRect[i];
 7         if (point.x() > rc.left() && point.x()< rc.right()
 8             && point.y() > rc.top() && point.y() < rc.bottom())
 9         {
10             id=i;
11             break;
12         }
13     }
14 
15     return id;
16 }

上述判断只用了一个循环就可以判断出点击位置是在那个日期上,_ptr->m_aRect这个结构存储了所有日期的矩形区域,他在合适的时机就会重置。

6、周内容绘制

 1 void DrawDateTime::DrawWeek(QPainter & painter)
 2 {
 3     QString aText[7]={ STR("周日"), STR("周一"), STR("周二"), STR("周三"), STR("周四"), STR("周五"), STR("周六") };
 4 
 5     painter.save();
 6     painter.setFont(_ptr->weekFont);
 7     QFontMetrics fm(_ptr->weekFont);
 8     int height=fm.height();
 9     for (int i=0; i < 7; ++i)
10     {
11         int left=_ptr->GetColumnLeft(i);
12         int right=_ptr->GetColumnRight(i);
13         QRect rect(left, _ptr->topBorder, right - left, height);
14         painter.drawRect(rect);
15         painter.drawText(rect, Qt::AlignCenter, aText[i]);
16     }
17     painter.restore();
18 }

7、日期内容绘制

 1 void DrawDateTime::DrawDay(QPainter & painter)
 2 {
 3     _ptr->ResetDayFlag();
 4 
 5     painter.save();
 6 
 7     QFontMetrics fm(_ptr->weekFont);
 8     int weekHeight=fm.height();
 9 
10     for (int column=0; column < _ptr->m_column_count; ++column)
11     {
12         int column_left=_ptr->GetColumnLeft(column);
13         int column_right=_ptr->GetColumnRight(column);
14         for (int row=0; row < _ptr->m_row_count; ++row)
15         {
16             int index=row * _ptr->m_column_count + column;
17             QRect & rcTmp=_ptr->m_aRect[index];
18             tDayFlag & flag=_ptr->m_aDayFlag[index];
19 
20             rcTmp.setLeft(column_left);
21             rcTmp.setRight(column_right);
22             rcTmp.setTop(_ptr->GetRowTop(row));
23             rcTmp.setBottom(_ptr->GetRowBottom(row));
24 
25             QPainterPath path;
26             path.addRoundRect(rcTmp, 25);
27 
28             if (index==_ptr->m_sOverIndex)//hover时背景色
29             {
30                 painter.fillPath(path, QColor(144, 151, 151));
31             }
32 
33             painter.save();
34             if (-1==flag.m_chFlagM || 1==flag.m_chFlagM)
35             {
36                 painter.setPen(QColor(Qt::blue));
37             }
38             else
39             {
40                 if (_ptr->MatchRealDate(flag))
41                 {
42                     painter.setPen(QColor(Qt::red));
43                 }
44                 else if (index==_ptr->m_sOverIndex)
45                 {
46                     painter.setPen(QColor(Qt::white));
47                 }
48                 else
49                 {
50 
51                 }
52             }
53             painter.setOpacity(0.5);//绘制半透明度字
54             painter.drawText(rcTmp, Qt::AlignCenter, QString::number(flag.m_chFlagD));
55 
56             painter.restore();
57 
58             painter.drawPath(path);
59         }
60     }
61 
62     painter.restore();
63 }

8、月份切换

 1 void DrawDateTime::PreviousMonth()//上一个月份
 2 {
 3     unsigned short year, month;
 4     _ptr->GetPreviousMonth(year, month);
 5 
 6     int acturlDays=DayofMonth(year, month);
 7     if (acturlDays < _ptr->m_wDay)
 8     {
 9         _ptr->m_wDay=acturlDays;
10     }
11     SetDate(year, month, _ptr->m_wDay);
12 
13     update();
14 }
15 
16 void DrawDateTime::NextMonth()//下一个月份
17 {
18     unsigned short year, month;
19     _ptr->GetNextMonth(year, month);
20 
21     int acturlDays=DayofMonth(year, month);
22     if (acturlDays < _ptr->m_wDay)
23     {
24         _ptr->m_wDay=acturlDays;
25     }
26     SetDate(year, month, _ptr->m_wDay);
27 
28     update();
29 }

9、日期点击时效果

 1 void DrawDateTime::mousePressEvent(QMouseEvent * event)
 2 {
 3     if (event->button()==Qt::LeftButton)
 4     {
 5         int cur=GetIndex(event->pos());
 6         tDayFlag & flag=_ptr->m_aDayFlag[cur];
 7         
 8         unsigned short year=_ptr->m_wYear, month=_ptr->m_wMonth;
 9         if (flag.m_chFlagM==-1)
10         {
11             _ptr->GetPreviousMonth(year, month);
12         }
13         else if (flag.m_chFlagM==1)
14         {
15             _ptr->GetNextMonth(year, month);
16 
17         }
18         bool b=(_ptr->m_wDay !=flag.m_chFlagD || month !=_ptr->m_wMonth || year !=_ptr->m_wYear);
19         if (b)
20         {
21             _ptr->m_wDay=flag.m_chFlagD;
22             _ptr->m_wMonth=month;
23             _ptr->m_wYear=year;
24             update();
25         }
26     }
27 }

日期点击时,如果点击的是上一个月份的日期,则把当前月份切换到上一个月;如果点击的是下一个月的日期,则把当前月份切换到下一个月;否则当前月份不变,当前点击的日期颜色变成白色。

10、鼠标移动时效果

 1 void DrawDateTime::mouseMoveEvent(QMouseEvent * event)
 2 {
 3     int cur=GetIndex(event->pos());
 4     bool b=(cur !=_ptr->m_sOverIndex);
 5     if (b)
 6     {
 7         int previousHover=_ptr->m_sOverIndex;
 8         _ptr->m_sOverIndex=cur;
 9         update(_ptr->m_aRect[cur]);
10         update(_ptr->m_aRect[previousHover]);
11     }
12 
13     QWidget::mouseMoveEvent(event);
14 }

鼠标hover时,修改当前所hover的日期,然后在重新绘制时,如果是hover的日期,则绘制颜色变为QColor(144, 151, 151)

点击领取Qt学习资料+视频教程~「链接」

11、实现动画

本文所讲述的这种日历在demo中实现时没有使用动画来切换月份,但是用label拼凑的日历使用了动画来切换月份,如果有兴趣的同学可以把本文后面提供的demo下载下来,自行进行修改,修改的时候可以参考label拼凑月份的动画,由于label拼凑的月份实现起来更为繁琐,因此本片文章就不继续讲解了,在下一篇文章中我将讲解一些关键的实现思路和不足。顺便提一句,支持动画切换月份的日历控件时在一个窗口的基础上切换的,感兴趣的同学也可以实现5个月份的切换,就像一些音乐播放器主页上的音乐提示一样,是一个轮播的形式。例如网易音乐

网易轮播

日历同意可以以这样的形式来切换,这样的功能我已经实现了,如果感兴趣的同学可以私聊,demo中的代码在完善优化下,就可以做出这个效果,基于widget完全绘制的日历可能更合适这样的切换,label的堆砌在切换的时候可能会有效率的问题,这就取决于你的机器了,一般的机器还都是没有问题的,除非你要做对效率要求很高的程序。

关于label拼凑的日历我将会在自定义日历(二)给出详细讲解

作者:朝十晚八 or Twowords

转载:https://www.cnblogs.com/swarmbees/p/5927823.html

eb 服务器控件是服务器可理解的特殊 ASP.NET 标签。


Web 服务器控件

就像 HTML 服务器控件,Web 服务器控件也是在服务器上创建的,它们同样需要 runat="server" 属性才能生效。然而,Web 服务器控件没有必要映射任何已存在的 HTML 元素,它们可以表示更复杂的元素。

创建 Web 服务器控件的语法是:

<asp:control_name id="some_id" runat="server" />

Web 服务器控件描述
AdRotator显示一个图形序列
Button显示下压按钮
Calendar显示日历
CalendarDaycalendar 控件中的一天
CheckBox显示复选框
CheckBoxList创建多选的复选框组
DataGrid显示 grid 中数据源的字段
DataList通过使用模版显示数据源中的项目
DropDownList创建下拉列表
HyperLink创建超链接
Image显示图像
ImageButton显示可点击的图像
Label显示可编程的静态内容(使您对其内容应用样式)
LinkButton创建超链接按钮
ListBox创建单选或多选的下拉列表
ListItem创建列表中的一个项目
Literal显示可编程的静态内容(无法使您对其内容应用样式)
Panel为其他控件提供容器
PlaceHolder为由代码添加的控件预留空间
RadioButton创建单选按钮
RadioButtonList创建单选按钮组
BulletedList创建项目符号格式的列表
Repeater显示绑定到控件的项目的重复列表
Style设置控件的样式
Table创建表格
TableCell创建表格单元格
TableRow创建表格行
TextBox创建文本框
Xml显示 XML 文件或 XSL 转换的结果

ccess快速开发平台一个一直被忽略的功能,即通用日期选择器可以对日期进行选择设置日期,可以选择时间、选择时分功能,之前一直有人问日期控件能不能选择时间、选择分钟?现在把这个时间选取器的设置方法分享给大家学习一下。

如果你还没有下载Access快速开发平台,则可以先下载一个空白版平台打开测试一下该功能,Access开发平台最新版下载地址:

http://www.accessgood.com


Access快速开发平台时间选取器的设置步骤:

1.文本框控件,单击里面写 =DatePickerFor([Text0],False)

设为False 只选取日期


2.日期选择的显示效果如下图:


3.文本框控件,单击里面写 =DatePickerFor([Text0],True)

设为True 带调时分


4.日期时间、日期分钟选择的显示效果如下图:


5.查看最终效果:对需要选择设置日期、选择设置时间的朋友来说,这样看起来就方便多了。


更多Access快速开发平台自带的函数\功能用法,请查阅帮助中心详细了解!

《盟威软件快速开发平台》在线帮助中心 (http://www.accessgood.com/help/Main.html)


Access快速开发平台--通用日期选择器设置选择日期、选择时间、选择时分、时间选取器的设置方法【Access软件网】