Làm nổi bật dòng và cột-phần 2

tuhocvba

Administrator
Thành viên BQT
Đã có . Nhưng có lẽ mọi người chưa thỏa mãn, nên tôi lập ra topic này.
Xin chú ý, sản phẩm này không phải do chúng tôi sáng tác ra, chỉ đơn thuần là chuyển ngữ từ tiếng nhật sang tiếng việt, bao gồm cả comment trong code. Vì vậy, chúng tôi rất vui nếu nhận được lý giải logic code trong topic này của các bạn. Các bạn hiểu được đến đâu, xin hãy chia sẻ đến đấy. Sau đó chúng tôi sẽ tổng hợp lại.

Trong phiên bản này, Tool sẽ tạo ra file INI và lưu thông tin vào đây. Tôi không có file data lớn để thử độ mượt, liệu nó có gây cản trở cho công việc của các bạn hay không, vì vậy, hãy phản hồi cho chúng tôi biết.

1. Link download:

2. Video thuyết minh:
Bạn cần đăng nhập để thấy đa phương tiện

Bây giờ đã là gần 5h sáng (GMT+9), rất mệt. Hi vọng làm hài lòng các bạn, đặc biệt là bạn @thaipv .

3. Mật khẩu macro: tuhocvba.net
 
M

maiban2068

Guest
-Điểm chính của chương trình: Sử dụng Line (shape) để dóng, thay vì tô màu vào Cells.
Ưu điểm: Sẽ không làm thay đổi định dạng vốn có của bảng tính.
Cụ thể logic thì cần thời gian tôi sẽ tìm hiểu kỹ hơn. Trước đây ông D.Tuân cũng cho ra lò cái như này, dùng shape, và nói mình là tác giả. Thật là chẳng hiểu nổi.
 
T

thanhphong

Guest
Diễn đàn ưu ái với bạn @phamthach quá.
Như vậy chương trình sử dụng 4 đường kẻ để thực hiện dóng hàng dóng cột.
Class EventClassModule sẽ làm việc này với ứng dụng Excel, làm việc trên Activesheet.
Mã:
If drawSideLine = "1" Then 'Nếu thiết định đường kẻ ngang là True'
        Set line1 = ActiveSheet.Shapes.AddLine(0, Target.Top, lineLength, Target.Top)
        Call setCondition(line1, LINE1_NAME, lineColor, lineWeight)
        Set line2 = ActiveSheet.Shapes.AddLine(0, Target.Top + Target.Height, lineLength, Target.Top + Target.Height)
        Call setCondition(line2, LINE2_NAME, lineColor, lineWeight)
End If
If drawVerticalLine = "1" Then 'Nếu thiết định đường kẻ dọc là True'
        Set line3 = ActiveSheet.Shapes.AddLine(Target.Left, 0, Target.Left, lineLength)
        Call setCondition(line3, LINE3_NAME, lineColor, lineWeight)
        Set line4 = ActiveSheet.Shapes.AddLine(Target.Left + Target.Width, 0, Target.Left + Target.Width, lineLength)
        Call setCondition(line4, LINE4_NAME, lineColor, lineWeight)
End If
Trong đó: setCondition để thiết định màu, độ dày mỏng của đường kẻ.
Mã:
Private Sub setCondition(ByRef lineObj, ByVal name, ByVal color, ByVal weight)

    lineObj.name = name
    'lineObj.Line.ForeColor.SchemeColor = color
    lineObj.Line.ForeColor.RGB = ActiveWorkbook.Colors(color)
    lineObj.Line.weight = weight

End Sub
Tương tự như khi ta viết sự kiện select change cho một sheet của một file cụ thể.
Mã:
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
'Code'
End Sub
Để áp dụng cho tất cả các file và sheet đang active thì phải sử dụng class. Trong Class trên ta cũng thấy:
Mã:
Private Sub App_SheetSelectionChange(ByVal Sh As Object, ByVal Target As Range)
    Call drawLine(Target) 'Code tạo đường với cells đang được active = Target
End Sub
Hiểu được logic này thì mọi người có thể tự code được.
Các thông tin thiết định được lưu trong file INI.
Mã:
[SIGNLINE]
WEIGHT=2
LENGTH=2000
DRAWSIDELINE=1
DRAWVERTICALLINE=1
VALID=1
Code xử lý file INI trông khá phức tạp, thay vì INI, mọi người có thể sử dụng file txt. Dùng để kiểm tra tồn tại và tạo mới nếu cần thiết, cũng như đọc thông tin file txt này.
Bạn cần đăng nhập để thấy hình ảnh
 

