Lập trình VBA tạo nút Min, Max, Resize Userform - Kỹ thuật lập trình Windows API

maiban2068

Thành viên mới
Trước nay nghe bác Duy Tuân giới thiệu cái này, phải sử dụng thư viện riêng của bác ấy xây dựng.
Mình rất ủng hộ quan điểm của BQT diễn đàn, đó là chia sẻ kiến thức để mọi người thảo luận. Chẳng biết bác ấy tự xây dựng rồi gắn tên mình vào hay thế nào, nhưng mình thử google thì cũng không khó.
1. Nguyên liệu:
Thực hiện google ra được cái này:
Tất nhiên file phải chỉnh sửa chứ bê về chưa dùng ngày được.
Cụ thể phần khai báo API cần viết lại như sau:
Mã:
#If Win64 And VBA7 Then
        Private Declare PtrSafe Function FindWindowA Lib "USER32" _
        (ByVal lpClassName As String, _
        ByVal lpWindowName As String) As Long
       
        Private Declare PtrSafe Function GetWindowLongA Lib "USER32" _
        (ByVal hWnd As Long, _
        ByVal nIndex As Long) As Long
       
        Private Declare PtrSafe Function SetWindowLongA Lib "USER32" _
        (ByVal hWnd As Long, _
        ByVal nIndex As Long, _
        ByVal dwNewLong As Long) As Long
#Else
    Private Declare Function FindWindowA Lib "USER32" _
    (ByVal lpClassName As String, _
    ByVal lpWindowName As String) As Long
   
    Private Declare Function GetWindowLongA Lib "USER32" _
    (ByVal hWnd As Long, _
    ByVal nIndex As Long) As Long
   
    Private Declare Function SetWindowLongA Lib "USER32" _
    (ByVal hWnd As Long, _
    ByVal nIndex As Long, _
    ByVal dwNewLong As Long) As Long
#End If
Code phần còn lại trên Module là:
Mã:
Sub FormatUserForm(UserFormCaption As String)
   
    Dim hWnd            As Long
    Dim exLong          As Long
   
    hWnd = FindWindowA(vbNullString, UserFormCaption)
    exLong = GetWindowLongA(hWnd, -16)
    If (exLong And &H20000) = 0 Then
        SetWindowLongA hWnd, -16, exLong Or &H20000
    Else
    End If
   
End Sub

Sub ShowForm()
   
    UserForm1.Show
   

End Sub
Code cho UserForm là:
Mã:
Private Sub CommandButton1_Click()

    Unload Me

End Sub

Private Sub UserForm_Initialize()

    Call FormatUserForm(Me.Caption)

End Sub
Kết quả:
Bạn cần đăng nhập để thấy hình ảnh

Các bạn đã nhìn thấy cái nút thu nhỏ minize ở trên UserForm chưa?
File download:
 
Sửa lần cuối:

maiban2068

Thành viên mới
2. Giải thích code
Ở bài viết trên, các bạn chú ý thủ tục:
Sub FormatUserForm
đây chính là phần chính của cả topic này. Sau đây mình sẽ giải thích từng dòng code.
Mã:
Sub FormatUserForm(UserFormCaption As String)
   
    Dim hWnd            As Long
    Dim exLong          As Long
   
    hWnd = FindWindowA(vbNullString, UserFormCaption)
    exLong = GetWindowLongA(hWnd, -16)
    If (exLong And &H20000) = 0 Then
        SetWindowLongA hWnd, -16, exLong Or &H20000
    Else
    End If
   
End Sub
Để nhận handler của US ta có dòng code:
Mã:
hWnd = FindWindowA(vbNullString, UserFormCaption)
Để dễ tưởng tượng, trên máy tính của các bạn có rất nhiều cửa sổ (window), và để xác định cửa sổ nào mà chúng ta muốn can thiệp, thì ta phải có dòng code này. FindWindowA là một trong các hàm sử dụng thường xuyên nhất trong lĩnh vực VBA API.

Ai còn mơ hồ khái niệm handler, thì xem bài viết của admin tuhocvba .

Tiếp theo là dòng code:
Mã:
exLong = GetWindowLongA(hWnd, -16)
để xác định kiểu dáng (style) hiện tại của window này-cụ thể là với UserForm.
Code API mà viết -16 như thế này thật ra là làm khó người đọc code rất nhiều, chẳng hiểu nó là gì. Đó chính là hằng số GWL_STYLE (Get window Long Style: dịch ra là nhận về kiểu window, giá trị trả về là kiểu Long). Bạn có thể google trực tiếp từ khóa GWL_STYLE sẽ ra ngay đáp số -16:
Tôi tra thử cho các bạn ra .

Tiếp theo:
Mã:
If (exLong And &H20000) = 0 Then
        SetWindowLongA hWnd, -16, exLong Or &H20000
