Hiển thị thông báo xin hãy chờ

tuhocvba

Administrator
Thành viên BQT
Đôi khi chương trình chạy quá lâu, người dùng trở nên sốt ruột và không biết chương trình có bị lỗi hay treo giữa chừng hay không, cảm giác rất lo lắng.
Vì thế, người code cần thiết kế chương trình để đưa ra thông báo xin hãy chờ một chút, chương trình đang xử lý. Điều này cũng giống như ta có hẹn một ai đó, nếu như cứ đứng chờ mà chẳng có liên lạc nào từ họ thì thật là sốt ruột. Nhưng nếu họ liên lạc và nói, họ vẫn đang trên đường tới chỗ hẹn, xin hãy chờ một chút, dù là phải chờ đi nữa nhưng cảm giác cũng nguôi đi phần nào.
Vì vậy sau đây tôi giới thiệu 3 phương pháp hiện cảnh báo này.

1. Thiết kế trên sheet.
Đây là cách đơn giản nhất. Chúng ta tạo ra một sheet có tên là sheet3. Ở đây, chúng ta cho sẵn một thông báo:
Nội dung sheet3:
Bạn cần đăng nhập để thấy đính kèm

Code của chúng ta:
Mã:
Sub Sample1()
   
    Worksheets("Sheet3").Activate
    ''Duoi day la doan code gay mat thoi gian
    'Code bat ky cua cac ban
    'Tro ve giao dien ban dau
    Worksheets("Sheet1").Activate
End Sub
Đây là một kỹ thuật đơn giản, tuy nhiên sheet3 được hiển thị ra sẽ gây khó khăn cho thao tác của macro. Giả sử macro đang thao tác với sheet1. Nhưng thứ hiện ra lại là sheet 3.
Ngoài ra nếu chỉ hiển thị vui lòng đợi, thì vẫn là chưa đủ. Người dùng cũng muốn biết trạng thái hiện tại bây giờ đang là như thế nào. Cũng tương tự như cuộc hẹn hò, đối phương đã đi tới đâu rồi, đó là thông tin người đợi muốn biết.

2. Cho hiển thị trên statusbar:
Ý tưởng này không mới, trên diễn đàn đã có một bài viết sử dụng statusbar:

Bây giờ tôi có code như sau:
Mã:
Sub Sample2()
    Dim TotalSize As Long, buf As String, ReturnSheet As Worksheet
    Set ReturnSheet = ActiveSheet           ''(1)
    Worksheets("Sheet3").Activate           ''(2)
    Application.ScreenUpdating = False      ''(3)
    ReturnSheet.Activate                    ''(4)
    ''Xu ly duoi day la xu ly mat thoi gian
    buf = Dir("C:\Windows\System32\*.*")
    Do While buf <> ""
        TotalSize = TotalSize + FileLen("C:\Windows\System32\" & buf)
        Application.StatusBar = buf & " dang duoc xu ly..."
        ActiveCell = TotalSize
        buf = Dir()
    Loop
    Application.ScreenUpdating = True       ''(5)
    Application.StatusBar = False
End Sub
Bạn cần đăng nhập để thấy đính kèm


ReturnSheet là sheet mà macro sẽ thao tác để xử lý số liệu, đây là sheet đang được Active.
Tuy nhiên sau đó chúng ta sẽ cho sheet 3 hiện ra, bởi ở sheet này có thông báo mà chúng ta thiết kế sẵn, như đã trình bày ở 1.
(3): Chúng ta không cho cập nhật màn hình. Tức là những gì diễn ra trên màn hình, từ nay sẽ không được cập nhật, những gì hiện trước mắt người dùng sẽ là hình ảnh sheet 3, là sheet có nội dung thông báo.
(4): Chúng ta quay trở về sheet mà macro sẽ thao tác. Tuy nhiên những gì hiện trước mắt người dùng vẫn là sheet 3, do chúng ta đã tắt cập nhật màn hình ở (3).
Sau khi xử lý xong ta lại cho màn hình được cập nhật bình thường, đó là (5).

(Còn nữa)
Nguồn tham khảo và dịch:
 

giaiphapvba

Administrator
Thành viên BQT
3. Sử dụng UserForm (cho Excel 97) để hiển thị thông báo.
Nếu chúng ta sử dụng Msgbox để hiển thị thông báo thì điều gì sẽ xảy ra?
Mã:
Sub Sample3()
    Dim TotalSize As Long, buf As String
    MsgBox "Chuong trinh dang xu ly..." & vbCrLf & "Xin vui long cho."
    ''Day la phan xu ly gay mat thoi gian
    buf = Dir("C:\Windows\System32\*.*")
    Do While buf <> ""
        TotalSize = TotalSize + FileLen("C:\Windows\System32\" & buf)
        buf = Dir()
    Loop
End Sub
Bạn cần đăng nhập để thấy đính kèm

Thông báo hiện ra, tuy nhiên chương trình sẽ bị tạm dừng cho tới khi người dùng ấn vào nút OK. Đây là thất bại.
Sau đây chúng ta sẽ tìm hiểu nguyên nhân thất bại.
Bạn cần đăng nhập để thấy đính kèm

Khi msgbox hiện ra, buộc người dùng phải ấn OK thì chương trình mới tiếp tục. Do đó, tại thời điểm thông báo hiện ra, thì chương trình đang bị tạm dừng.
Vì lý do này, tôi phải nói rằng, việc sử dụng msgbox để hiển thị thông báo chờ là không tốt. Chúng ta có thể khắc phục điều này bằng UserForm. Khi UserForm hiện ra, thì chương trình vẫn có thể tiếp tục được.

Ở đây tôi sử dụng hai label để ghi nội dung thông báo ra. Việc thiết kế hiển thị như thế nào là tùy ý mỗi người.
Bạn cần đăng nhập để thấy đính kèm

Bởi tính thẩm mỹ thì mỗi người khác nhau, hiện nay cũng chưa có quy chuẩn nào từ font chữ cho tới cỡ chữ, vậy các bạn tùy ý nhé.
Tiếp theo tôi sẽ viết code cho UserForm:
Mã:
Private Sub UserForm_Activate()
    Dim TotalSize As Long, buf As String
    Me.Repaint
    ''Day la doan xu ly gay mat thoi gian
    buf = Dir("C:\Windows\System32\*.*")
    Do While buf <> ""
        TotalSize = TotalSize + FileLen("C:\Windows\System32\" & buf)
        buf = Dir()
    Loop
    Unload Me
End Sub
Ở đầu thủ tục, ta có dòng code: Me.Repaint , đây cách để hiển thị cưỡng bức UserForm. Trong trường hợp máy phải xử lý nặng, có thể những gì hiển thị trên màn hình sẽ bị loạn, và code này có tác dụng làm mới lại màn hình, chắc chắn rằng UserForm này sẽ được hiển thị trên màn hình.
Sau khi đoạn code xử lý mất thời gian chạy xong, chúng ta cho ẩn UserForm bằng Unload Me.

Không thể tự nhiên mà UserForm được gọi ra, vì vậy chúng ta viết đoạn code sau ở trên một Module bất kỳ.
Mã:
Sub Sample4()
    UserForm1.Show
End Sub
Bạn cần đăng nhập để thấy đính kèm


Trên đây tôi đã giới thiệu xong cách sử dụng UserForm để hiển thị thông báo. Tuy nhiên nó chỉ phù hợp cho các bản Excel97 trở về cũ hơn.
Còn từ Excel 2000 trở về hiện tại, thì chúng ta có cách xử lý hay hơn mà chúng tôi sẽ giới thiệu sau.
Nguồn tham khảo:
 

Euler

Administrator
Thành viên BQT
3. Sử dụng UserForm (cho Excel 2000 hoặc mới hơn) để hiển thị thông báo.
Đối với Excel 2000 trở đi thì chúng ta có cách đơn giản hơn một chút.
Đó là chúng ta sẽ cho UserForm hiển thị ở chế độ vbModeless (không chế độ).
Thông thường khi UserForm được hiển thị, thì chúng ta không thể thao tác được với worksheet. Chế độ như vậy được gọi là Modal, là chế độ mặc định, nếu không chỉ thị gì thì máy tính sẽ cho hiển thị ở chế độ này.
Từ Excel 2000 trở đi, chúng ta có thể truyền tham số để can thiệp vào chế độ hiển thị của UserForm. Và, cho tới hiện tại thì các máy tính đều đã dùng Excel 2007 trở đi, cho nên bài viết này là hữu ích và có ứng dụng cao cho tất cả các bạn nếu muốn áp dụng.
Code sau các bạn đặt ở module bất kỳ:
Mã:
Sub Sample5()
    Dim TotalSize As Long, buf As String
    UserForm1.Show vbModeless
    UserForm1.Repaint
    ''Doan xu ly mat thoi gian
    buf = Dir("C:\Windows\System32\*.*")
    Do While buf <> ""
        TotalSize = TotalSize + FileLen("C:\Windows\System32\" & buf)
        buf = Dir()
    Loop
    Unload UserForm1
End Sub
Trong code trên, tôi lưu ý với các bạn rằng, vì code viết trên Module, cho nên tên đối tượng UserForm là gì cần ghi cụ thể. Ví dụ UserForm1. Chúng ta không dùng những từ khóa chung chung như Unload Me.

Tới đây, chúng ta nâng lên một mức chuyên nghiệp hơn trong thiết kế. Đó là muốn dừng chương trình giữa chừng thì phải làm như thế nào?

Dừng chương trình giữa chừng:
Trên thông báo hiện ra, tôi muốn có một nút bấm, mà nếu người dùng ấn vào thì chương trình sẽ kết thúc:
Bạn cần đăng nhập để thấy đính kèm


Trong suốt quá trình macro đang xử lý data, bất cứ thời điểm nào người dùng nhấn vào nút này thì chương trình sẽ kết thúc. Do đó quá trình kiểm tra nút bấm này là diễn ra liên tục. Hễ mà nút bấm này được nhấn, sẽ có thông báo hiện ra:
Bạn cần đăng nhập để thấy đính kèm


Nếu ấn vào YES, sẽ thực hiện phép gán flag = true. Khi đó chương trình sẽ dừng.
Bạn cần đăng nhập để thấy đính kèm


Mã:
Dim flag As Boolean
Private Sub CommandButton1_Click()
    If MsgBox("Ban co muon dung chuong trinh khong?", 292) = vbYes Then flag = True
End Sub
Private Sub UserForm_Activate()
    Dim TotalSize As Long, buf As String
    Me.Repaint
    ''Xu ly mat thoi gian
    buf = Dir("C:\Windows\System32\*.*")
    Do While buf <> ""
        DoEvents
        If flag = True Then Exit Do
        TotalSize = TotalSize + FileLen("C:\Windows\System32\" & buf)
        buf = Dir()
    Loop
    Unload Me
End Sub
Có một điểm quan trọng trong code trên đó là DoEvents. Khi máy tính xử lý khối lượng data lớn, CPU có thể bị đắm chìm vào công việc đó, dẫn tới việc bạn không thể nào click vào nút bấm trên UserForm khi chương trình đang làm việc. Và như vậy thì không thể nào dừng được chương trình. Lệnh DoEvents cho phép làm mới lại màn hình sau mỗi lần lệnh này được gọi, do đó bạn có thể thao tác với nút bấm trên màn hình, tránh được hiện tượng như đã nói ở trên. Về điều này, trên diễn đàn cũng đã từng đề cập và có video thuyết minh, các bạn xem lại ở đây:

Tóm lại, bây giờ ta có thể thiết kế chương trình như sau:
Code trên Module:
Mã:
Public flag As Boolean
Sub Sample5()
    Dim TotalSize As Long, buf As String
    UserForm1.Show vbModeless
    UserForm1.Repaint
    flag = False 'Gia tri khoi tao ban dau'
    ''Doan xu ly mat thoi gian
    buf = Dir("C:\Windows\System32\*.*")
    Do While buf <> ""
        DoEvents
        If flag = True Then Exit Do
        TotalSize = TotalSize + FileLen("C:\Windows\System32\" & buf)
        buf = Dir()
    Loop
    Unload UserForm1
End Sub
Code trên UserForm1:
Mã:
Private Sub CommandButton1_Click()
    If MsgBox("Ban co muon dung chuong trinh khong?", 292) = vbYes Then flag = True
End Sub
Biến flag khai báo public. Do đó, để tránh ảnh hưởng sau mỗi lần chạy, giá trị cũ của biến số này còn lưu lại, chúng ta cần cẩn thận khởi tạo giá trị ban đầu cho nó là False. Điều này đảm bảo rằng, dù là lần chạy thứ 2 hay lần chạy thứ n, thì giá trị khởi tạo của nó luôn là False. Có nhiều trường hợp sau khi kết thúc chương trình, giá trị biến số không bị xóa đi. Nếu như nó là True, thì ở lần chạy sau, giá trị của nó là True, trái với ý đồ của ta là bắt đầu chương trình thì nó phải là False, do đó, việc khởi tạo giá trị ban đầu là False, là bước làm cần thiết, không dư thừa.

Như vậy, thông qua topic này, chúng ta đã bước đầu làm quen với thiết kế chương trình. Nắm được mong muốn của người dùng, từ đó đưa ra các chỉ dẫn phù hợp, thông báo cho người dùng biết, chương trình xử lý sẽ mất thời gian cho nên xin hãy đợi. Tuy nhiên đây chỉ là bước đầu tiên. Như ban đầu topic đã đề cập, làm sao để người dùng hiểu được chương trình đã đi tới đâu, thực hiện được bao nhiêu % công việc, đó là thông tin cần thiết. Vì vậy chúng ta sẽ còn đề cập tới câu chuyện còn dở dang này vào một lần khác. Topic này xin được tạm thời kết thúc ở đây.
(Hết).
Nguồn tham khảo:
 
Top