分享
 
 
 

TextBox模拟拖曳选取文字

王朝厨房·作者佚名  2007-01-04
窄屏简体版  字體: |||超大  

我们知道Rich text或Word 或VB的程式撰写环境,可以将Mouse移到Select起来的文字

按Mouse左键做拖曳移动的功能,後来想,TextBox能不能做呢?这可真的吃了不少苦头

,这个程式模拟其做法,但主要的精神是在於对TextBox的了解。

首先,TextBox中当选取一段文字之後,我们只要按Mosue,便使Select的区域失效,且

可能进入另外的一个Select域,故第一件事是如何在有Select的区域时,使这动作失效;

的作法是在MouseUp时Check一下有没有选取文字,如果有,就使用SubClass的技术,拦截

Mouse的左键,所以当我们按左键时,不会再有选取文字又不见了的情况。

第二,我们没有按下Mouse,那如何得知Mouse所在的地方到底是TextBox的哪个字呢,所幸

有EM_CHARFROMPOS这个讯息可Send给textBox,其传回值的HiWord 得该字元是在第几行

0为base,LowWord是该字元在TextBox中的位置(含换行与LineFeed),因而我们可以单

由MouseMove便得知何时Mouse要是箭号,何时是内定I形的Mouse。当然想得知Mouse所在

可以透过Mouse Event的X, Y座标,但是它们是以Twips为单位,而另外也可以用GetCursorPos()

来得知Mouse的位置,但这是相对於萤幕者,EMCHARFROMPOS的讯息需要的是相对於TextBox

的座标,有许多种方法可以完成这转换,但我选ScreenToClient()这个API,比较直接。

第叁,Caret如何隐藏呢?使用HideCaret可完成,但这个Function只能呼叫一次,以便

下回 ShowCaret()时可以将Caret Show出来,这是因为呼叫2次的HideCaret时,也要呼

叫2次的ShowCaret才能使Caret出现。另设定Caret的SetCaretPos() API只是令Caret出现

在什麽地,但如果您KeyIn任何字时,仍出现在原来之地方,而不是方才设定之处,而

要用EM_SETSEL的Message才能设定KeyIn的位置是Caret的位置。

另有一个取得textbox中第charindex个字元,在textbox中Mouse的位置(textbox的左上角为原点)

pos = SendMessage(hwnd, EM_POSFROMCHAR, charindex, 0)

my = pos \ 2 ^ 16 ’Y座标

mx = pos Mod 2 ^ 16 ’X座标

这个程式的重点便是上面所写的,其他是苦功

’以下在.Bas

’注:本程式之所以要用一个变数来存Caret是否被隐藏,原因是:当HideCaret()呼叫N次

’便得呼叫N次 ShowCaret()来复原,反之亦然,所以程式中,用一个变数来确认Hide/Show

’的动作只做一次

Option Explicit

Declare Function SetWindowLong Lib "user32" Alias "SetWindowLongA" _

(ByVal hwnd As Long, ByVal nIndex As Long, ByVal dwNewLong As Long) As Long

Declare Function GetWindowLong Lib "user32" Alias "GetWindowLongA" _

(ByVal hwnd As Long, ByVal nIndex As Long) As Long

Declare Function CallWindowProc Lib "user32" Alias "CallWindowProcA" _

(ByVal lpPrevWndFunc As Long, ByVal hwnd As Long, ByVal Msg As Long, _

ByVal wParam As Long, ByVal lParam As Long) As Long

Public Const GWL_WNDPROC = (-4)

Public Const WM_MOUSEMOVE = &H200

Public Const WM_RBUTTONDOWN = &H204

Public Const WM_LBUTTONDOWN = &H201

Public Const WM_CUT = &H300

Public Const WM_PASTE = &H302

Public Const EM_POSFROMCHAR = 214

Public Const EM_CHARFROMPOS = 215

Public Const EM_SETSEL = &HB1

Public Const EM_GETSEL = &HB0

Public Const EM_SCROLL = &HB5

Public Const EM_LINEFROMCHAR = &HC9

Public Const EM_LINEINDEX = &HBB

Public Const EM_LINESCROLL = &HB6

Public Const SB_LINEDOWN = 1

Public Const SB_LINEUP = 0

Type POINTAPI

X As Long

Y As Long

End Type

Type RECT

Left As Long

Top As Long

Right As Long

Bottom As Long

End Type

Declare Function GetWindowRect Lib "user32" (ByVal hwnd As Long, lpRect As RECT) As Long

Declare Function GetClientRect Lib "user32" (ByVal hwnd As Long, lpRect As RECT) As Long

