ExcelHome技术论坛

 找回密码
 免费注册

QQ登录

只需一步,快速开始

快捷登录

搜索
EH技术汇-专业的职场技能充电站 妙哉!函数段子手趣味讲函数 Excel服务器-会Excel,做管理系统 效率神器,一键搞定繁琐工作
HR薪酬管理数字化实战 Excel 2021函数公式学习大典 Excel数据透视表实战秘技 打造核心竞争力的职场宝典
让更多数据处理,一键完成 数据工作者的案头书 免费直播课集锦 ExcelHome出品 - VBA代码宝免费下载
用ChatGPT与VBA一键搞定Excel WPS表格从入门到精通 Excel VBA经典代码实践指南
楼主: prome3

[分享] UI设计自制日历窗体(无需注册MSCOMCTL.OCX)

  [复制链接]

TA的精华主题

TA的得分主题

 楼主| 发表于 2024-1-2 23:58 | 显示全部楼层
[广告] Excel易用宝 - 提升Excel的操作效率 · Excel / WPS表格插件       ★免费下载 ★       ★ 使用帮助
各位,新版来了。

版本号1.0.4

此次更新相对以往版本做了如下改变:
1. 功能上依旧是极简模式,但界面不再是极简模式,而是保留控件的原生样式,并且用户窗体的标题栏也做了保留;
2. 之前的版本显示的是非模态窗口,此版改为强制显示模态窗口。这样做主要为了方便使用 With New ... End With 语句来调用日历,稳定性更高;

3. 之前的版本只作为示例,仅提供了在工作表上应用的例子,而没有在用户窗体上调用的接口。此版发布为一个可用的控件,开始启用版本号,并提供了在用户窗体上调用的接口。此版开始,这款日历控件既能在工作表上用,也能加入别的用户窗体上使用。在工作表上调用的接口放在了 MonthCalendar.bas 标准模块里,而在别的窗体上调用的接口放在了 frmMonthCal.frm 窗体模块里。
4. 日历由原先的选择日期后自动隐藏,改为手动点击右上角 X 隐藏。


以下提供在别的用户窗体上调用的例子和操作步骤:
1. 下载此楼层附件中的 month_calendar_v1.0.4.zip 压缩包,进入解压缩后的文件夹,其内有一个名为 src 的文件夹,src 文件夹内的两个文件,frmMonthCal.txt 和 MonthCalendar.txt ,即为此版日历窗体的全部源码。
2. 打开要应用日历的工作簿文件,打开VBA编辑器,插入一个标准模块,重命名为 MonthCalendar ,然后用记事本打开步骤1中的 MonthCalendar.txt 文件,全选,复制,然后粘贴进刚刚插入的 MonthCalendar 标准模块。接着再插入一个用户窗体模块,重命名为 frmMonthCal ,然后用记事本打开步骤1中的 frmMonthCal.txt 文件,全选,复制,粘贴进 frmMonthCal 模块中。
3. 绑定控件和创建接口。以下默认要调用日历窗体的用户窗体名称为 UserForm1 。
3.1 在 UserForm1 的代码中新建私有成员变量,类型为对象 (Object),例如:
  1. Private CalendarBindingControl As Object
复制代码
3.2 在 UserForm1 的代码中新建公开成员过程(Public Sub)作为对外接口,并且提供具体实现。当日历窗体被点击时,此过程会被调用,并且传入的是用户选择的日期。如下代码把传入的日期赋值给步骤3.1创建的对象:
  1. Public Sub DateSelected(ByVal d As Variant)
  2.     If Not CalendarBindingControl Is Nothing Then
  3.         CalendarBindingControl.Value = Format$(d, "YYYY/MM/DD")
  4.     End If
  5. End Sub
复制代码
3.3 在 UserForm1 的控件触发事件中绑定控件、日历和步骤3.1中的对象,其中 CalendarBindingControl 设置成指向该控件的指针。而控件负责日历对象的创建和销毁。如下所示:
  1. Private Sub TextBox1_Enter()
  2.     Set CalendarBindingControl = Me.TextBox1
  3.     With New frmMonthCal
  4.         .SetInitSelection Me.TextBox1.Value
  5.         .ShowOnControl Me.TextBox1
  6.     End With
  7. End Sub
复制代码
在上述代码片段中,CalendarBindingControl 对象指向 TextBox1 对象,当它的值改变时,即 TextBox1 的值也发生改变。“New frmMonthCal” 是 TextBox1 创建了一个新的日历,“SetInitSelection”设置日历显示的日期为 TextBox1 中之前输入的日期(如有),“ShowOnControl”则在 TextBox1 上弹出日历。最后,“End With”销毁 TextBox1 创建的这个日历。
3.4 其它文本框或组合框等控件也可做类似处理。例如:
  1. Private Sub TextBox3_Enter()
  2.     Set CalendarBindingControl = Me.TextBox3
  3.     With New frmMonthCal
  4.         .SetInitSelection Me.TextBox3.Value
  5.         .ShowOnControl Me.TextBox3
  6.     End With
  7. End Sub
复制代码
步骤3完整代码如下图1所示:

图1 用户窗体调用示例

图1 用户窗体调用示例

4. 创建全局的类型为 UserForm1 的用户窗体变量,并在公开过程内显示用户窗体*,例如:

  1. Public form1 As UserForm1

  2. Sub showform1()
  3.     Set form1 = New UserForm1
  4.     form1.Show 1
  5. End Sub
复制代码
5. 在日历窗体的消息处理函数中调用用户窗体的 DateSelected 过程(即步骤3.2中所创建的对外接口),如下图2红色方框中的代码所示:

图2 调用用户窗体的对外接口

图2 调用用户窗体的对外接口

6. 最后一步,编译当前 VBA 工程,保存工作簿文件。运行用户窗体,点击文本框,即弹出日历。



日历窗体和主窗体各部件之间的关系如下图3草图所示:

图3 各窗口之间的关系

图3 各窗口之间的关系

上图中,从信息传递的角度看,第①至第④步形成一个闭环。第①步箭头的起始点和第④步箭头的终点都在 TextBox1 上,相当于 TextBox1 获得了用户输入的日期信息。由于同一时间用户只能用鼠标点击一个控件,图中同一时间只能有一个闭环。例如当用户点击 TextBox2 时,第①步箭头的起始点和第④步箭头的终点都在 TextBox2 上,形成另一个闭环,TextBox1 和 TextBox3 都将不在这个新的闭环上。


最后,在使用中有任何问题、建议、意见或者疑问等,可随时留言评论。公历新年伊始,谨借此楼层和此版本祝大家在2024年身体健康,工作顺利!


___________________

脚注:

* 建议把用户窗体当作一个类模块来用。通常的做法都是直接 UserForm1.Show,但不建议这么做,这么做实际上是在调用VBE所提供的全局实例。更好的做法如上操作步骤中的第4步所示,先建立一个变量,变量类型为用户窗体模块名称,再 Set 变量 = New 用户窗体,即创建一个新的实例。这样做有一个极大的好处,就是可以把窗体模块和类模块统一按类来处理。

month_calendar_v1.0.4.zip

78.63 KB, 下载次数: 104

TA的精华主题

TA的得分主题

发表于 2024-1-3 10:28 | 显示全部楼层
感谢分享。是个好东西

TA的精华主题

TA的得分主题

发表于 2024-1-3 10:41 | 显示全部楼层
[广告] Excel易用宝 - 提升Excel的操作效率 · Excel / WPS表格插件       ★免费下载 ★       ★ 使用帮助
  感谢分享

TA的精华主题

TA的得分主题

发表于 2024-1-4 13:47 | 显示全部楼层
[广告] Excel易用宝 - 提升Excel的操作效率 · Excel / WPS表格插件       ★免费下载 ★       ★ 使用帮助
鼠标双击不能插入excel里了,老版本可以

TA的精华主题

TA的得分主题

 楼主| 发表于 2024-1-4 20:30 | 显示全部楼层
本帖最后由 prome3 于 2024-1-5 19:57 编辑

感谢楼上@qiu521119、@paulc、@linkhai_kkumi、@luikias、@838225419这几位的肯定和支持!

在1.0.4版本中,日历的隐藏方式由原先的自动隐藏改为手动隐藏。如果要自动隐藏,代码可以如下构建:

图1 点击后自动隐藏日历的代码构建示例

图1 点击后自动隐藏日历的代码构建示例

今天又做了一点点小小的修改和优化,代码请见附件。

MonthCalendar_v1.0.6.1.zip

4.49 KB, 下载次数: 65

优化版

TA的精华主题

TA的得分主题

 楼主| 发表于 2024-1-4 20:41 | 显示全部楼层
[广告] Excel易用宝 - 提升Excel的操作效率 · Excel / WPS表格插件       ★免费下载 ★       ★ 使用帮助
gchao15 发表于 2024-1-4 13:47
鼠标双击不能插入excel里了,老版本可以

“(1.0.4版本)鼠标双击(日历)不能把(日期)插入excel(工作表单元格)里了,老版本可以。”

是这个意思吧?

TA的精华主题

TA的得分主题

发表于 2024-1-5 14:46 | 显示全部楼层
[广告] VBA代码宝 - VBA编程加强工具 · VBA代码随查随用  · 内置多项VBA编程加强工具       ★ 免费下载 ★      ★使用手册
prome3 发表于 2024-1-4 20:41
“(1.0.4版本)鼠标双击(日历)不能把(日期)插入excel(工作表单元格)里了,老版本可以。”

是这 ...

现在是窗体和excel里不能同时插入日期, ActiveCell.MergeArea.Value = ds   ----excel里可用,form1.DateSelected ds---窗体里可用

TA的精华主题

TA的得分主题

 楼主| 发表于 2024-1-5 20:55 | 显示全部楼层
gchao15 发表于 2024-1-5 14:46
现在是窗体和excel里不能同时插入日期, ActiveCell.MergeArea.Value = ds   ----excel里可用,form1.Dat ...