vbano1

SMod
Thành viên BQT
Ngắn gọn, logic cho nút Play:
Bạn cần đăng nhập để thấy hình ảnh

drawLine thực hiện hai nhiệm vụ chính: Xóa Line cũ (nếu có) và tạo Line mới theo thông tin được set trong file INI.
 
B

bvtvba

Guest
deleteLine:
Trong Class name a có khai báo 4 hằng số, đây chính là tên của 4 đường kẻ dóng ngang và dóng dọc:
Mã:
Const LINE1_NAME As String = "line1_tuhocvba.net"
Const LINE2_NAME As String = "line2_tuhocvba.net"
Const LINE3_NAME As String = "line3_tuhocvba.net"
Const LINE4_NAME As String = "line4_tuhocvba.net"
Code xóa shape cũng không có gì phức tạp: Tìm tất cả các shape trong activesheet, nếu tên trùng với các hằng số vừa nêu thì thực hiện xóa.
Mã:
Public Sub deleteLine()

    Dim objShape
    For Each objShape In ActiveSheet.Shapes
        If objShape.name = LINE1_NAME Then
            ActiveSheet.Shapes(LINE1_NAME).Delete
            Exit For
        End If
    Next
    For Each objShape In ActiveSheet.Shapes
        If objShape.name = LINE2_NAME Then
            ActiveSheet.Shapes(LINE2_NAME).Delete
            Exit For
        End If
    Next
    For Each objShape In ActiveSheet.Shapes
        If objShape.name = LINE3_NAME Then
            ActiveSheet.Shapes(LINE3_NAME).Delete
            Exit For
        End If
    Next
    For Each objShape In ActiveSheet.Shapes
        If objShape.name = LINE4_NAME Then
            ActiveSheet.Shapes(LINE4_NAME).Delete
            Exit For
        End If
    Next

End Sub
Phần lấy thông tin INI bắt đầu khó hiểu:
blnGetInFileData => GetPrivateProfileString (API): Cần tìm hiểu thêm về hàm GetPrivateProfileString (API).
 

Euler

Administrator
Thành viên BQT
Hàm GetPrivateProfileString (API). đang được Euler biên dịch , mọi người cùng nhau lý giải các phần khác đi nhé.
Cảm ơn mọi người rất nhiều.
 

thaipv

Mod
Thành viên BQT
Đã có . Nhưng có lẽ mọi người chưa thỏa mãn, nên tôi lập ra topic này.
Xin chú ý, sản phẩm này không phải do chúng tôi sáng tác ra, chỉ đơn thuần là chuyển ngữ từ tiếng nhật sang tiếng việt, bao gồm cả comment trong code. Vì vậy, chúng tôi rất vui nếu nhận được lý giải logic code trong topic này của các bạn. Các bạn hiểu được đến đâu, xin hãy chia sẻ đến đấy. Sau đó chúng tôi sẽ tổng hợp lại.

Trong phiên bản này, Tool sẽ tạo ra file INI và lưu thông tin vào đây. Tôi không có file data lớn để thử độ mượt, liệu nó có gây cản trở cho công việc của các bạn hay không, vì vậy, hãy phản hồi cho chúng tôi biết.

1. Link download:

2. Video thuyết minh:
Bạn cần đăng nhập để thấy đa phương tiện

Bây giờ đã là gần 5h sáng (GMT+9), rất mệt. Hi vọng làm hài lòng các bạn, đặc biệt là bạn @thaipv .

3. Mật khẩu macro: tuhocvba.net
OK. Cảm ơn các bạn. Thực ra những vấn đề này tôi đều đã nghiên cứu qua. Về tạo Highlight thì cũng có 1 số cách như sau :
- Cách 0 : Dùng sự kiện SelectionChange thay đổi màu của dòng và cột.
+ Ưu điểm : Dễ dàng code​
+ Nhược điểm : Mất định dạng ban đầu của dòng và cột.​
- Cách 1 : Dùng Conditional Formatting (Định dạng có điều kiện). Đó là chủ đề trước chúng ta đã thảo luận.
- Cách 2 : Kẻ đường (dùng shape), chính là chủ đề này.
+ Ưu điểm : Không làm mất định dạng ban đầu của ô, vùng...​
+ Nhược điểm : Có thể xảy ra tình trạng người dùng click nhầm vào Shape.​
- Cách 3 : Dùng UserForm
+ Ưu điểm : Không làm mất định dạng ban đầu của ô, vùng... và rất đẹp.​
+ Nhược điểm : Rất khó code.​
Bạn cần đăng nhập để thấy đính kèm

 
M

