Tặng tiện ích CALENDAR tuyệt đẹp!

Liên hệ QC

Hoàng Trọng Nghĩa

Chuyên gia GPE
Thành viên BQT
Moderator
Tham gia
17/8/08
Bài viết
8,616
Được thích
16,675
Giới tính
Nam
***************************************************************************************************************
***************************************************************************************************************

Đã có phiên bản mới tại đây:

Tặng tiện ích CALENDAR (Excel 2007 trở về sau)


***************************************************************************************************************
***************************************************************************************************************




Nhân dịp khoác trên vai “4 sao vàng”, tôi xin tặng các bạn một UserForm Calendar tuyệt đẹp, nó không những thay thế được với Control Calendar của Excel mà nó còn hiển thị ngày Âm lịch.

(Giới thiệu trước, gửi file ở bài sau)
3.jpg

Mặc dù mã nguồn tôi đã sưu tầm từ nhiều nơi (thật sự tôi không nhớ nguồn gốc của các mã này của ai sáng tác), nhưng tôi đã cải tiến cũng như thiết kế lại giao diện, kết hợp mã nguồn của dương lịch và mã nguồn chuyển Âm lịch, có đầy đủ “thiên can địa chi” cho năm.

Cũng như tại bài viết này tôi đã giới thiệu (http://www.giaiphapexcel.com/forum/showthread.php?36542-Đặt-caption-cho-nhiều-Label&p=242247#post242247) thì cải tiến lần này hoàn chỉnh nhất, Calendar này sẽ nhớ ngày hiện hành (hôm nay) bằng cách tô màu hồng đậm. Dùng phím mũi tên (lên, xuống, trái, phải) để di chuyển giữa các ô ngày; mỗi ô ngày được chọn sẽ có nền trắng, viền ngoài để phân biệt với ngày hiện hành và các ngày trong tháng.

Các bạn để ý sẽ thấy, khi ô ngày nào được chọn, thì Label ở dưới cùng thể hiện ngày Dương lịch được chọn bên trái và ngày Âm lịch được chọn bên phải, chúng có màu nền, cũng như màu font chữ của ô ngày hiện hành.

Cũng tại Label này, khi bạn đang chọn ngày khác với ngày hiện hành, thì bạn click vào đó nó sẽ chọn về ngày hôm nay.

2.jpg

Nếu bạn rê chuột ngang qua nó, nó sẽ show cho bạn một ToolTip để báo bạn biết chức năng của nó.

Đặc biệt, lần cải tiến này tôi đã thay đổi 2 Label tháng và năm thành 2 ComboBox THÁNG & NĂM để chúng ta có thể di chuyển ngay tới tháng hoặc năm cần xem.

1.jpg

– Chọn tháng –

4.jpg

– Chọn năm –

5.jpg

Các thao tác trên lịch:

  • Di chuyển giữa các ô ngày bằng các phím mũi tên để di chuyển qua lại, lên xuống.
  • Dùng phím Tab để di chuyển ngày kế tiếp, shift + tab để di chuyển ngược lại.
  • PgUp, PgDn để chọn tháng trước, tháng sau (tương đương với bấm vào 2 CommandButton mũi tên qua, lại sát ComboBox Tháng, cũng tương đương Shift + các phím mũi tên).
  • Shift+ PgUp/ PgDn để chọn năm trước, năm sau (tương đương với bấm vào 2 CommandButton mũi tên qua, lại sát ComboBox Năm).
  • Phím Home để trở về ngày hiện hành (ngày hôm nay).

Các bạn cứ bấm thử với Shift hoặc Ctrl kết hợp với các phím trên sẽ nắm rõ nguyên lý hoạt động của lịch.

Với phím Enter, Esc hoặc click vào ô ngày nào đó sẽ thoát lịch.

Nếu lịch được khởi động trên một UserForm và muốn nhận giá trị ngày từ Calendar vào một TextBox trên form này, thì sau khi thoát Lịch, giá trị lịch tại ô ngày nào được chọn sẽ nhập vào TextBox của UserForm đó.

Năm nào có tháng nhuần thì nó thể hiện chữ (N) trên Calendar.

6.jpg
Khi gọi Calendar từ một UserForm, nếu TextBox cần nhập Date có sẳn ngày tháng, lịch sẽ lấy ngày đó làm ngày hiển thị, ngược lại, lịch sẽ hiển thị ngày hiện hành.

7.jpg
 
Lần chỉnh sửa cuối:
Cái mà tôi quan tâm nhất trong topic này là: Liệu có thể tạo được 1 cái Calendar hoạt động gần giống như Calendar Control của Excel không?
Hoạt động gần giống nghĩa là:
- Khi click vào 1 ngày nào đó, tôi muốn giá trị ngày ấy gán vào đâu (trên cell, trên textbox hay bất kỳ đâu) thì nó sẽ gán vào đó
- Khi tôi muốn Calendar Show tại vị trí nào thì nó sẽ show đúng vị trí đó

- Code phải được Save thành 1 AddIn để dùng bất cứ khi nào tôi cần
vân vân...
Để làm chi? Để trong trường hợp tôi không thể cài được MSCAL.OCX (Office 64 không xài được) thì ít nhất tôi cũng có cái để dùng
----------------
Như vậy đấy! Nhưng xem ra thì cuối cùng cái Calendar này chỉ để... nhìn, mức độ ứng dụng chưa cao

2 cái hàng tô đậm thì đã làm được rồi mà Thầy? Riêng hàng thứ 2 thì trong form muốn nó ở vị trí nào thì cũng đã làm được ở chỗ đó, còn trên cell thì cái hàm mà Thầy và Thầy Siwtom hướng dẫn em vừa qua (CellPositon) đã giải quyết được đó thôi.

Riêng save thành addins thì để code hoàn thiện thì mình xây dựng cũng không muộn.

Việc thay thế cái thư viện MSCAL.OCX này e ra không thể, vì hình như trong các lịch nó lấy từ thư viện này kể cả lịch tự tạo cũng lấy từ lịch hệ thống, không biết em nói vậy đúng không?
 
Upvote 0
Riêng save thành addins thì để code hoàn thiện thì mình xây dựng cũng không muộn.

Không dễ ăn để làm điều này đâu. Thử sẽ biết
Đúng trên cương vị người dùng, tôi đâu cần biết code viết gì, cũng không quan tâm sẽ sửa code thế nào, miễn sao tôi có được 1 lệnh tổng quát để điều khiển calendar là được rồi
 
Upvote 0
Không dễ ăn để làm điều này đâu. Thử sẽ biết
Đúng trên cương vị người dùng, tôi đâu cần biết code viết gì, cũng không quan tâm sẽ sửa code thế nào, miễn sao tôi có được 1 lệnh tổng quát để điều khiển calendar là được rồi

Em nghĩ chuyện code của form như thế nào thì người dùng chẳng cần bận tâm.

Vậy thì làm một Addins gán macro gọi lịch vào một menu, trên cell thì mình dùng thủ tục selection.value=calendar

Không biết ý Thầy nói khó ở đâu?
 
Upvote 0
Em kết hợp hàm CellPosition (của Thầy được cải tiến bởi Thầy siwtom), làm một Addins thực hiện trên Cell Menu như sau:

Trên Thisworkbook Module ta đặt 2 sự kiện:

[GPECODE=vb]Option Explicit

Private Sub Workbook_Open()
With Application.CommandBars("Cell")
On Error GoTo MenuLich
If Not .Controls("Calendar") Is Nothing Then Exit Sub
MenuLich:
.Controls("Cut").BeginGroup = True
.Controls.Add(1, , , 1).Caption = "Calendar"
With .Controls("Calendar")
.Style = 3
.FaceId = 59
.BeginGroup = True
.OnAction = "CalShow"
End With
End With
End Sub


Private Sub Workbook_BeforeClose(Cancel As Boolean)
On Error Resume Next
Application.CommandBars("Cell").Controls("Calendar").Delete
End Sub
[/GPECODE]

Và thủ tục chạy sự kiện đó như sau:

[GPECODE=vb]

Sub CalShow()
With Selection
If .Rows.Count > 1 Then
Dim Msg As Integer, MsgText As String
MsgText = "B" & ChrW(7841) & "n " & ChrW(273) & "ang ch" & ChrW(7885) & _
"n trên nhi" & ChrW(7873) & "u Cell, b" & ChrW(7841) & _
"n mu" & ChrW(7889) & "n nh" & ChrW(7853) & _
"p ngày tháng nh" & ChrW(432) & " th" & ChrW(7871) & " nào?" & _
String(2, vbLf) & "- " & ChrW(272) & ChrW(7875) & " nh" & ChrW(7853) & _
"p trên t" & ChrW(7845) & "t c" & ChrW(7843) & " các cell, ch" & ChrW(7885) & "n YES" & _
String(2, vbLf) & "- Ch" & ChrW(7881) & " nh" & ChrW(7853) & "p t" _
& ChrW(7841) & "i ActiveCell (ô hi" & ChrW(7879) & "n hành), ch" & ChrW(7885) & "n NO."

Msg = Application.Assistant.DoAlert("THÔNG BÁO", MsgText, msoAlertButtonYesNo, _
msoAlertIconQuery, msoAlertDefaultSecond, msoAlertCancelDefault, False)
If Msg = vbNo Then
ActiveCell.Value = DatePicked(.Value)
Exit Sub
End If
End If
.Value = DatePicked(.Value)
End With
End Sub
[/GPECODE]

Với thủ tục CalShow, nếu vùng được chọn là 1 ô thì không nói gì, nhưng nếu là nhiều ô sẽ có một thông báo trước khi Lịch được hiện ra, hỏi bạn chọn nhập ngày tháng tại ô hiện hành hay cả khối ô đã chọn.

Và hàm DatePicked dưới đây cũng chỉnh lại chút cho phù hợp với lịch trên cell:

[GPECODE=vb]Function DatePicked(Optional varPassedDate As Variant) As Variant
On Error Resume Next
gvarStartDate = IIf(IsMissing(varPassedDate), Date, varPassedDate)
If Not IsDate(gvarStartDate) Then gvarStartDate = Date
Call ShowForm(UsfCalendar, ActiveCell)
DatePicked = gvarSelectedDate
End Function
[/GPECODE]

Bây giờ người dùng có thể chép vào thư mục Addin để chạy calendar thôi.

==============================================

ĐÃ UPDATE THÀNH ADDINS VỚI CÁC THỦ TỤC ĐƠN GIẢN.

TẢI BÀI MỚI TẠI ĐÂY (BÀI #63) - CLICK VÀO ĐÂY
 

File đính kèm

  • CldrAddins.rar
    67.7 KB · Đọc: 116
Lần chỉnh sửa cuối:
Upvote 0
Em kết hợp hàm CellPosition (của Thầy được cải tiến bởi Thầy siwtom), làm một Addins thực hiện trên Cell Menu như sau:
.

Nếu tôi không muốn cái Popup mà muốn dùng khi có sự kiện SelectionChange thì sao?
Thật ra Nghĩa chưa hiểu ý tôi! Cái tôi muốn là 1 công cụ chưa được "lắp ráp" sẵn, khi người dùng muốn xài sao thì tự họ "lắp ráp"
Ví dụ:
- Giả định rằng tôi viết được 1 Sub có tham số truyền theo dạng ShowCalendar(Left, Top)
- Giả định AddIn đã được gọi lên
- Giờ nếu tôi muốn hiện Calendar tại vị trí cell A1 thì tôi (người dùng) sẽ viết ShowCalendar(Range("A1").Left, Range("A1").Top)
Đại khái thế chứ không phải "lắp ráp hoàn chỉnh"... vì như code AddIn này, tôi muốn nó hiện tại UserForm của tôi thì làm thế nào?
 
Upvote 0
Nếu tôi không muốn cái Popup mà muốn dùng khi có sự kiện SelectionChange thì sao?
Thật ra Nghĩa chưa hiểu ý tôi! Cái tôi muốn là 1 công cụ chưa được "lắp ráp" sẵn, khi người dùng muốn xài sao thì tự họ "lắp ráp"
Ví dụ:
- Giả định rằng tôi viết được 1 Sub có tham số truyền theo dạng ShowCalendar(Left, Top)
- Giả định AddIn đã được gọi lên
- Giờ nếu tôi muốn hiện Calendar tại vị trí cell A1 thì tôi (người dùng) sẽ viết ShowCalendar(Range("A1").Left, Range("A1").Top)
Đại khái thế chứ không phải "lắp ráp hoàn chỉnh"... vì như code AddIn này, tôi muốn nó hiện tại UserForm của tôi thì làm thế nào?

Thầy thử với cái này xem:

Tại File Addins, trong Module Thầy đặt thủ tục sau:

[GPECODE=vb]Sub CalendarShow()
UsfCalendar.Show
End Sub
[/GPECODE]

Bây giờ tại file người dùng Thầy đặt trong Module thủ tục sau:

[GPECODE=vb]Sub CallForm()
Run "'FileName.xla'!CalendarShow"
End Sub
[/GPECODE]

Như vậy, với đẳng cấp của Thầy, thì thầy biết phải đặt vị trí form này ở đâu và làm gì ở Sub CalendarShow rồi phải không?
 
Upvote 0
Em tải cái file đầu tiên, khi em chọn một mục ở combobox Tháng, rồi enter, ngay lập tức nó chuyển focus sang combobox Năm, xổ list của combobox này ra, nhưng hoàn toàn không có việc thay đổi ngày tháng năm theo những tháng, năm em chọn.

Và em đã tải 3 lần, cả 3 lần code đều khác nhau, có thể Thầy đã update trong mỗi phiên bản.

Thế thì lạ thật. Tôi gửi tập tin RAR chỉ 1 lần, và lần cuối vì thêm xử lý việc gõ sai trong ComboBox.

Mà đã nói ứng dụng thay thế, nếu nó không bằng thằng control calendar, thì ai mà xài? Phải có điểm hơn mới thể dùng được.

Các điểm hơn mà chúng ta phải tạo:
Nếu bạn đọc bài đầu tiên của tôi trong chủ để này thì bạn thấy cái mà tôi muốn giới thiệu chính là CLASS. Bạn viết nhiều bài, tại sao tôi lại trả lời đúng bài "ấy"?
Bạn có mục đích của mình là làm một Calendar, vậy bạn muốn nó có nhiều chức năng, thân thiện, giao diện đẹp. Mục đích của tôi là giới thiệu về CLASS, về code, tôi viết hoàn chỉnh code để cho có đầu có đuôi mà thôi. Vì phải "gắn" cái class ấy thế nào, vào đâu, xử lý click thế nào, vậy phải có calendar để test. Thế thôi. Cái tôi quan tâm là code, vậy tôi muốn biết code như thế đã chuẩn chưa, click, bấm phím vào "kia" có gì sẩy ra không. Tôi không quan tâm về chức năng, về giao diện vì tôi có ý làm Calendar đâu. Mà bạn đã có Calendar với chức năng từ A đến Z thì bạn cần 1 Calendar thứ 2 giống hệt về chức năng để làm gì???
Cái mà tôi muốn giỡi thiệu là class. Nhưng nếu bạn thấy cách code "chỗ này" là hay, "chỗ kia" có kỹ thuật mình chưa biết hay mẹo gì đó thì tôi mừng rồi. Vì mục đích của tôi chỉ có thế. Calendar chỉ là cái cớ để giới thiệu về code chứ không phải mục đích của tôi.
 
Upvote 0
Thế thì lạ thật. Tôi gửi tập tin RAR chỉ 1 lần, và lần cuối vì thêm xử lý việc gõ sai trong ComboBox.


Nếu bạn đọc bài đầu tiên của tôi trong chủ để này thì bạn thấy cái mà tôi muốn giới thiệu chính là CLASS. Bạn viết nhiều bài, tại sao tôi lại trả lời đúng bài "ấy"?
Bạn có mục đích của mình là làm một Calendar, vậy bạn muốn nó có nhiều chức năng, thân thiện, giao diện đẹp. Mục đích của tôi là giới thiệu về CLASS, về code, tôi viết hoàn chỉnh code để cho có đầu có đuôi mà thôi. Vì phải "gắn" cái class ấy thế nào, vào đâu, xử lý click thế nào, vậy phải có calendar để test. Thế thôi. Cái tôi quan tâm là code, vậy tôi muốn biết code như thế đã chuẩn chưa, click, bấm phím vào "kia" có gì sẩy ra không. Tôi không quan tâm về chức năng, về giao diện vì tôi có ý làm Calendar đâu. Mà bạn đã có Calendar với chức năng từ A đến Z thì bạn cần 1 Calendar thứ 2 giống hệt về chức năng để làm gì???
Cái mà tôi muốn giỡi thiệu là class. Nhưng nếu bạn thấy cách code "chỗ này" là hay, "chỗ kia" có kỹ thuật mình chưa biết hay mẹo gì đó thì tôi mừng rồi. Vì mục đích của tôi chỉ có thế. Calendar chỉ là cái cớ để giới thiệu về code chứ không phải mục đích của tôi.

Thì em đã nói là em được học thêm Thầy về Class, em đâu có nói gì đến code của Thầy đâu, mà bản thân em chưa biết tí gì về Class hết sao biết đúng hay sai trong code, nên chỉ thấy Lịch nó thể hiện thế nào thì nói thế ấy thôi ạ.

À, mà em cũng không biết Thầy nói hiện tiếng Việt nữa vời là sao nữa, trong VBE em gõ chữ "vô, ngày..." được, em vẫn thấy nó hiển thị được, nhưng không hiểu sao cái hình của Thầy nó lại không ra chữ "vô, ngày..."?

Em có một thủ thuật để gõ như vậy là em quay macro tại WORD em gõ gì vào đó thì macro nó tự mã hóa dấu tiếng Việt cho em, em chỉ ôm nó qua Excel thôi, không chỉnh sửa thêm gì hết!

[GPECODE=vb]Sub Macro1()
'
' Macro1 Macro
' Macro recorded 28/12/2012 by NRKH
'
Selection.TypeText Text:="Kính th" & ChrW(432) & "a Th" & ChrW(7847) & _
"y siwtom, em r" & ChrW(7845) & "t bi" & ChrW(7871) & "t " & ChrW(417) & _
"n Th" & ChrW(7847) & "y!"
Selection.TypeParagraph
End Sub
[/GPECODE]

[GPECODE=vb]Sub Macro2()
'
' Macro2 Macro
' Macro recorded 28/12/2012 by NRKH
'
Selection.TypeText Text:="B" & ChrW(7845) & "m vô " & ChrW(273) & "ây " & _
ChrW(273) & ChrW(7875) & " ch" & ChrW(7885) & "n ngày hôm nay"
End Sub
[/GPECODE]
 
Lần chỉnh sửa cuối:
Upvote 0
Thầy thử với cái này xem:

Tại File Addins, trong Module Thầy đặt thủ tục sau:

[GPECODE=vb]Sub CalendarShow()
UsfCalendar.Show
End Sub
[/GPECODE]

Bây giờ tại file người dùng Thầy đặt trong Module thủ tục sau:

[GPECODE=vb]Sub CallForm()
Run "'FileName.xla'!CalendarShow"
End Sub
[/GPECODE]

Như vậy, với đẳng cấp của Thầy, thì thầy biết phải đặt vị trí form này ở đâu và làm gì ở Sub CalendarShow rồi phải không?
Mình nói mà hình như Nghĩa đếch có hiểu ý mình gì cả (hoặc là văn chương của mình có vấn đề)
Thôi, khỏi góp ý luôn chon rồi
Ẹc... Ẹc...
 
Upvote 0
Mình yếu nên không hiểu ndu và Nghĩa - "hai thầy trò" - thảo luận với nhau cái gì, tại sao lại phải tìm cách đặt nó ở ô A1, A2 ....

Nếu cái lịch này hoàn chỉnh, mình chỉ muốn tạo một lệnh ở đâu đó trên menu nào đó để thỉnh thoảng gọi nó show để tra ngày, đồng thời "ủng hộ" và giới thiệu sản phẩm của GPE.

Cái được nhất ở đây là mình học được giải thuật, thuật toán và class ... từ những cao thủ, đặc biệt là anh siwtom.
 
Lần chỉnh sửa cuối:
Upvote 0
Mình nói mà hình như Nghĩa đếch có hiểu ý mình gì cả (hoặc là văn chương của mình có vấn đề)
Thôi, khỏi góp ý luôn chon rồi
Ẹc... Ẹc...

1) Ban đầu, Thầy nói người dùng chẳng cần biết gì về code, chỉ biết Addins rồi dùng. OK, em đã làm

2) Thầy lại nói sao gọi được cái Calendar từ Addins (tức người dùng đã biết VBA). OK, thủ tục để hiện cái Form em cũng đã đưa lên.

