“Nhấn và xem” với VB6
Trong công việc cũng như trong học tập, chắc không ít lần bạn phải tiếp xúc
với tài liệu nước ngoài và tự điển trên máy tính là trợ thủ đắc lực không thể
thiếu. Khả năng 'nhấn và xem' (hay 'click and see') - tra nghĩa từ ngay trong
tài liệu là tính năng rất cần thiết. Bài viết này sẽ hướng dẫn bạn xây dựng
một chương trình dạng 'nhấn và xem' với VB6.
Nguyên tắc hoạt động
Chương trình được đặt tên là CnS. Để có thể tra từ trong các ứng dụng khác (MS
Word, Arobat Reader, IE,...), chúng ta phải 'bắt' - câu móc hệ thống - để phát hiện khi
người dùng nhấn chuột (dĩ nhiên chương trình của chúng ta phải chạy nền), sau đó sao
chụp từ vị trí con trỏ chuột vào bộ nhớ và gửi về cho CnS xử lý. CnS sẽ so sánh từ này
với CSDL từ điển có sẵn và hiển thị form thông tin ngay vị trí từ muốn tra. CnS chạy
nền, đặt biểu tượng ở khay hệ thống.
Khởi tạo chương trình
Để bắt đầu, bạn tạo Standard Project mới, form1 mặc định được tạo. Vào menu
Project/Add Form để thêm form2, Project/Add module để thêm module mới. Đặt tên
form1=frmMain (Visible=false, Caption= 'Click and See'), form2=frmPopup (form hiển thị
thông tin, BoderStyle=0), module1=mHook. Vào Project/Properties chọn Startup Object
là Sub Main.
Tạo một file CSDL trong Access gồm 1 bảng (WordsTable) và trong bảng này tạo 9
trường: Words, Display, Pronunciation, Noun, Verbs, Adjective, Preposition, Adverb và
Other. Lưu với tên EV.mdb cùng thư mục với CnS.
Phần FRMMAIN
Trong frmMain bạn dùng MenuEditor tạo một menu cha (Caption: tùy ý, name:
mnuCnS,bỏ chọn ô Visible) và 2 menu con: 1(Caption: &Return to Program, name:
mnuR), 2(Caption: &Exit Program, name: mnuE). Nhấn Ctrl+T để vào hộp thoại
Components Control, click chọn và thêm lên form:
1. Microsoft ADO Data Control 6.0(OLEDB) (Name:ADO)
2. Microsoft DataGrid Control 6.0(OLEDB) (Name:DG, Enable=False, DataSource =
ADO)
3. Microsoft SysInfo Control 6.0 (Name: SI)
Đối tượng DG kết hợp với ADO sẽ hiển thị và cho phép bạn hiệu chỉnh trực tiếp lên
CSDL (Edit Mode).
Thêm lên form 5 CommandButton:
1.Caption: &Edit Mode, Name:cmdE
2. Caption: &Add Record, Name: cmdAR, Enable=false
3. Caption: &Delete Record, Name: cmdDR,Enable=false
4. Caption: &Return to Systray, Name: cmdRT
5. Caption: &Exit, Name: cmdExit
Ngoài ra, bạn thêm 2 Image để hiển thị trạng thái 'Enable' (hoạt động) và 'Disable' (tắt)
chương trình ở khay hệ thống.
Sub đảm nhiệm việc tạo icon ở khay hệ thống:
Private Sub AddToSysTray()
TrayI.cbSize = Len(TrayI)
TrayI.hWnd = Me.hWnd 'lấy handle của frmMain
TrayI.uId = 1&
TrayI.uFlags = NIF_ICON Or NIF_TIP Or NIF_MESSAGE
TrayI.ucallbackMessage = WM_LBUTTONDOWN
TrayI.hIcon = imgIcon(1).Picture 'Icon hiển thị trạng thái Enable
TrayI.szTip = 'Click and See® Program-Enable' & Chr$(0) 'Chr$(0)- định dạng lại
tooltiptext
Shell_NotifyIcon NIM_ADD, TrayI 'tạo Icon
Me.Hide 'Ẩn frmMain đi
End Sub
và gỡ bỏ icon khỏi khay hệ thống:
Private Sub RemoveFromSystray()
TrayI.hWnd = Me.hWnd
TrayI.uId = 1&
TrayI.cbSize = Len(TrayI)
Shell_NotifyIcon NIM_DELETE, TrayI 'hủy bỏ icon
UnhookWindowsHookEx hHook 'không câu móc hệ thống
End Sub
Thủ tục form_load:
Private Sub Form_Load()
AddToSysTray 'xuống khay hệ thống
bState = 1 'trạng thái của chương trình là Enable
Edit_Mode = 0 'không cho phép hiệu chỉnh DataGrid
'thực hiện câu móc chuột hệ thống:
hHook = SetWindowsHookEx(WH_MOUSE_LL, AddressOf MouseProc,
App.hInstance, 0)
ConnectStr = 'Provider=Microsoft.Jet.OLEDB.4.0;Data Source=' & App.Path &
'\EV.mdb;Persist
Security Info=False' 'chuỗi ConnectString của ADO
ADO.ConnectionString = ConnectStr
ADO.RecordSource = RecordSourceStr
ADO.Refresh
ADO.Recordset.Sort = 'Words' 'sắp xếp theo trường Words
End Sub
Sự kiện xử lý nhấn chuột lên form (thực ra là nhấn lên Icon ở khay
hệ thống):
Private Sub Form_MouseDown(Button As Integer, Shift As Integer, X As Single, Y As
Single)
Msg = X / Screen.TwipsPerPixelX 'lấy tọa độ tương ứng với từng thông báo
Select Case Msg
Case WM_RBUTTONUP 'sau khi nhấn chuột phải
Me.PopupMenu mnuCnS 'hiển thị menu Popup
Case WM_LBUTTONDOWN 'nhấn chuột trái
bState = (bState = 0)
Select Case Abs(bState)
Case 0 'Disable chương trình
TrayI.hIcon = imgIcon(0).Picture
TrayI.szTip = 'Click and See Pro gram-Disable' & Chr$(0)
'không câu móc
UnhookWindowsHookEx hHook
Case 1 'Enable chương trình
TrayI.szTip = 'Click and See Pro gram®-Enable' & Chr$(0)
TrayI.hIcon = imgIcon(1).Picture
'câu móc lại
hHook = SetWindowsHookEx(WH_MOUSE_LL, AddressOf MouseProc,
App.hInstance, 0)
End Select
Shell_NotifyIcon NIM_MODIFY, TrayI 'biến đổi thông tin Icon
End Select
End Sub
Khi tìm từ, nếu có DG sẽ trích duy nhất từ đó trong CSDL và hiển thị ở dòng đầu tiên.
Để biết từ cần tìm có trong CSDL hay không ta dựa vào thuộc tính Text của cột đầu
tiên:
Public Function FindWord(ByVal sWord As String) As Boolean
On Error GoTo errTrap
'câu truy vấn, không có khoảng cách trắng giữa dấu nháy đơn và dấu nháy kép
ADO.RecordSource = RecordSourceStr & ' where [Words]= ' ' & sWord & ' ' '
ADO.Refresh
FindWord = IIf(DG.Columns(0).Text vbNullString, True, False)
Exit Function
errTrap:
Select Case Err.Number
Case 6160 'lỗi không tìm thấy
FindWord = False 'không tìm thấy
End Select
End Function
Phần FRMPOPUP
Chọn BorderStyle cho form=0-None, Backcolor=Tooltip, Width=2000, Height=3075.
Thêm một Shape Control (mặc định là hình chữ nhật, Top=0, Left=0, Width=2000,
Height=3075) làm đường biên cho form, một CommandButton (Name:cmdClose) để
tạm thời giấu cửa sổ frmPopup, và một vbalRichEditControl (Name:rtfMain,
BackColor:vbWhite, Border=True).
Chú ý, trước khi sử dụng OCX vbalRichEditControl (có cung cấp kèm theo mã nguồn
hoặc có thể dùng RichTextBox), bạn phải chép nó vào thư mục Windows/System32,
nhấn -Run-cmd, từ dấu nhắc DOS di chuyển đến thư mục System32, gõ
'regsvr32 RichEditControl.ocx' nhấn Enter. Đồng thời cũng đăng kí thêm thư viện
SSubtmr6.dll (có cung cấp kèm theo mã nguồn). vbalRichEditControl là một
RichTextBox cấp cao, tôi đã tích hợp thêm TOM (Text Object Model) và các tính năng
khác nên rất hữu ích. Vào hộp thoại Add Components (Ctrl+T) chọn và thêm lên form
Microsoft Direct Text-to-Speech (file xvoice.dll, Name=TTS ) để thêm chức năng phát
âm cho chương trình. Thêm một Command Button (Name:cmdSpk) để khi nhấn vào đó
sẽ phát âm.
Private Sub cmdSpeak_Click()
TTS.Speak str4Speak 'str4Speak là một biến kiểu chuỗi toàn cục khai báo trong
module để lưu giữ chuỗi cần phát âm
End sub
Có những từ khác nhau nhưng nghĩa giống nhau, ta có thể tham khảo chéo bằng cách
nhấn đúp vào từ đó trong rtfMain.
Private Sub rtfMain_MouseUp(X As Single, Y As Single, Shift As Integer)
If rtfMain.SelectedText vbNullString Then'tìm từ được chọn và hiển thị nghĩa
mHook.FindWord rtfMain.SelectedText 'sub FindWord trong module mHook
End If
End Sub
Câu móc chương trình
Để câu móc vào các sự kiện của hệ thống, bạn phải sử dụng 3 API CallNextHookEx,
SetWindowsHookEx,UnhookWindowsHookEx (cẩn thận khi làm việc với hook, mã
nguồn của bạn phải chính xác, nếu không lúc debug chỉ cần một lỗi nhỏ là VB đóng lại
ngay).
Phần quan trọng nhất của chương trình, hàm 'bắt' chuột:
Public Function MouseProc(ByVal nCode As Integer, ByVal wParam As Long, ByVal
lParam As Long) As Long
On Error Goto errTrap
If nCode < 0 Then
MouseProc = CallNextHookEx(0, nCode, wParam, ByVal lParam)
Else
If (GetKeyState(VK_CONTROL) And &HF000000) And wParam =
WM_LBUTTONDOWN Then
SendKeys '^c'
SetPos frmPopup
strDisplay = Trim(Clipboard.GetText)
FindWord strDisplay
End If
MouseProc = CallNextHookEx(0, nCode, wParam, ByVal lParam)
End If
Exit Function
errTrap:
Select Case Err.Number
Case 521 'lỗi Can't open clipboard
DoEvents 'chờ 1 chút
End Select
Nếu giá trị nCodefrm.Width, pnt.X * 15)
frm.Show
BringWindowToTop frm.hWnd 'cho ở trên các cửa sổ khác
End Sub
Biến strDisplay sẽ lấy nội dung từ đã được đưa vào bộ nhớ và kiểm tra xem từ có trong
CSDL hay không, nếu có thì đưa vào rtfMain của frmPopup bằng Sub AddWord:
Private Sub AddWord(ByRef rtfText As vbalRichEdit, ByVal sDisplay As String, _
ByVal sPro As String, ByVal sN As String, ByVal sV As String, _
ByVal sAdj As String, ByVal sPrep As String, ByVal sAdv As String, _
ByVal sOther As String)
'các đối số sDisplay, sPro, sV,... để chỉ nội dung các trường
Dim cStrW As String
Static nCount As Long
a = Array('{danh từ}', '{động từ}', '{tính từ}', '{giới từ}', '{trạng từ}', '{thể loại khác}') 'tên
tiếng Việt khi thêm vào rtfText ứng với 6 trường cuối
sA = Array(sN, sV, sAdj, sPrep, sAdv, sOther)
ResetRTF rtfText 'sub ResetRTF thiết lập các thuộc tính mặc định cho rtfText
rtfText.SelText = sDisplay & ' ' & sPro & Chr(13) 'Hiển thị từ và cách phát âm
'sub HighLight để tô màu cho các từ
HighLight rtfText, sDisplay, vbBlue, True 'từ tô màu xanh và đậm
HighLight rtfText, sPro, RGB(125, 0, 0) 'cách phát âm tô màu nâu
'vòng lặp để kiểm tra trường
For i = 0 To 5
If Len(sA(i)) 0 Then
'nếu trường khác rỗng
rtfText.SelText = a(i) & Chr(13) 'thêm tên trường tương ứng tiếng Việt và kí tự xuống
dòng
ExtractWord rtfText, sA(i) 'trích nghĩa của từ trong trường
HighLight rtfText, a(i),vbRed
'tô màu đỏ cho tên tiếng Việt của trường
End If
Next
rtfText.SelStart = 0 'đưa con trỏ về đầu
rtfText.ReadOnly = True 'không cho phép hiệu chỉnh rtfMain
End Sub
Cách hiển thị từ và nghĩa tôi dựa vào tự điển của English Study 4. Tôi xin nói qua về
cách tổ chức từ điển chứa từ. CSDL gồm 9 trường, trong đó có 3 trường cần có đầy đủ
thông tin là Words, Display, Pronunciation; 6 trường còn lại có thể có hoặc không.
Trong mỗi trường, nếu từ có nhiều nghĩa thì các nghĩa cách nhau bởi dấu '/', trước câu
ví dụ phải có dấu '*'. Chương trình ở đây không đủ thông minh để nhận ra từ loại nên
phải dùng vòng lặp kiểm tra trong 6 trường cuối, nếu trường rỗng thì bỏ qua, ngược lại
thì thêm tên của trường (theo tiếng Việt tương ứng) và trích nội dung của trường vào
rtfMain. Ví dụ như chữ a, chỉ có trường Noun= 'Chữ cái đầu tiên/ một, chỉ một/nốt thứ
sáu trong gam đô trưởng, nốt la*A sharp: la thăng*A flat:la giáng' thì khi gọi AddWord
sẽ thêm vào rtfMain:
A [ei]
{danh từ}
1.Chữ cái đầu tiên
2.một, chỉ một
3.nốt thứ sáu trong gam đô trưởng, nốt la
vd:
A sharp:la thăng
A flat: la giáng
Sub sau sẽ đảm nhiệm việc trích từ:
Private Sub ExtractWord(ByRef rtfText As vbalRichEdit, ByVal sStr As String)
Dim hOneMean As Boolean
Dim SymbolDivPos As Long
Dim SymbolStarPos As Long
Dim n As Long, m As Long, i As Integer
Dim sArray(0 To 100) As String
Dim sField As String, sChar As String, sExample As String
hOneMean = IIf(InStr(sStr, '/') = 0, True, False) 'từ có một nghĩa?
sField = IIf(Right(sStr, 1) '/', sStr & '/', sStr) 'chuỗi trong mỗi trường, thêm '/' vào cuối
chuỗi
'để CnS không bỏ qua nghĩa cuối
While (InStr(sField, '/') 0)
n=n+1
'tăng số hiển thị trước mỗi nghĩa
m = -1
SymbolDivPos = InStr(sField, '/') 'vị trí của '/' trong sFiled
sChar = Left(sField, SymbolDivPos - 1) ' mỗi nghĩa trong sFiled
If InStr(sChar, '*') 0 Then 'nghĩa này có ví dụ?
sExample = Mid(sChar, InStr(sChar, '*') + 1) & '*' 'chuỗi ví dụ
While (InStr(sExample, '*') 0)
SymbolStarPos = InStr(sExample, '*') 'vị trí của '*'
m = m + 1 'số ví dụ
sArray(m) = Left(sExample, SymbolStarPos - 1) 'trích mỗi ví dụ vào mảng
sExample = Mid(sExample, SymbolStarPos + 1) ' cắt bỏ ví dụ đã trích
Wend
sChar = Left(sChar, InStr(sChar, '*') - 1) 'cắt bỏ chuỗi ví dụ
End If
rtfText.SelText = Space(2) & IIf(hOneMean, vbNullString, n & '.') _
& sChar & Chr(13) 'ghi mỗi nghĩa vào rtfText
HighLight rtfText, sChar, RGB(0, 125, 0) 'và tô màu
If m -1 Then 'mỗi nghĩa có ví dụ?
rtfText.SelText = Space(5) & 'Ví dụ:' & Chr(13)
HighLight rtfText, 'Ví dụ:', RGB(217, 0, 217) 'tô màu hồng
For i = 0 To m
rtfText.SelText = Space(7) & sArray(i) & Chr(13) 'ghi ví dụ vào rtfText
HighLight rtfText, sArray(i), RGB(125, 0, 0) 'và tô màu nâu
Next
End If
sField = Mid(sField, SymbolDivPos + 1) 'không để vòng lặp vô tận
Wend
End Sub
Đầu tiên, ta kiểm tra từ có một nghĩa hay nhiều nghĩa. Gán biến cField=sStr(nội dung
của trường) và thêm vào bên phải kí tự '/' nếu chưa có. Sau đó sử dụng 2 vòng lặp
While để trích từng nghĩa và câu ví dụ.
Private Sub HighLight(ByRef rtfText As vbalRichEdit, ByVal sStr As String, _
ByVal oColor As Long, Optional bBold As Boolean = False)
rtfText.FindText sStr, , , , lStart, lEnd
rtfText.TextDocument.Range(lStart, lEnd).Font.ForeColor = oColor
rtfText.TextDocument.Range(lStart, lEnd).Font.Bold = bBold
End Sub
Xin nói thêm về cách dùng vbalRichEditControl và TOM, OCX này lấy từ trang
vbaccelerator.com (đúng như tên của nó, bạn sẽ tìm được những thứ rất hữu ích về VB
trên trang web này). Phương thức FindText của rtfMain tìm chuỗi sStr và lưu vị trí bắt
đầu của chuỗi vào biến lStart, vị trí kết thúc vào biến lEnd. Thuộc tính TextDocument
chỉ TOM, Range(lStart,lEnd) chỉ vùng được chọn từ vị trí lStart đến lEnd, Font xác lập
các giá trị của Font cho vùng được chọn như Shadow, Emboss, Animation...
Trở lại với hàm 'bắt' chuột của ta, nếu từ không tìm thấy thì hiển thị thông báo trong
rtfMain của frmPopup:
Private Sub DisplayNoSug(rtfText As vbalRichEdit, ByVal sStr As String)
ResetRTF rtfText
rtfText.SelText = sStr & Chr(13) 'hiển thị từ không có nghĩa này
HighLight rtfText,sStr,vbBlue,True 'tô màu và đậm
rtfText.SelText = ' Không tìm thấy từ này' & Chr(13) & ' trong từ điển' 'câu thông báo
HighLight rtfText, ' Không tìm thấy từ này' & Chr(13) & ' trong tự điển',vbRed 'tô đỏ câu
thông báo
rtfText.ReadOnly = True 'không cho phép hiệu chỉnh rtfMain
str4Speak = '' 'không thể phát âm từ này
End Sub
Và thêm 2 hàm không thể thiếu:
Private Sub ResetRTF(ByRef rtfText As vbalRichEdit)
rtfText.ReadOnly = False
rtfText.Text = ''
rtfText.SelectAll
rtfText.FontBold = False
rtfText.FontColour = vbBlack
End Sub
Public Sub FindWord(ByVal sWordFind As String)
Dim sFind As String
sFind = Trim(sWordFind)
With frmMain
If .FindWord(sFind) Then 'nếu từ có trong cơ sở dữ liệu
AddWord frmPopup.rtfMain, .DG.Columns(1).Text, .DG.Columns(2).Text,
Columns(3).Text, .DG.Columns(4).Text, .DG.Columns(5).Text, .DG.Columns(6).Text,
.DG.Columns(7).Text, .DG.Columns(8).Text
str4Speak = strDisplay 'chuỗi phát âm
Else
DisplayNoSug frmPopup.rtfMain, sFind 'không tìm thấy từ
End If
End With
End Sub
Hàm chính khởi động chương trình:
Sub Main()
If FindWindow(vbNullString, 'Click and See') = 0 Then'nếu chương trình chưa có ở khay
hệ thống
Load frmMain 'thì khởi động frmMain
End If
End Sub
Tới đây chương trình của bạn đã hoàn tất. Việc còn lại hơi nặng nhọc là nhập từ vào
CSDL.