maiban2068

Guest
Các bạn đang tìm hiểu Class EventClassModule thì cũng nên có chú ý như thế này. Để tạo ra cái class mà đối tượng ở đây là Excel, họ có dòng code ngay đầu Class.
Mã:
Public WithEvents App As Excel.Application
Như vậy trên thư viện phải khai báo đối tượng Excel.
Bạn cần đăng nhập để thấy hình ảnh


Điều này quan trọng, nếu không, dù có copy code ra file khác, cũng không chạy được đâu. Đã tìm hiểu phải tìm hiểu cho hết. Các thư viện khai báo là gì, lý do tại sao phải khai báo?
 

giaiphapvba

Administrator
Thành viên BQT
Ý kiến phân tích của bạn @NhanSu ở đây:
________________________________________________
Form và class ColorPicker dùng để tạo bảng chọn màu.
Code trong Class định nghĩa một lớp đối tượng có kiểu là Command button, thuộc tính idx sẽ chứa mã màu. Khi nút được bấm thì thủ tục SetColor trong form Main sẽ chạy còn form ColorPicker sẽ ẩn đi.
Mã:
Public WithEvents lab As MSForms.CommandButton
Public idx As Long

Private Sub Class_Initialize()

End Sub

Private Sub lab_Click()
    frmMain.SetColor (idx)
    frmColorPicker.Hide
End Sub
Trong form ColorPicker định nghĩa 1 mảng gồm 56 đối tượng ColorPicker. Các đối tượng này sau khi khai báo nó vẫn chưa được tạo ra nên cần lệnh New để tạo các nút bấm trên form. 56 nút bấm này sẽ có màu theo bảng GetPal, vị trí của các nút trên form được tính theo r (dòng) và c (cột).
Mã:
Dim clsClrs(1 To 56) As ColorPicker

Private Sub UserForm_Initialize()
   
    Dim i As Long, c As Long, r As Long
    Dim pal, clrs
    Dim lt As Double, tp As Double
    Dim hwnd&, sUnicode$
   
    pal = GetPal
    clrs = ActiveWorkbook.Colors
    For r = 0 To 6
        For c = 0 To 7
            i = i + 1
            Set clsClrs(i) = New ColorPicker
            clsClrs(i).idx = pal(i)
            Set clsClrs(i).lab = Controls.Add("forms.commandbutton.1")
            With clsClrs(i).lab
                           .Left = 15 * c + 3
                           .Top = 15 * r + 3
                           .Width = 15
                           .Height = 15
                           .BackColor = clrs(pal(i))
            End With
        Next
     Next
    hwnd = FindWindow("ThunderDFrame", Caption)  ' Tim HWnd cua UserForm
    sUnicode = UniConvert("Chojn mafu") 'Noi chua chuoi unicode
    DefWindowProcW hwnd, WM_SETTEXT, 0, StrPtr(sUnicode)
End Sub
Để lấy các mã màu ta có thể dùng thủ tục sau:
Mã:
Sub test()
    Dim Dong, Cot
    For Dong = 0 To 6
        For Cot = 0 To 7
            Sheets("Sheet1").Cells(Dong + 1, Cot + 1).Interior.ColorIndex = Dong * 8 + Cot
        Next
    Next
End Sub
 

giaiphapvba

Administrator
Thành viên BQT
@NhanSu :
Lệnh DefWindowProc với tham số WM_SETTEXT dùng để hiện title của form, xem thêm về WM_SETTEXT tại .
DefWindowProcW
Các hàm API liên quan đến chuỗi thường có 2 phiên bản: không unicode (tên hàm kết thúc bằng chữ A - ANSI), hỗ trợ unicode (kết thúc bằng chữ W -Wide). Khi sử dụng hàm DefWindowProc với Msg là WM_SETTEXT thì tham số wParam không sử dụng, lParam chứa địa chỉ của chuỗi. HWnd lấy bằng hàm FindWindow.
 