??? Tại sao Thầy lại nói vậy?

Nếu Thầy có hướng đi khác thì Thầy chia sẽ, chứ trình độ của em chỉ được vậy.
 
Lần chỉnh sửa cuối:
Upvote 0
Mình yếu nên không hiểu ndu và Nghĩa - "hai thầy trò" - thảo luận với nhau cái gì, tại sao lại phải tìm cách đặt nó ở ô A1, A2 ....

Là tìm cách sao cho "nó" hoạt động gần giống như Calendar Control mà ta vẫn dùng trên Excel đấy anh
 
Upvote 0
Là tìm cách sao cho "nó" hoạt động gần giống như Calendar Control mà ta vẫn dùng trên Excel đấy anh

Vậy mình nghĩ mục đích của Nghĩa cũng như mình, coi lịch chỉ như một tiện ích. Còn ndu thì nâng lên tầm cao hơn, làm cho lịch trở thành một control, nếu vậy chỉ có ndu trở lên mới có thể làm được. Vậy mà không hiểu nhau, thôi đừng nóng nữa thày ạ!
 
Lần chỉnh sửa cuối:
Upvote 0
Vậy mình nghĩ mục đích của Nghĩa cũng như mình, coi lịch chỉ như một tiện ích. Còn ndu thì nâng lên tầm cao hơn, làm cho lịch trở thành một control, nếu vậy chỉ có ndu trở lên mới có thể làm được. Vậy mà không hiểu nhau, thôi đừng nóng nữa thày ạ!