Else
SetWindowLongA : Để thiết định hiển thị mới cho Window
Tham số thứ nhất, là chú định thiết định cho Window nào-Dạ, em thiết định cho hWnd, chính là UserForm của chúng ta.
Tham số thứ hai, chúng ta đưa vào phương pháp Set, cho nó một tham số, ở đây chính là GWL_STYLE (-16).
Tham số thứ ba: Chúng ta đưa vào kiểu hiển thị cũ (exLong) hoặc kiểu minize (&H20000). Tự tra google nhé, WS_MINIMIZEBOX = &H20000.

Quay trở lại câu lệnh điều kiện:
Mã:
If (exLong And &H20000) = 0
Nếu chú đang hiển thị không phải là kiểu MINIZEBOX (kiểu thu nhỏ) thì ...


Đấy, quá trình học API, nó từng bước như vậy. Nhưng các cô các bác cứ giấu giếm kiến thức, không diễn giải cho mọi người hiểu, biến mọi người như những kẻ ngốc, chỉ mình các cô các chú các bác đóng vai người thông minh.
Lại thêm, mọi người lười tìm tòi, copy cả rổ code về chẳng tìm hiểu gì, có đặt mấy câu hỏi thì lại quay sang tự ái, chán lắm cơ.
 
Sửa lần cuối:

maiban2068

Thành viên mới
3. Hoàn thiện:
Giờ anh chị em đã hiểu được logic rồi, thì câu chuyện bây giờ dễ nói chuyện hơn rồi.
Tóm lại, nếu đã to (maximum) thì có thể thu nhỏ. Còn nếu đã minize thì nút này không được hiện ra nữa. Do đó tôi sửa lại code. Hãy tập trung vào các dòng code 42-47.
File anh chị em download ở #1, anh chị thay thế code trong Module bằng code dưới đây:
Mã:
#If Win64 And VBA7 Then
        Private Declare PtrSafe Function FindWindowA Lib "USER32" _
        (ByVal lpClassName As String, _
        ByVal lpWindowName As String) As Long
       
        Private Declare PtrSafe Function GetWindowLongA Lib "USER32" _
        (ByVal hWnd As Long, _
        ByVal nIndex As Long) As Long
       
        Private Declare PtrSafe Function SetWindowLongA Lib "USER32" _
        (ByVal hWnd As Long, _
        ByVal nIndex As Long, _
        ByVal dwNewLong As Long) As Long
#Else
    Private Declare Function FindWindowA Lib "USER32" _
    (ByVal lpClassName As String, _
    ByVal lpWindowName As String) As Long
   
    Private Declare Function GetWindowLongA Lib "USER32" _
    (ByVal hWnd As Long, _
    ByVal nIndex As Long) As Long
   
    Private Declare Function SetWindowLongA Lib "USER32" _
    (ByVal hWnd As Long, _
    ByVal nIndex As Long, _
    ByVal dwNewLong As Long) As Long
#End If

Option Explicit
'https://referencesource.microsoft.com/#windowsbase/Shared/MS/Win32/NativeMethodsCLR.cs,d12c7a7540390018,references
'https://docs.microsoft.com/en-us/windows/win32/winmsg/window-styles
Const WS_MINIMIZEBOX = &H20000
Const WS_MAXIMIZEBOX = &H10000
Const WS_SIZEBOX = &H40000
Sub FormatUserForm(UserFormCaption As String)

Dim hWnd            As Long
Dim exLong          As Long

    hWnd = FindWindowA(vbNullString, UserFormCaption)
    exLong = GetWindowLongA(hWnd, -16)
    If (exLong And WS_MINIMIZEBOX) = 0 Then
        SetWindowLongA hWnd, -16, exLong Or WS_MINIMIZEBOX Or WS_MAXIMIZEBOX Or WS_SIZEBOX
    ElseIf (exLong And WS_MAXIMIZEBOX) = 0 Then
        SetWindowLongA hWnd, -16, exLong Or WS_MINIMIZEBOX Or WS_SIZEBOX
   
    End If

End Sub

Sub ShowForm()
   
    UserForm1.Show
   
End Sub
Kết quả:
Bạn cần đăng nhập để thấy hình ảnh

Giải thích tới đây rồi, chắc không còn ai thắc mắc mấy hằng số này tôi tra từ đâu nữa nhé:
Mã:
Const WS_MINIMIZEBOX = &H20000 'Tham số này để co cửa sổ về nhỏ

Const WS_MAXIMIZEBOX = &H10000 'Tham số này để phóng cửa sổ ra to

Const WS_SIZEBOX = &H40000 'Tham số này để cho phép kéo co thay đổi kích thước US
WS là viết tắt của Window Style. Đến đây, kết hợp với vốn tiếng anh của bản thân, anh chị có thể suy nghĩ google, tôi cũng vậy.

Như vậy, diễn đàn tuhocvba.net là diễn đàn đầu tiên không phải sử dụng thư viện của ai, tự xây dựng xong kéo to-kéo nhỏ- thay đổi kích thước tùy ý. Ngoài ra, tôi cũng trình bày cố gắng để các bạn hiểu, và tái hiện lại quá trình tìm kiếm của tôi.

Tôi chúc diễn đàn phát triển, các bạn đồng lòng cùng admin tuhocvba xây dựng diễn đàn lớn mạnh, vì một Việt Nam giàu mạnh.
 
Top