tuhocvba

Administrator
Thành viên BQT
Mình không hiểu câu lệnh này:
Mã:
frmMain.SetColor
Có ai hiểu không? Bạn @NhanSu hiểu thì giải thích giúp mình với nhé.
Cụ thể chỗ không hiểu là: SetColor. UserForm nào cũng có lệnh này à?
 

NhanSu

SMod
Thành viên BQT
Mình đang không có máy tính ở đây nên không xem code được nhưng Setcolor là thủ tục được định nghĩa trong form Main để thay đổi màu, không phải phương thức sẵn có của lớp form.
 
M

maiban2068

Guest
Mình không hiểu câu lệnh này:
Mã:
frmMain.SetColor
Có ai hiểu không? Bạn @NhanSu hiểu thì giải thích giúp mình với nhé.
Cụ thể chỗ không hiểu là: SetColor. UserForm nào cũng có lệnh này à?
UserFormName.SubName: Gọi thủ tục SubName , thủ tục này code được viết trong UserFormName.
Bạn @NhanSu trình bày như trên thì cũng như bác sĩ chữa bệnh ngoài da thôi.
Bạn cần đăng nhập để thấy hình ảnh

Thủ tục SetColor sẽ thiết định màu cho đối tượng lblColor trên UserForm.

Vai trò của Class ColorPicker là như thế này:
Bạn cần đăng nhập để thấy hình ảnh
 

BKKBG

Yêu THVBA nhất
Tôi không nghĩ ý kiến này của @NhanSu là đúng hoặc ví dụ mà bạn đưa ra không khớp với code trong addin:
Mã:
Sheets("Sheet1").Cells(Dong + 1, Cot + 1).Interior.ColorIndex = Dong * 8 + Cot
Cụ thể code trong addin không sử dụng colorindex.
Code trong addin là:
Mã:
clrs = ActiveWorkbook.Colors
Sẽ ghi ra mã RGB của 56 màu của Workbook Excel.
Bạn cần đăng nhập để thấy hình ảnh

Đây là giá trị màu RGB, không phải Colorindex (1,2,3,...).
Vì vậy đưa ra ví dụ sau sẽ hợp lý hơn:
Mã:
Sub test()
    Dim Dong As Long, Cot As Long, cnt As Long
    Dim clrs
    clrs = ActiveWorkbook.Colors
    cnt = 0
    For Dong = 0 To 6
        For Cot = 0 To 7
            cnt = cnt + 1
            Sheets("Sheet2").Cells(Dong + 1, Cot + 1).Interior.Color = clrs(cnt)
        Next
    Next
End Sub
Bạn cần đăng nhập để thấy hình ảnh

Bảng màu này nhìn khá lộn xộn, trong khi đó bảng màu trên Excel có trật tự hơn nhiều:
Bạn cần đăng nhập để thấy hình ảnh


Ở code trên, cnt tăng tuần tự từ 1 tới 56.
Về thứ tự màu mọi người có thể tìm bằng từ khóa "Palette Color", ví dụ tôi ra được link .
Còn trong addin, thay vì cho cnt chạy từ 1 tới 56 tuần tự như trên, thì cnt sẽ theo trình tự sau:
Mã:
Private Function GetPal()
     GetPal = Array( _
                        0, 1, 53, 52, 51, 49, 11, 55, 56, _
                        9, 46, 12, 10, 14, 5, 47, 16, _
                        3, 45, 43, 50, 42, 41, 13, 48, _
                        7, 44, 6, 4, 8, 33, 54, 15, _
                        38, 40, 36, 35, 34, 37, 39, 2, _
                        17, 18, 19, 20, 21, 22, 23, 24, _
                        25, 26, 27, 28, 29, 30, 31, 32)
End Function
Vì vậy:
Đầu tiên lấy mã màu RGB của 56 màu trong bảng màu:
Mã:
clrs = ActiveWorkbook.Colors
Sau đó xây dựng bảng màu:
Mã:
pal = GetPal 'cnt sẽ tăng theo trình tự này'
.BackColor = clrs(pal(i)) 'Thiết định màu'
pal(i) có vai trò như cnt ở ví dụ đầu tiên.
pal(i) = là phần tử trong mảng GetPal ở trên.

