Khai báo biến như thế nào để có thể "xài cả đời" ?

  • Thread starter Thread starter BNTT
  • Ngày gửi Ngày gửi
Liên hệ QC

BNTT

Bùi Nguyễn Triệu Tường
Thành viên danh dự
Tham gia
3/7/07
Bài viết
4,946
Được thích
23,208
Nghề nghiệp
Dạy đàn piano
Tôi đang tập tành vọc VBA. Tôi có làm vài cái Form, và những Combobox cũng như nhiều thứ khác truy xuất đến dữ liệu trong bảng tính. Những dữ liệu này đã được đặt Name hoặc đó là những Table, để dễ nhớ, và cũng dễ truy xuất hàng, cột...

Vấn đề là, Excel không nhớ được Name (hoặc Table) nào là của WorkSheet nào. Do đó, nếu lỡ như mà đang mở một lúc 2 bảng tính (hoặc nhiều hơn), thì khi chạy Form, tôi bị VBA báo lỗi tùm lum. Tức quá, tôi gài cái lệnh này vào trước khi chạy một Form:

PHP:
    Windows("DMHH.xlsm").Activate
Mọi việc trong có vẻ ổn. Bởi bi giờ thì Excel đã biết là dữ liệu cần truy xuất lấy ở Workbook nào.

Nhưng... Lại có cái dở là, lỡ như tôi Save as Workbook đó với tên khác, thì công toi... hoặc là lại phải lục hết mấy cái dòng lệnh như ở trên đây, đổi tên của Workbook thành tên mới. Thật mắc công quá.

Và tôi lại mày mò tạo một cái biến, cái biến này chính là tên của Workbook, và có thể dùng để khai báo trong suốt quá trình làm việc. Nghĩa là ngay khi mở Workbook có chứa những cái Form này ra, thì có một lệnh sẽ chạy, chụp lấy cái tên của Workbook, và sử dụng chúng cho mỗi khi cần chạy một Form nào đó.

Tôi đã nghĩ đến trò sử dụng sự kiện Workbook_Open, và làm cái code sau:

PHP:
Private Sub Workbook_Open()
    AAA = ActiveWorkbook.Name
End Sub

Và cứ nghĩ là sẽ dùng cái biến này thay thế vào đây được:

PHP:
    Windows(AAA).Activate
Nhưng... VBA nó hổng chịu nhớ AAA là cái quái gì. Và đương nhiên là nó tô vàng khè cái dòng lệnh trên đây.

Vậy, cho tôi hỏi, tôi phải khai báo AAA như thế nào để VBA hiểu và nhớ mãi... cho đến khi nào tôi đóng cái Workbook đó lại ?

À, xin nói thêm, lão Cheettit có biểu tôi khai báo cái này ở trong module:

Nhưng... không ăn thua gì hết. Khi chạy Form, VBA nó vẫn hỏi AAA là cái gì ?
 
Lần chỉnh sửa cuối:
Bạn phải xem kỹ lại vị trí khai báo biến.
Nếu khai báo public trong modul thì không dùng trong thisworkbook được đâu.
Muốn có biến dùng chung thì hãy khai báo bên ngoài các thủ tục.
Ví dụ trong ThisWorkbook bạn khai báo như sau
Dim AAA as string
Private Sub Workbook_Open()
AAA = ActiveWorkbook.
Name
End Sub
Private Sub Workbook_SheetSelectionChange(ByVal Sh As Object, ByVal Target As Range)
MsgBox AAA

End Sub
Biến này chỉ có hiệu lực trong File bạn đang làm việc thôi, khi chuyển qua file khác thì không có hiệu lực nữa nên bạn không thể gọi được Windows(AAA).Activate
Nói thêm.
- Nếu form của bạn để ở trong file.xls bình thường thì hiển nhiên khi mở file đó lên thì nó đã được Activate rồi.
- Thường thì sử dụng câu lệnh của bạn khi cần cho một workBook nào đó Activate và như vậy bạn phải dùng modul hoặc form đặt trong .xla để gọi.
 
Lần chỉnh sửa cuối:
Upvote 0
Vấn đề là, Excel không nhớ được Name (hoặc Table) nào là của WorkSheet nào. Do đó, nếu lỡ như mà đang mở một lúc 2 bảng tính (hoặc nhiều hơn), thì khi chạy Form, tôi bị VBA báo lỗi tùm lum. Tức quá, tôi gài cái lệnh này vào trước khi chạy một Form:
Em không biết công việc anh muốn làm gì và cấu trúc của dữ liệu anh thế nào nhưng em nghĩ không phải Excel không nhớ Name.. mà là do code của anh chưa "tường minh".
Ví dụ :
Workbooks(1).Sheets(2).Range("A1").Value
khác với :
Workbooks(2).Sheets(1).Range("A1").Value

