در اين مبحث مي خواهيم توانايي شمارش خط رو به پروژه مان اضافه كنيم .
بهترين محل براي نمايش مكان نما استفاده از status bar است.
براي اين كار يك كپي از MyNotes0.6.1 گرفته و به نام MyNotes0.6.2 ذخيره ميكنيم .
حالا فرم frmNotes را كليك مكنيم و از از منوي تول بار بروي ايكون status bar ديل كليك كرده تا به فرم ما اضافه شود. اسم آن را به StatusBar تغيير ميدهيم
براي پيدا كردن شماره سطر يك edit box ما ميتوانيم از api ها استفاده كنيم
معمولا توابع api در فرم module تعريف ميشوند .
يكي از كاربردي ترين توابع در زمينه كنترل بچه پنجره ها استفاده از تابع SendMessageA است
کد:
Public Declare Function SendMessage Lib "user32" Alias "SendMessageA" (ByVal hwnd As Long, ByVal wMsg As Long, ByVal wParam As Long, lParam As Any) As Long
اين تابع داراي چهار متغير است
Hwnd كه دستگيره بچه پنجره ماست ( child window )
wMsg نوع پيغام فرستاده شده است
wParam متغير اول بر حسب نوع پيغام
lParam متغير دوم بر حسب نوع پيغام
مثلا اگر ما بخواهيم متن داخل يك box edit را دريافت كنيم از پيغام xx استفاده نماييم .
حال بر فرض مثال اين پيغام دو نوع مقدار بر ميگرداند يكي بافري كه متن را در بر دارد و ديگري اندازه يا طول اين متن از انجايي كه خروجي تابع يك عدد است . متغير متن ما توسط wParam فرستاده ميشود . اين فقط يك مثال بود تا نحوه عمل كرد اين تابع مشخص شود و در شرايط عملي اين اتفاق ميافتد اما ممكن است جاي متغير ها تغيير پيدا كند براي اطلاعات بيشتر به رفرنس هاي api مراجع كنيد .
در مثال بالا همانطور كه ديده شد بايد ما متغير متن خود را به تابع ارسال نماييم اما در تعريف پارامتر wParam از نوع any تعريف ميشود و اين تعريف مشكل ساز است . لذا تعريف هاي مثل زير انجام ميپذيرد كه همگي يك عمل را انجام ميدهند اما بر اساس متغير هاي سوم و چهارم و پيغام فرستاده شده تعريف آنها فرق ميكند. اين مشكل بخاطر نوع فرستادن متغير ها در زبان وي بي به api ميباشد.
براي بدست آوردن شماره خط ميتوان از پيغام EM_LINEFROMCHAR استفاده نمود . اگر متغير wParam برابر با -1 باشد شماره خط از ابتدا فرستاده ميشود . يعني اولين خط شماره 0 و الا آخر
نكته : تمامي متن هاي موجود در edit box ها بصورت يك آرايه تعريف ميشوند و هر خط با كد اسكي 13 ( كد دكمه enter ) و 10 تمام ميشود . لذا شماره خط ما بر حسب تعداد كدهاي 13 و بعد از ان بلافاصله كد اسكي 10 محاسبه ميشود .
براي بدست آوردن محل قرار گيري مكان نما ( curser) در خط را بصورت غير مستقيم بدست مي آوريم . بدين صورت كه با فرستادن
پيغام EM_GETSEL به edit box محل قرار گيري مكان نما بر حسب كاركتر بدست مي آيد ، سپس تعداد كاركتر هاي موجود تا انتهاي خط قبل با فرستادن پيغام EM_LINEINDEX بدست مي آيد . حال با كم كردن اين دو مقدار محل curser در خط مورد نظر محاسبه ميشود .
خوب مرسيم به كد ها
اين دو تعريف را به module مان يعني My_func اضافه ميكنيم
کد:
Public Declare Function SendMessage Lib "user32" Alias "SendMessageA" (ByVal hwnd As Long, ByVal wMsg As Long, ByVal wParam As Long, lParam As Any) As Long
Public Declare Function SendMessageReferenceParams Lib "user32" Alias "SendMessageA" (ByVal hwnd As Long, ByVal wMsg As Long, ByRef wParam As Long, ByRef lParam As Long) As Long
خوب براي مشخص كردن پيعام ها بصورت عمومي اين متغير هاي ثابت را زير اين توابع اضافه مكنيم
کد:
Public Const EM_LINEFROMCHAR = &HC9
Public Const EM_LINEINDEX = &HBB
Public Const EM_GETSEL = &HB0
تا اينجا هر چيزي كه لازم بود اضافه كرديم . حال تابعي نوشته ميشود كه با فرستادن شي edit box به ان شماره خط و محل طولي مكان نما را محاسبه نمايد.
کد:
Function FindCurofobject(obj As RichTextBox(
Dim MyHwnd As Long, Y As Long, X As Long, dumm As String
Dim PosLine As String * 2, startPos As Long, endPos As Long
Static X_old As Long, Y_old As Long
' get hwnd of child window i mean RichtextBox
MyHwnd = obj.hwnd
' by sending message to control whith paramter EM_LINEFROMCHAR and
' -1 we can find out line index number in base 0 array
' so we add 1 to convert it to base 1 array
Y = SendMessage(MyHwnd, EM_LINEFROMCHAR, -1, 0) + 1
' by sending message to control whith paramter EM_GETSEL and
' startPos ,endPos we can find character index number from first one
' that Cursor point to it. if user select some text the number return
' is index number of first character selct begin.
' and like last varible this return number is base 0 array
Call SendMessageReferenceParams(MyHwnd, EM_GETSEL, startPos, endPos)
' by sending message to control whith paramter EM_GETSEL and
' -1 we can find out how much character before this line
X = SendMessage(MyHwnd, EM_LINEINDEX, -1, 0)
' by sub this value we can find pos of cursor in this line
' don't forgot to add 1 to convert it to base 1 array
X = startPos - X + 1
' check if x and y changed(to avoid flashing in panels(
If X_old = X And Y_old = Y Then Exit Function
'Store our x & y
X_old = X
Y_old = Y
' make buffer
dumm = "(" & Y & "," & X & ")"
' set text of statusbar's first panle to our buffer
frmNotes.StatusBar.Panels.Item(1).Text = dumm
End Function
در اين تابع ابتدا مكان y مكان نما و سپس محل x آن محاسبه ميشود .
سپس اين مقادير بر اساس شكل مورد نظر در خانه اول status bar قرار داده ميشوند. لازم به ذكر است كه براي جلوگيري از چشمك زدن status bar ( زماني چشمك زدن بوقوع ميپيوند كه يك شيئ نمايش داده شده يا به سرعت تغيير نمايد يا با اينكه مقدار ان ثابت است مداوم با مقدار قبلي پر شود ) 2 مقدار استاتيك تعريف شده است ( Y_oldوX_old) كه مقادير قبلي در آنها ذخيره ميگردد. و هنگام تغيير متن خانه اول status bar چك ميگردد ايا مقادير نسبت به دفعه قبل تغيير كرده اند يا خير و اگر نه تغييري اعمال نميشود. لازم به ذكر است اين تابع را در module مان اضافه ميكنيم .
خوب حالا اين تابع را كجا ها ميتوان صدا زد ؟
بهترين محل هنگام نقاشي مجدد شي مورد نظر است .اما در داخل وي بي شي ما خاصيت paint ندارد ( با استفاده از توابع api و هوك كردن و ديگر تغييرات ميتوان اين خاصيت را به اين شي اضافه نمود كه خود مبحث ديگري را مي طلبد .) لذا ، ما اين تابع را هنگام تغيير متن
Private Sub txtNotes_Change()
کد:
TextStatus = TextStatus Or TextChanged
' if some pase happend and cursor changed
' so we call FindCurofobject to find x and y of text
Call FindCurofobject(txtNotes)
End Sub
، هنگام رها كردن هر كليد ( ممكن است دكمه هاي جابجاي arrow key استفاده شده باشد)
کد:
Private Sub txtNotes_KeyUp(KeyCode As Integer, Shift As Integer)
' if any key pressed cursor maybe change.
' so we call FindCurofobject to find x and y of text
Call FindCurofobject(txtNotes)
End Sub
، هنگام فشار دادن دكمه موس ( با كليك كردن روي جاي جاي اين edit box محل مكان نما تغيير ميكند)
کد:
Private Sub txtNotes_MouseDown(Button As Integer, Shift As Integer, X As Single, Y As Single)
' if MouseDown cursor maybe change.
' so we call FindCurofobject to find x and y of text
Call FindCurofobject(txtNotes)
End Sub
و در آخر هنگام رها كردن دكمه موس ( هنگام درگ كردن ممكن است محل مكان نما تغيير كند)
کد:
Private Sub txtNotes_MouseUp(Button As Integer, Shift As Integer, X As Single, Y As Single(
' if MouseUp cursor maybe change.
' so we call FindCurofobject to find x and y of text
Call FindCurofobject(txtNotes)
End Sub
صدا ميزنيم تا تمامي اتفاقات منجر به تغيير مكان نما را لحاظ كرده باشيم ( تا آنجايي كه من ميدانم بقيه موارد هم كه ممكن است باعث تغيير مكان نما شوند، باعث صدا زدن يكي از اين اتفاقها يا چند تاي از آنها مي شود ).
اميدوارم توضيحات كامل باشه و بتونه كمك كنه .