Declare Function SendMessage Lib "user32" Alias "SendMessageA" (ByVal hwnd As Long, ByVal wMsg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long

Declare Function HideCaret Lib "user32" (ByVal hwnd As Long) As Long

Declare Function ShowCaret Lib "user32" (ByVal hwnd As Long) As Long

Declare Function SetCaretPos Lib "user32" (ByVal X As Long, ByVal Y As Long) As Long

Declare Function GetCursorPos Lib "user32" (lpPoint As POINTAPI) As Long

Declare Function ScreenToClient Lib "user32" (ByVal hwnd As Long, lpPoint As POINTAPI) As Long

Private SelEnd As Long ’存TextBox Mark起来的起点

Private SelST As Long ’存textBix Mark起来的终点

Private CaretHide As Boolean ’存Caret是否被隐藏

Private CanPaste As Boolean ’存是否处於可以Paste的状态

Public preWinProc As Long

’取得Mouse所在的字元在TextBox中的位置

Public Function GetCharIndex(ByVal hwnd As Long, Optional CharLineNo As Long) As Long

Dim mx As Integer, my As Integer

Dim wParam As Long, lParam As Long

Dim i As Long

Dim pos As Long, pt As POINTAPI

Call GetCursorPos(pt) ’取得相对Screen的Mouse之位置

i = ScreenToClient(hwnd, pt) ’将Mouse位置转换成相对於TextBox的位置

mx = pt.X

my = pt.Y

If mx < 0 Then mx = 0

If my < 0 Then my = 0

lParam = mx + 2 ^ 16 * my

wParam = 0

i = SendMessage(hwnd, EM_CHARFROMPOS, 0, lParam)

If Not IsMissing(CharLineNo) Then

CharLineNo = i \ 2 ^ 16 ’取得该字元是在第几行,0为base

End If

GetCharIndex = i Mod 2 ^ 16 ’传回该字元是在textBox中的第几个字,0为base

End Function

Public Sub SetCaretPosition(ByVal hwnd As Long)

Dim mx As Long, my As Long, pos As Long

Dim charindex As Long

Dim pt As POINTAPI, i As Long

Dim rect5 As RECT, rect6 As RECT

charindex = GetCharIndex(hwnd)

’取得textbox中第charindex个字元,在textbox中Mouse的位置(textbox的左上角为点

pos = SendMessage(hwnd, EM_POSFROMCHAR, charindex, 0)

my = pos \ 2 ^ 16

mx = pos Mod 2 ^ 16

’设定Caret出现的位置,但只是显示的位置,实际keyin进去的字出现的地方没因而更动

Call SetCaretPos(mx, my)

’取得Mouse所在之座标(Screen左上角为原点)

Call GetCursorPos(pt)

’取得TextBox的萤幕座标(Screen左上角为原点)

Call GetWindowRect(hwnd, rect6)

’取得TextBox可keyin字的区域大小(textBox左上角为原点)

Call GetClientRect(hwnd, rect5)

’取得textbox Client区域相对Screen的座标

rect5.Left = rect6.Left

rect5.Right = rect5.Right + rect6.Left

rect5.Top = rect6.Top

rect5.Bottom = rect5.Bottom + rect6.Top

’Mouse移到四个边时,自动scroll,就算不必Scroll时也可呼叫,只是不会有作用

If pt.Y <= rect5.Top + 3 Then

i = SendMessage(hwnd, EM_SCROLL, SB_LINEUP, 0)

End If

If pt.Y >= rect5.Bottom - 3 Then

Call SendMessage(hwnd, EM_SCROLL, SB_LINEDOWN, 0)

End If

If pt.X <= rect5.Left + 3 Then

i = SendMessage(hwnd, EM_LINESCROLL, -1, 0)

End If

If pt.X >= rect5.Right - 3 Then

Call SendMessage(hwnd, EM_LINESCROLL, 1, 0)

End If

End Sub

’设定Mouse的形状

Public Sub SetMouseShap(hwnd As Long, ByVal Button As Integer)

Dim charindex As Long

Dim i As Long

If preWinProc <> 0 Then

If Button = 1 Then

Screen.ActiveControl.MousePointer = 99

Screen.ActiveControl.MouseIcon = LoadPicture("dragmove.cur")

’请自行设定dragmove.cur的位置

Call SetCaretPosition(hwnd)

Exit Sub

End If

charindex = GetCharIndex(hwnd)

’设定Mouse移过mark的区块时,Mouse变箭号

If charindex >= SelST And charindex <= SelEnd Then

If Button = 0 Then

Screen.ActiveControl.MousePointer = 1

End If

Else

Screen.ActiveControl.MousePointer = 0

End If

End If

End Sub

Public Function wndproc(ByVal hwnd As Long, ByVal Msg As Long, _

ByVal wParam As Long, ByVal lParam As Long) As Long

’以下程式会截取mouse move,处理完後,再将之送往原来的Window Procedure

Dim charindex As Long

Dim i As Long

If Msg = WM_LBUTTONDOWN Then

If CaretHide Then

Call ShowCaret(hwnd)

CaretHide = False

End If

If SelEnd - SelST <> 0 Then

charindex = GetCharIndex(hwnd)

If charindex >= SelST And charindex <= SelEnd Then

Call SetCaretPosition(hwnd)

Screen.ActiveControl.MousePointer = 99

Screen.ActiveControl.MouseIcon = LoadPicture("c:\tmp2\dragmove.cur")

CanPaste = True

Exit Function

End If

End If

End If

wndproc = CallWindowProc(preWinProc, hwnd, Msg, wParam, lParam)

End Function

Public Sub MoveText(ByVal hwnd As Long, CanFree As Boolean)

Dim i As Long, sellen As Long, charindex As Long

sellen = SelEnd - SelST

’如果Caret落在mark起来之处则不处理

charindex = GetCharIndex(hwnd)

If charindex >= SelST And charindex <= SelEnd Then

CanFree = False

Exit Sub

End If

Call SendMessage(hwnd, WM_CUT, 0, 0) ’将Mark起来的地方Cut掉

Dim setpos As Long

If charindex < SelST Then

setpos = charindex

Else

If charindex > SelEnd Then setpos = charindex - sellen

End If

’设定Caret新位置,此时Keyin进去的字才真的会在此位置出现,使用SetCaretPos()则不行

Call SendMessage(hwnd, EM_SETSEL, setpos, setpos)

Call SendMessage(hwnd, WM_PASTE, 0, 0)

End Sub

Public Sub SetHook(ByVal hwnd As Long, ByVal Button As Integer)

Dim ret As Long

Dim i As Long

Dim charindex As Long

If Button = 1 Then

If Screen.ActiveControl.SelLength > 0 Then

If preWinProc = 0 Then

’记录原本的Window Procedure的位址

preWinProc = GetWindowLong(hwnd, GWL_WNDPROC)

ret = SetWindowLong(hwnd, GWL_WNDPROC, AddressOf wndproc)

Call HideCaret(hwnd)

CaretHide = True

CanPaste = False

’取得Mark起来的区域之Start, End之Index,之所以不用Text.SelStart

’与Text.SelLength来做的原因是:vb对之的度量是字元为单位,但API

’的其他呼叫都以Byte为单位,我如此做,省得中间的转换

i = SendMessage(hwnd, EM_GETSEL, 0, 0)

SelEnd = i \ 2 ^ 16

SelST = i Mod 2 ^ 16

Else

Dim CanFree As Boolean

CanFree = True

If CanPaste Then

Call MoveText(hwnd, CanFree)

End If

If CanFree Then Call FreeHook(hwnd)

End If

Else

If preWinProc <> 0 Then

Call FreeHook(hwnd)

End If

End If

End If

End Sub

Public Sub FreeHook(ByVal hwnd As Long)

Dim ret As Long

If preWinProc <> 0 Then

ret = SetWindowLong(hwnd, GWL_WNDPROC, preWinProc)

End If

preWinProc = 0

Screen.ActiveControl.MousePointer = 0

If CaretHide Then

Call ShowCaret(hwnd)

CaretHide = False

End If

End Sub

Public Sub GetCaretPos(ByVal hwnd5 As Long, lineno As Long, colno As Long)

Dim i As Long, j As Long

Dim lParam As Long, wParam As Long

Dim k As Long

i = SendMessage(hwnd5, EM_GETSEL, wParam, lParam)

j = i / 2 ^ 16 ’取得目前Caret所在前面有多少个byte

lineno = SendMessage(hwnd5, EM_LINEFROMCHAR, j, 0) ’取得前面有多少行

lineno = lineno + 1

k = SendMessage(hwnd5, EM_LINEINDEX, -1, 0)

’取得目前caret所在行前面有多少个byte

colno = j - k + 1

End Sub

>

’以下在Form

Private Sub Text1_LostFocus()

Call FreeHook(Text1.hwnd)

End Sub

Private Sub Text1_KeyDown(KeyCode As Integer, Shift As Integer)

Call FreeHook(Text1.hwnd)

End Sub

Private Sub Text1_MouseMove(Button As Integer, Shift As Integer, X As Single, Y As Single)

Call SetMouseShap(Text1.hwnd, Button)

End Sub

Private Sub Text1_MouseUp(Button As Integer, Shift As Integer, X As Single, Y As Single)

Call SetHook(Text1.hwnd, Button)

End Sub

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有