Nếu nhu cầu công việc của anh không quá phức tạp thì dùng cách trên sẽ khỏi mất công khai báo biến gì cả, code lại gọn hơn, chạy nhanh hơn.

Dòng này anh phải khai báo lên "trên đầu" Module nhưng biến này chỉ có tác dụng trên file đó thôi. Ví dụ
Mã:
Public AAA As String

Sub Test()
..........
End Sub
TDN
 
Lần chỉnh sửa cuối:
Upvote 0
Khó diễn đạt cho các bạn hiểu ghê... Thôi để tôi ráng làm một ví dụ nho nhỏ như vầy:

Tôi có một Workbook có tên là DMHH.xls. Trong Workbook này, có một cái Form, có tên là ABC. Để mở cái Form này, tôi dùng một macro:

PHP:
Sub Open_ABC()
      ABC.Show
End sub
Và cái macro này, tôi cho nằm trên Menu chung của Excel, để mà chỉ cần nhấn vào nó thì mở cho tôi cái Form ABC.

Vấn đề là, cái Form ABC này chỉ có trong DMHH.xls mà thôi, do đó, nếu như mở 2, 3 bảng tính cùng một lần, mà thò chuột lên Menu của Excel nhấn cái macro kia, thì làm sao mà Excel nó biết Form ABC nằm trong Workbook nào ? Và nếu lỡ như cái Workbook hiện hành không phải là DMHH.xls, thì Excel sẽ la làng...

Do đề phòng chuyện này, nên tôi chèn vào trong macro trên một đoạn nữa:
PHP:
Sub Open_ABC()
     Windows("DMHH.xls").Activate 
     ABC.Show
End sub

Mọi việc OK. Tới đây thì các bạn hiểu chứ ạ ?

Vấn đề là.... Lỡ như mà tôi đổi tên DMHH.xls thành thứ gì đó, XZY.xls chẳng hạn, thì cái dòng lệnh mà tôi chèn vào ở trên vô tác dụng. Dĩ nhiên là tôi có thể vào macro đó sửa lại tên của Workbook. Nhưng tôi lười lắm, nên muốn Excel tự động làm việc này cho tôi.

Và ý tôi muốn là vầy: Khi mở cái Workbook có chứa cái Form ABC ra, thì ngay lập tức, tạo dùm tôi một cái biến, cái biến này chính là tên của Workbook (bất cứ tên gì, không nhất thiết phải là DMHH.xls nữa). Ở bài trên, tôi có tạm gọi đó là biến AAA. Và tôi muốn làm sao đó để có thể xài cái biến này mỗi khi tôi Activate cái Workbook có chứa cái Form ABC, để Excel biết đường mà mở cái Form ABC, chứ không có la làng.

Các bạn có hiểu ý tôi muốn không ạ ? Nếu hiểu, thì vui lòng chỉ dùm tôi: Phải khai báo cái biến AAA này ở đâu, khai báo như thế nào để bất kỳ lúc nào Excel nó cũng hiểu rằng AAA là tên của cái Workbook có chứa cái Form ABC.

Tôi đã thử khai báo ở trong cái module có chứa cái macro dùng để mở cái Form ABC là:
PHP:
Public ABC As String
Đồng thời trong This Workbook, tôi có làm cái này:
PHP:
Sub Workbook_Open
     AAA = ThisWorkbook.Name
End Sub
Nhưng không được. Nghĩa là Excel nó không nhớ cái AAA này là gì khi chạy macro, trừ phi vừa mở Workbook đó ra và gọi macro mở Form ABC liền.

Lúc nãy, lão Cheettit có nói tôi khai như vầy:
PHP:
Global AAA As String
Cái này nhớ được lâu hơn cái Public một tí, là nếu như có chọn Sheet khác, làm gì đó... rồi mới gọi macro mở Form ABC, thì Excel vẫn nhớ AAA là tên của Workbook hiện hành. Nhưng nếu tôi mà chọn một Workbook khác (trong số các Workbook đang mở đồng thời) thì Excel quên sạch sành sanh, chả còn nhớ AAA là cái gì nữa...

Mong các bạn cho tôi phương án khác. Cũng không cần phải bám vào chuyện khai báo biến... Chỉ cần giúp tôi làm thế nào để khi tôi nhấn vào cái macro (đang nằm trên Menu chung của Excel) thì nó biết đường mà tìm ra cái Form ABC đang nằm trong Workbook nào, nhảy tới cái Workbook đó, rồi mở dùm tôi cái Form ABC ra....