Thú thật là em khá sốt ruột. Chừng 1 năm nữa thôi, nếu đổi máy tính mới thì bắt buộc phải cài Win 64.
Mà theo em được biết thì 2 thằng em Calendar Control + DTPicker không chạy được trên hệ thống 64 bit ---> Lúc đó phải làm sao nếu có nhu cầu sử dụng?
Vậy nên em rất muốn tạo 1 Control cho riêng mình. Cái calendar ấy đẹp xấu không quan trọng... những giải thuật trong cũng chẳng có gì phải bàn cả, vấn đề là làm sao "múc" nó thành 1 dạng OCX?
Nghĩa làm không được thôi nhưng em nghĩ cở siwtom chắc sẽ làm được (còn em cũng đang nghiên cứu theo hướng này nhưng chưa được bao nhiêu)
 
Lần chỉnh sửa cuối:
Upvote 0
Thú thật là em khá sốt ruột. Chừng 1 năm nữa thôi, nếu đổi máy tính mới thì bắt buộc phải cài Win 64.
Mà theo em được biết thì 2 thằng em Calendar Control + DTPicker không chạy được trên hệ thống 64 bit ---> Lúc đó phải làm sao nếu có nhu cầu sử dụng?
Vậy nên em rất muốn tạo 1 Control cho riêng mình. Cái calendar ấy đẹp xấu không quan trọng... những giải thuật trong cũng chẳng có gì phải bàn cả, vấn đề là làm sao "múc" nó thành 1 dạng OCX?
Nghĩa làm không được thôi nhưng em nghĩ cở siwtom chắc sẽ làm được (còn em cũng đang nghiên cứu theo hướng này nhưng chưa được bao nhiêu)