Tóm lại ta có được kết quả sau:
Bạn cần đăng nhập để thấy hình ảnh
 

giaiphapvba

Administrator
Thành viên BQT
Cảm ơn bạn BKKBG.
Mã:
Sheets("Sheet2").Cells(Dong + 1, Cot + 1).Interior.Color = clrs(cnt)
clrs(cnt) có thể thay bằng ActiveWorkbook.Colors(cnt) : Cũng có giá trị là mã màu RGB.
Kết quả cũng tương tự như của bạn:
Bạn cần đăng nhập để thấy hình ảnh

Mình thấy code ví dụ của NhanSu cũng cho kết quả tương tự thôi.
Bạn cần đăng nhập để thấy hình ảnh

Mình nghĩ cả hai bạn đều đúng. Không có ai sai.
Bạn cần đăng nhập để thấy hình ảnh
 

NhanSu

SMod
Thành viên BQT
Đúng như admin nói. Mình xin giải thích rõ hơn như sau: excel có thể thay đổi màu bằng thuộc tínhcolorindex hoặc color. Thuộc tính color có thể gán 16 triệu màu thông qua hàm RGB(0 to 255, 0 to 255, 0 to 255). Thuộc tính colorindex được gán giá trị 1 đến 56, đây là màu trong bảng màu (pallete) mà mình sẽ nói ở dưới. Trong addin này sử dụng colorindex (lệnh trong thủ tục SetColor ở formMain, hình đầu tiên trong bài 14 của maiban). Hàm GetPal với các con số lộn xộn như vậy chỉ nhằm mục đích sắp xếp lại các màu trong bảng màu cho đẹp.
Nói thêm về bảng màu, do chỉ có giá trị từ 1 đến 56 nhưng lại có tất cả 16 triệu màu nên mỗi giá trị colorindex trong bảng màu (pallete) có thể được gán bất kỳ màu nào (color) trong 16 triệu. Mỗi cách gán màu như thế ta lại có một pallete mới, mình chưa test nhưng chắc ta có thể gán 1 pallete gồm tất cả màu giống nhau cũng được. Trong thủ tục SetColor sử dụng activeworkbook.colors để lấy bảng màu của workbook, nếu không làm việc này có thể xảy ra trường hợp chọn màu đỏ lại được màu xanh chẳng hạn.
 
Sửa lần cuối:

phuongnamhp92

Yêu THVBA
OK. Cảm ơn các bạn. Thực ra những vấn đề này tôi đều đã nghiên cứu qua. Về tạo Highlight thì cũng có 1 số cách như sau :
- Cách 0 : Dùng sự kiện SelectionChange thay đổi màu của dòng và cột.
+ Ưu điểm : Dễ dàng code​
+ Nhược điểm : Mất định dạng ban đầu của dòng và cột.​
- Cách 1 : Dùng Conditional Formatting (Định dạng có điều kiện). Đó là chủ đề trước chúng ta đã thảo luận.
- Cách 2 : Kẻ đường (dùng shape), chính là chủ đề này.
+ Ưu điểm : Không làm mất định dạng ban đầu của ô, vùng...​
+ Nhược điểm : Có thể xảy ra tình trạng người dùng click nhầm vào Shape.​
- Cách 3 : Dùng UserForm
+ Ưu điểm : Không làm mất định dạng ban đầu của ô, vùng... và rất đẹp.​
+ Nhược điểm : Rất khó code.​
Bạn cần đăng nhập để thấy đính kèm

Chủ đề này không biết còn anh chị nào quan tâm không!
Em cũng từng mày mò làm theo cách số 3 của anh thaipv , vẽ userform thành đường thẳng và show form theo sự kiện selection change
Nhưng mà nó có nhược điểm là khi cuộn trang tính, resize workbook, di chuyển vị trí workbook trên màn hình thì Form sẽ bị lệch đi so với vị trí của dòng muốn Highlight ban đầu (do em chưa làm được hoặc chưa tìm hiểu để sửa chữa lỗi đó)
Hôm nay tình cờ đọc Topic, và e nghĩ đến một phuơng pháp khác là vẽ một activex control(ví dụ như Textbox) ngay trên sheet thay thế cho Userfom, có lẽ code sẽ đơn giản hơn rất nhiều
 
Top