Xin cảm ơn.

From Sa_DQ:
Nhìn vô tiêu đề thì bài này phải ở BOX 'Bổ sung kiến thức VB & lập trình trên nền .NET cho Excel'.
 
Chỉnh sửa lần cuối bởi điều hành viên:
Upvote 0
Mình tạo được thanh trình đơn có tên :"Vi du Menu" trong file đính kèm.
Nếu đúng ý thì tôi sẽ giải thích về nồi dung code cho mọi người.
Khi mở excel lên thì hệ thống menu xuất hiện cùng và cả Userform do mình tạo sẽ tồn tại suốt khi ta mở Excel.
Như vậy sẽ không có macro nào cả, form sẽ tự load khi ta khỏi động Excel.
Theo tôi nghĩ vấn đề này không biết có đúng ý của BNTT không?
Thân !!!
 

File đính kèm

Lần chỉnh sửa cuối:
Upvote 0
Trong workbook có Userform, anh hãy khai báo mã dưới đây tại sự kiện Workbook_Open
Mã:
Private Sub Workbook_Open()
    [COLOR="SeaGreen"]'Lưu tên workbook vào Registry "HKEY_CURRENT_USER\Software\VB and VBA Program Settings\"[/COLOR]
    SaveSetting "BTNNAPP", "WBInfo", "WBName", ActiveWorkbook.Name
End Sub

Thủ tục mở Userform khai báo như sau
Mã:
Sub ShowUserform()
    Dim WBName As String
    WBName = GetWorkbookName
    If LCase(ActiveWorkbook.Name) <> LCase(WBName) Then
        Workbooks(WBName).Activate
    End If
    Userform.Show
End Sub
'-------------------------------------------------------------------
Function GetWorkbookName()
    GetWorkbookName = GetSetting("BTNNAPP", "WBInfo", "WBName")
End Function
 
Upvote 0
Trong workbook có Userform, anh hãy khai báo mã dưới đây tại sự kiện Workbook_Open
Mã:
Private Sub Workbook_Open()
    [COLOR=SeaGreen]'Lưu tên workbook vào Registry "HKEY_CURRENT_USER\Software\VB and VBA Program Settings\"[/COLOR]
    SaveSetting "BTNNAPP", "WBInfo", "WBName", ActiveWorkbook.Name
End Sub
Thủ tục mở Userform khai báo như sau
Mã:
Sub ShowUserform()
    Dim WBName As String
    WBName = GetWorkbookName
    If LCase(ActiveWorkbook.Name) <> LCase(WBName) Then
        Workbooks(WBName).Activate
    End If
    Userform.Show
End Sub
'-------------------------------------------------------------------
Function GetWorkbookName()
    GetWorkbookName = GetSetting("BTNNAPP", "WBInfo", "WBName")
End Function

Anh có thể giải thích rõ hơn chút được không ạh -\\/.
 
Upvote 0
Vì muốn xài cả đời!

Nên tôi muốn khi mở excel lên là tôi sẵn có biến đối tượng hay biến kiểu chuỗi tôi khai báo trước đó không vậy
Những biến này còn tồn tại cho đến khi tôi ngưng excel được không vậy các bạn?