Sao lại siwtom?
Các bạn thử tìm phần mềm cho 64 bit.
Lập trình cho 64 bit thì cũng phải học và có công cụ chứ lập trình bằng gì?
Tôi có phải người lập trình chuyên nghiệp đâu. Tới giờ tôi vẫn dùng XP 32 bit chứ chưa nói tới Win 7 hay 64 bit gì.
Nếu bạn muốn nhờ thì địa chỉ đúng là Nguyễn Duy Tuân. Tuân lập trình chuyên nghiệp, tạo sản phẩm, vậy Tuân có kiến thức và công cụ để tạo sản phẩm. Tuân bắt buộc phải học những công nghệ mới. Tôi chỉ lập trình chơi thôi nên tôi chỉ học những cái mình thích. Tôi không bắt buộc "phải" cái gì cả.
 
Upvote 0
Sao lại siwtom?
Các bạn thử tìm phần mềm cho 64 bit.
Lập trình cho 64 bit thì cũng phải học và có công cụ chứ lập trình bằng gì?
Tôi có phải người lập trình chuyên nghiệp đâu. Tới giờ tôi vẫn dùng XP 32 bit chứ chưa nói tới Win 7 hay 64 bit gì.
Nếu bạn muốn nhờ thì địa chỉ đúng là Nguyễn Duy Tuân. Tuân lập trình chuyên nghiệp, tạo sản phẩm, vậy Tuân có kiến thức và công cụ để tạo sản phẩm. Tuân bắt buộc phải học những công nghệ mới. Tôi chỉ lập trình chơi thôi nên tôi chỉ học những cái mình thích. Tôi không bắt buộc "phải" cái gì cả.