这跟日历控件本身无关,而是用户怎么用这个日历控件的问题。
现在是窗体和excel里不能同时插入日期
“同时”插入是可以的,把示例代码 ActiveCell.MergeArea.Value = ds 这行取消注释就行。但这应该不是你所指的那种“同时”。


其实我在写那些示例代码的时候就知道会有人这么问,会提出这个问题。然而,在工作表和窗口同时插入日期的需求,在现实中应该很少,如果要做手动录入,比较好的做法是在窗口上录入,然后通过窗口往工作表写入数据。


如果非要同时插入,可以添加几行的代码,以区分新建的窗体是在工作表上还是窗体上:
日历显示于工作表还是窗口.png




致其他人:如果在用这款日历时不知道具体怎么调用,或者像25楼 @pao810 那样,需要我写示例代码,可以在此帖下留言评论;或者需要定制版嵌入自己的工程并且工程代码需要保密的话,可以加我QQ(或邮箱交流)1833407330@qq.com。


这款日历是我亲手一行一行代码写出来的,并非抄别人的,实属不易(虽然我并不知道论坛内之前已经有人做过了……有雷同,但纯属巧合!后面我会单独开一个楼层说明)。2024年1月3日那天晚上,为了优化1.0.4版本到上边发布的那个1.0.6版本,期间出现了一个BUG,调试了整整两个小时,期间Excel崩溃了N多次,就只为了一点——把这款日历做成兼容性、稳定性足够高的、可用的控件。我可太难了我-.-

TA的精华主题

TA的得分主题

 楼主| 发表于 2024-1-8 22:50 | 显示全部楼层
59楼所说的论坛内已有人做过同样的日历窗体,指的是版主 @stanleypan 发表于 2005-8-21 22:56 的帖子,链接如下:

纯API 构成的窗体及DTP.
https://club.excelhome.net/thread-117472-1-1.html
(出处: ExcelHome技术论坛)


他做的那款日历完全不需要窗体模块。这一点其实是我一开始想实现的,只是在做的时候发现用窗体模块能省很多事,而且窗体模块本身也能当成一个类模块来用,用来封装日历控件的各种属性信息。然后是他做的那款日历可以设置在同一个窗体内显示几个月的日历,可以更改日历各个部件的颜色,这些是我做的日历所没有的。

我特别想运行版主做的这款日历看看效果,奈何手头只有64位的机器。感兴趣的朋友可以运行他的日历控件看看效果。在此向这位前辈致敬!

接下来推荐一个日历窗体方面真正的神作!


这是由国外一位大神做的。最新版,也是最后一版,发布时间刚好在上面提到的这位版主发布时间5个月前(也就是同一年做的)。不同的是,国外这位前辈的最新版是第10个大更新版本,版本迭代了好多次(相比之下,我和版主的日历版本只有1个大版本)。此人名叫 Stephen Lebans,网址是

https://www.lebans.com/monthcalendar.htm

他做的日历是真正的稳定版,除了有版主日历有的功能外,还多一个设置日历控件字体的功能。代码共3000余行(包括注释、空行等)。其中 modCalendar 模块共1700余行,从第一行看到最后一行花了我将近半小时时间……不过好在大部分内容都是函数、变量、常量的声明,而且注释做得很好,读下来就一个感觉两个字:畅快。


可惜他的这款日历和版主的一样,年代久远,只支持32位环境。手头有 Microsoft Access 2003/2007 的朋友可以试一下(我不打算把他的代码改成支持64位的,因为那实在太简单了,单纯的体力活。喜欢他那款日历并且需要在64位环境下使用的朋友,可以自己转化一下。文件请见附件或于上述网址下载)。


说实话我是在独立做完自己的这款日历之后,才发现上面这两位前辈的作品的。就在前几天,发布1.0.4版本后,无聊中翻看了一下EH论坛的知识树,突然看到“纯API 构成的窗体及DTP”这个标题,心里一惊,点进去一看,好家伙,已经有人做过了。看完版主的代码后,又急匆匆找国外有没有也做过的。嘿,还真有!


现在看来,我的这款日历相比于两位前辈的作品的唯一优势就是支持64位了。我这款日历的版本更新就到此为止,后续若有坛友反馈只做讨论但不发布新版本,因为——这日历窗体本身不是

本帖的真正主题,

它只是一个例子。本帖的真正要分享和讨论的东西,后面会在单独的楼层解释。



最后,感谢诸位在各个楼层送的花,以及 蓝桥玄霜 版主送的技术分!

TA的精华主题

TA的得分主题

发表于 2024-1-8 23:34 | 显示全部楼层
您需要登录后才可以回帖 登录 | 免费注册

本版积分规则

手机版|关于我们|联系我们|ExcelHome

GMT+8, 2024-11-19 05:53 , Processed in 0.036194 second(s), 6 queries , Gzip On, MemCache On.

Powered by Discuz! X3.4

© 1999-2023 Wooffice Inc.

沪公网安备 31011702000001号 沪ICP备11019229号-2

本论坛言论纯属发表者个人意见,任何违反国家相关法律的言论,本站将协助国家相关部门追究发言者责任!     本站特聘法律顾问:李志群律师

快速回复 返回顶部 返回列表