--=0 :-= @$@!^% )(&&@@ @!## !$@!!
 
Upvote 0
Nên tôi muốn khi mở excel lên là tôi sẵn có biến đối tượng hay biến kiểu chuỗi tôi khai báo trước đó không vậy
Những biến này còn tồn tại cho đến khi tôi ngưng excel được không vậy các bạn?

--=0 :-= @$@!^% )(&&@@ @!## !$@!!

Biến chỉ tồn tại trong VBA khi Excel đang chạy anh ạ, muốn nó giữ lại các giá trị của biến thì chỉ có cách ghi giá trị nó vào bảng tính Excel hoặc file hoặc Registry, khi mở Excel thì lại gán lại.
 
Upvote 0
BNTT đã viết:
Mong các bạn cho tôi phương án khác. Cũng không cần phải bám vào chuyện khai báo biến... Chỉ cần giúp tôi làm thế nào để khi tôi nhấn vào cái macro (đang nằm trên Menu chung của Excel) thì nó biết đường mà tìm ra cái Form ABC đang nằm trong Workbook nào, nhảy tới cái Workbook đó, rồi mở dùm tôi cái Form ABC ra....
Xin cảm ơn.
Vấn đề của bạn là do bạn chưa nắm rõ đối tượng ta đang làm việc.
Biến lưu ở file.xls bình thường thì chỉ tồn tại và có tác dụng khi file đó hoạt động. Bạn đặt Form trong file.xls nhưng lại gọi nó bằng menu thì chỉ có tác dụng khi file dó Activate. Bạn muốn mở form trong file.xls thì làm như bạn là đúng rồi, vấn đề còn lại là làm sao để nhớ được cái file chứa Form ABC thì hay hãy lưu tên workbook vào trong registry như TuanVNUNI đã nêu hoặc trong file.xla (mang tính trung gian). Sau này, khi ta kích hoạt menu thì gọi giá trị đó ra để gán vào câu lệnh.
Ví dụ:
Khi kích hoạt file chứa form ABC ta lưu lại tên của nó vào trong registry bằng cách đặt đoạn code sau vào trong file chứa form ABC.
PHP:
Private Sub Workbook_Activate()
    'Lưu tên workbook vào registry
    SaveSetting "Tên Chương Trình", "Nhánh Gì Đó", "Khóa gì đó", ThisWorkbook.Name
End Sub
Khi kích hoạt menu
PHP:
Sub Open_ABC() 
     'Đọc giá trị lưu trong regisstry
     Dim AAA as string
     AAA = GetSetting "Tên Chương Trình", "Nhánh Gì Đó", "Khóa gì đó"
     Windows(AAA).Activate  
     ABC.Show 
End sub
Hy vọng bạn đã nắm rõ hơn.
 
Upvote 0
Khi kích hoạt file chứa form ABC ta lưu lại tên của nó vào trong registry bằng cách đặt đoạn code sau vào trong file chứa form ABC.
PHP:
Private Sub Workbook_Activate()
'Lưu tên workbook vào registry
SaveSetting "Tên Chương Trình", "Nhánh Gì Đó", "Khóa gì đó", ThisWorkbook.Name
End Sub
.
Thể khi muốn xóa key này (dọn rác) thì viết code như thế nào các bạn?
 
Upvote 0
Thể khi muốn xóa key này (dọn rác) thì viết code như thế nào các bạn?
Code vầy:
PHP:
Sub Test()
  Dim mykey As String
  mykey = """HKCU\Software\VB and VBA Program Settings\Ten chuong trinh""
  With CreateObject("WScript.Shell")
    .Run "cmd /c reg delete " & mykey & " /f", 0, True
  End With
End Sub
Dùng lệnh DOS quả nhiên lợi hại vô cùng ---> Nếu dùng theo cách thông thường (VB code) thì chắc code phải dài cả trang giấy
 
Lần chỉnh sửa cuối:
Upvote 0
Code vầy:
PHP:
Sub Test()
  Dim mykey As String
  mykey = """HKCU\Software\VB and VBA Program Settings\Ten chuong trinh""
  With CreateObject("WScript.Shell")
    .Run "cmd /c reg delete " & mykey & " /f", 0, True
  End With
End Sub
Dùng lệnh DOS quả nhiên lợi hại vô cùng ---> Nếu dùng theo cách thông thường (VB code) thì chắc code phải dài cả trang giấy

Như thế này cũng được mà không biết có ổn không? (mới tìm ra):
PHP:
Sub XoaKey()
mykey = GetSetting("Tên chương trình", "tên nhánh", "Khóa gì đó")
If mykey <> "" Then DeleteSetting "Tên chương trình"
End Sub
 
Upvote 0
Như thế này cũng được mà không biết có ổn không? (mới tìm ra):
PHP:
Sub XoaKey()
mykey = GetSetting("Tên chương trình", "tên nhánh", "Khóa gì đó")
If mykey <> "" Then DeleteSetting "Tên chương trình"
End Sub
Đương nhiên được anh à!
Cái khác nhau giữa 2 phương pháp là:
- Cách của anh luôn luôn xóa tại key: HKEY_CURRENT_USER\Software\VB and VBA Program Settings
- Cách của em có thể xóa bất cứ key nào mình muốn
 
Upvote 0
Đương nhiên được anh à!
Cái khác nhau giữa 2 phương pháp là:
- Cách của anh luôn luôn xóa tại key: HKEY_CURRENT_USER\Software\VB and VBA Program Settings
- Cách của em có thể xóa bất cứ key nào mình muốn

Vậy của ndu rất hay, có thể từ VBA dùng vòng lặp dọn dẹp các Registry có liên quan đến từ khóa nào đó mà mình muốn?
Còn ở trên mình chỉ có thể tạo key tại vị trí cố định là HKEY_CURRENT_USER\Software\VB and VBA Program Settings
Có tạo được key nơi khác đâu?
 
Upvote 0
Web KT

Bài viết mới nhất

Back
Top Bottom