Ah... cái em quan tâm hồng phải là 32 hay 64... Em biết cách để làm cho code chạy được trên cả 32 hay 64 bit...
Nhưng trước mắt, cái mà em quan tâm là muốn hướng đến lập trình OCX hoặc DLL thôi
 
Lần chỉnh sửa cuối:
Upvote 0
Nếu tôi không muốn cái Popup mà muốn dùng khi có sự kiện SelectionChange thì sao?
Thật ra Nghĩa chưa hiểu ý tôi! Cái tôi muốn là 1 công cụ chưa được "lắp ráp" sẵn, khi người dùng muốn xài sao thì tự họ "lắp ráp"
Ví dụ:
- Giả định rằng tôi viết được 1 Sub có tham số truyền theo dạng ShowCalendar(Left, Top)
- Giả định AddIn đã được gọi lên
- Giờ nếu tôi muốn hiện Calendar tại vị trí cell A1 thì tôi (người dùng) sẽ viết ShowCalendar(Range("A1").Left, Range("A1").Top)
Đại khái thế chứ không phải "lắp ráp hoàn chỉnh"... vì như code AddIn này, tôi muốn nó hiện tại UserForm của tôi thì làm thế nào?

Không biết thủ tục dưới đây có đúng ý của Thầy không:

Tại file Addins mà em gửi lên, viết thêm thủ tục này:

[GPECODE=vb]Public Sub CalendarShow(rCell As Range)
On Error Resume Next
Call ShowForm(UsfCalendar, rCell)
End Sub
[/GPECODE]

Vì thủ tục trên lệ thuộc vào thủ tục này (Thầy đã viết):

[GPECODE=vb]Sub ShowForm(ByRef frm As Object, rCell As Range)
On Error Resume Next
Dim Arr As Variant
Arr = CellPosition(rCell)
With frm
.StartUpPosition = 0
.Left = Arr(1)
.Top = Arr(2)
.Show
End With
End Sub
[/GPECODE]

Giờ tại File người dùng chỉ việc thực hiện công đoạn gọi form lên thôi:

[GPECODE=vb]Sub GoiFormTuFileKhac()
Application.Run "'CldrAddins.xla'!CalendarShow", ActiveCell
End Sub
[/GPECODE]

Để nó trở thành một thư viện thì em không có thể rồi, còn như Thầy nói thì em đã làm được.
 
Upvote 0
Thí dụ với yêu cầu này:

Theo tôi hiểu thì Nghĩa muốn kéo thả label chẳng hạn như Sunday từ cuối lên đầu và ngược lại, để cho tuần bắt đầu từ chủ nhật thay vì thứ hai.

Tôi mới tải tập tin của Nghĩa ở bài #44 về và thử nhấn và kéo các Label ngày trong tuần thì hình như "kéo thả" không đúng như bạn miêu tả thì phải. "Kéo thả" ở đây chắc là cho phép user "kéo" UserForm sang chỗ khác"
-----------------
@Nghĩa:
Bạn thử nghiên cứu cách này xem - chỉ dùng MouseDown:
À, không biết hiện thời bạn có để ý không: khi bạn kéo Form, nhất là khi kéo nhanh, thì bạn nhìn thấy những "vệt" khung trung gian rất xấu.
Tôi cho rằng cách sau gọn hơn cách trước, và không để lại vệt.

1. Trong Initialize bỏ hForm ra ngoài thành biến của module UserForm

2. Code
[GPECODE=vb]
Private Declare Function ReleaseCapture Lib "user32.dll" () As Long
Private Declare Function SendMessage Lib "user32.dll" Alias "SendMessageA" (ByVal hwnd As Long, ByVal wMsg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long

Private Const WM_NCLBUTTONDOWN = &HA1
Private Const HTCAPTION = 2

...

Private Sub DoMove(ByVal Button As Integer)
If Button = 1 Then
ReleaseCapture
SendMessage hForm, WM_NCLBUTTONDOWN, HTCAPTION, 0
End If
End Sub

Private Sub lblDay1_MouseDown(ByVal Button As Integer, ByVal Shift As Integer, ByVal x As Single, ByVal y As Single)
DoMove Button
End Sub

Private Sub lblDay2_MouseDown(ByVal Button As Integer, ByVal Shift As Integer, ByVal x As Single, ByVal y As Single)
DoMove Button
End Sub

Private Sub lblDay3_MouseDown(ByVal Button As Integer, ByVal Shift As Integer, ByVal x As Single, ByVal y As Single)
DoMove Button
End Sub

Private Sub lblDay4_MouseDown(ByVal Button As Integer, ByVal Shift As Integer, ByVal x As Single, ByVal y As Single)
DoMove Button
End Sub

Private Sub lblDay5_MouseDown(ByVal Button As Integer, ByVal Shift As Integer, ByVal x As Single, ByVal y As Single)
DoMove Button
End Sub

Private Sub lblDay6_MouseDown(ByVal Button As Integer, ByVal Shift As Integer, ByVal x As Single, ByVal y As Single)
DoMove Button
End Sub

Private Sub lblDay7_MouseDown(ByVal Button As Integer, ByVal Shift As Integer, ByVal x As Single, ByVal y As Single)
DoMove Button
End Sub
[/GPECODE]

Nhưng nói cho cùng thì user chỉ cần có khả năng "kéo thả" vậy chả lý gì dùng cả 7 Label cho việc này. Code chỉ vì thế mà rườm rà mà thôi. Vậy chỉ để

[GPECODE=vb]
Private Sub lblDay1_MouseDown(ByVal Button As Integer, ByVal Shift As Integer, ByVal x As Single, ByVal y As Single)
If Button = 1 Then
ReleaseCapture
SendMessage hForm, WM_NCLBUTTONDOWN, HTCAPTION, 0
End If
End Sub
[/GPECODE]

còn lại xóa hết
--------------------------------------
Nói nôm na thì khi bạn nhấn chuột trái trên thanh tiêu đề và giữ thì bạn có thể kéo Form. Khi bạn nhấn chuột trái trên thanh tiêu đề thì thông điệp WM_NCLBUTTONDOWN được gửi tới hàm cửa sổ. Nhưng thông điệp WM_NCLBUTTONDOWN cũng được gửi trong những trường hợp khác vd. khi bạn nhấn vào khung (các cạnh) cửa sổ. Nói chung WM_NCLBUTTONDOWN (tổng quát là các WM_NC***) liên quan tới
"NonClient area" - ý nghĩa của NC là thế.
Vậy làm sao biết được khi nào user nhấn vào tiêu đề và khi nào nhấn vào chỗ khác? Thông điệp trong Windows luôn có 2 thông số đi kèm - bạn cứ nhìn các thông số của hàm SendMessage thì thấy. Nếu thông điệp cần "làm rõ thêm" thì Windows sẽ thiết lập 1 hoặc cả 2 giá trị wParam và lParam. Vd. với WM_NCLBUTTONDOWN và wParam = HTCAPTION thì là user nhấn thanh tiêu đề. Còn nếu wParam = HTLEFT, HTTOP, HTRIGHT, HTBOTTOM thì có nghĩa là user nhấn chuột trái ở "gờ trái", "gờ trên", "gờ phải", "gờ dưới" của cửa sổ. Tất nhiên tôi không liệt kê hết các giá trị của wParam. Bạn có thể đọc trong help về các thông điệp WM_NC***
Vậy ta tự gửi tới cửa sổ thông điệp WM_NCLBUTTONDOWN với wParam = HTCAPTION. Đơn giản quá phải không?
 
Lần chỉnh sửa cuối:
Upvote 0
Để ý cái calender mà ta vẫn dùng trên Excel (MSCAL.OCX) thì khi ta nhấn chuột vào 1 ngày nào đó, cái nút có vẻ như bị lõm xuống (như thế nhìn mới đẹp)
Để làm điều này cũng không có gì khó. Tôi làm trên 1 file mới, nghĩa mang về tham khảo code nhé (tôi không đủ kiên nhẫn để sửa trên file của Nghĩa)
 

File đính kèm

  • Text_Calendar.rar
    22 KB · Đọc: 132
Upvote 0
Để ý cái calender mà ta vẫn dùng trên Excel (MSCAL.OCX) thì khi ta nhấn chuột vào 1 ngày nào đó, cái nút có vẻ như bị lõm xuống (như thế nhìn mới đẹp)
Để làm điều này cũng không có gì khó. Tôi làm trên 1 file mới, nghĩa mang về tham khảo code nhé (tôi không đủ kiên nhẫn để sửa trên file của Nghĩa)

Thầy thay 2 thủ tục này: FixDaysInMonth HandleIndent ở file AddIns trước thành:

Mã:
Private Sub FixDaysInMonth(intStartDay As Integer)
      Dim intRow As Integer, intCol As Integer, intNumDays As Integer, _
      intCount As Integer, strTemp As String, strTemp1 As String

      intNumDays = DaysInMonth(iMonth)
      If Day > intNumDays Then
            Day = intNumDays
      End If

      intCount = 0
      For intRow = 1 To 6
            For intCol = 1 To 7
                  If (intRow = 1) And (intCol < intStartDay) Then
                        Me("lbl1" & intCol).Visible = False
                        Me("AL1" & intCol).Visible = False
                  Else
                        intCount = intCount + 1
                        strTemp = "lbl" & intRow & intCol
                        strTemp1 = "AL" & intRow & intCol
                        With Me(strTemp)
                              If intCount <= intNumDays Then
                                    If Not .Visible Then
                                          .Visible = True
                                          If .Visible = True Then Me(strTemp1).Visible = True
                                    End If
                                    .Caption = intCount
                                    
                                   [COLOR=#ff0000] If .SpecialEffect <> fmSpecialEffectRaised Then .SpecialEffect = fmSpecialEffectRaised[/COLOR]
                                    
                                    Me(strTemp1).Caption = NgayAL(.Caption & "/" & iMonth & "/" & iYear)
                              Else
                                    If .Visible Then
                                          .Visible = False
                                          If .Visible = False Then Me(strTemp1).Visible = False
                                    End If
                              End If
                        End With
                        
                              Dim a As String, b As String, c As String, d As String
                              a = "lbl" & intRow & 1: b = "lbl" & intRow & 7
                              c = "AL" & intRow & 1: d = "AL" & intRow & 7
                              
                        If Me(strTemp) = mintDayToday And iMonth = mintMonthToday And iYear = mintYearToday Then
                              Me(strTemp).BackColor = ClrMagenta
                              Me(strTemp).ForeColor = ClrWhite
                              Me(strTemp1).ForeColor = ClrLightYellow
                        Else
                              If Me(strTemp).BackColor <> ClrLightCyan Then Me(strTemp).BackColor = ClrLightCyan
                              If Me(strTemp).ForeColor <> ClrDarkBlue Then Me(strTemp).ForeColor = ClrDarkBlue
                              If Me(strTemp1).ForeColor <> ClrLightBlue Then Me(strTemp1).ForeColor = ClrLightBlue
                              
                              With Me(a)
                                    If .Caption = mintDayToday And iMonth = mintMonthToday And iYear = mintYearToday Then
                                          .BackColor = ClrMagenta
                                          .ForeColor = ClrWhite
                                           Me(c).ForeColor = ClrLightYellow
                                    Else
                                          If .BackColor <> ClrPink Then .BackColor = ClrPink
                                          If .ForeColor <> ClrRed Then .ForeColor = ClrRed
                                          If Me(c).ForeColor <> ClrLightRed Then Me(c).ForeColor = ClrLightRed
                                    End If
                              End With
                              
                              With Me(b)
                                    If .Caption = mintDayToday And iMonth = mintMonthToday And iYear = mintYearToday Then
                                          .BackColor = ClrMagenta
                                          .ForeColor = ClrWhite
                                           Me(d).ForeColor = ClrLightYellow
                                    Else
                                          If .BackColor <> ClrLightGreen Then .BackColor = ClrLightGreen
                                          If .ForeColor <> ClrDarkGreen Then .ForeColor = ClrDarkGreen
                                          If Me(d).ForeColor <> ClrGreen Then Me(d).ForeColor = ClrGreen
                                    End If
                              End With
                        End If
                  End If
            Next intCol
      Next intRow
End Sub

Và:

Mã:
Sub HandleIndent(strNewSelect As String)
      If ErrHdle = 0 Then
HdleIdnt:
            If Len(mstrSelected) > 0 Then
                  With Me(mstrSelected)
                        [COLOR=#ff0000].SpecialEffect = fmSpecialEffectRaised[/COLOR]
                        .Font.Size = 11
                        .Font.Bold = False
                        .BackStyle = 1
                  End With
                  Me(Replace(mstrSelected, "lbl", "AL")).Font.Bold = False
            End If
            
            mstrSelected = strNewSelect
            
            Me(Replace(mstrSelected, "lbl", "AL")).Font.Bold = True
            
            With Me(mstrSelected)
                  .Font.Bold = True
                  .Font.Size = 12
                 [COLOR=#ff0000] .SpecialEffect = fmSpecialEffectSunken[/COLOR]
                  
                  Day = .Caption
            End With
            
            With LbSolar
                  .Caption = DayInWeek(DateSerial(iYear, iMonth, Day))
                  .BackColor = Me(mstrSelected).BackColor
                  .ForeColor = Me(mstrSelected).ForeColor
            End With
            
            With LbLunar
                  .Caption = AmLich(DateSerial(iYear, iMonth, Day))
                  .BackColor = Me(mstrSelected).BackColor
                  .ForeColor = Me(Replace(mstrSelected, "lbl", "AL")).ForeColor
            End With
      Else
            LbSolar_Click
            GoTo HdleIdnt
      End If
End Sub

Riêng với các controls trên Form, Thầy không cần chỉnh sửa thuộc tính gì thêm nữa!
 
Lần chỉnh sửa cuối:
Upvote 0
Web KT
Back
Top Bottom