source: trunk/ab5.0/ablib/src/Classes/ActiveBasic/Windows/UI/Control.ab@ 637

Last change on this file since 637 was 637, checked in by イグトランス (egtra), 16 years ago

Button.OnClickの仕組みを汎用的(WM_COMMAND全般)に。WndProcなどをProtectedへ。

File size: 16.6 KB
Line 
1'Classes/ActiveBasic/Windows/UI/Control.ab
2
3#require <Classes/ActiveBasic/Windows/UI/EventArgs.ab>
4#require <Classes/ActiveBasic/COM/ComClassBase.ab>
5
6Namespace ActiveBasic
7Namespace Windows
8Namespace UI
9
10Namespace Detail
11 TypeDef PTrackMouseEvent = *Function(ByRef tme As TRACKMOUSEEVENT) As BOOL
12End Namespace
13
14/*
15@brief Windowsのウィンドウを管理する基底クラス
16@auther Egtra
17*/
18Class Control
19 Inherits WindowHandle
20 Implements ActiveBasic.COM.InterfaceQuerable
21Public
22 /*!
23 @brief Destroyイベントよりも後に呼ばれるDestroyイベントデリゲート
24 @date 2008/07/16
25 */
26 finalDestroy As ActiveBasic.Windows.UI.Handler
27
28 Sub Control()
29 comImpl = New COM.ComClassDelegationImpl(This)
30 End Sub
31
32 Function Handle() As HWND
33 Handle = hwnd
34 End Function
35
36 /*!
37 @brief HWNDからControlインスタンスを取得する。
38 @param[in] hwnd 対象のウィンドウハンドル
39 @return 対応するControlインスタンス。ただし、存在しなければNothing。
40 */
41 Static Function FromHWnd(hwnd As HWND) As Control
42 FromHWnd = Nothing
43 If _System_IsWindow(hwnd) Then
44 FromHWnd = FromHWndCore(hwnd)
45 End If
46 End Function
47
48Private
49 Static Function FromHWndCore(hwnd As HWND) As Control
50 FromHWndCore = _System_PtrObj(GetProp(hwnd, PropertyInstance As ULONG_PTR As PCTSTR) As VoidPtr) As Control
51 End Function
52
53'--------------------------------
54' ウィンドウ作成
55
56Public
57 /*!
58 @brief ウィンドウを作成する(詳細版)。
59 @date 2008/08/02
60 通常はCreateやCreateFormその他を使ってください。
61 */
62 Sub CreateEx(parent As Control, style As DWord, exStyle As DWord, hmenu As HMENU)
63 Dim cs As CREATESTRUCT
64 With cs
65 .dwExStyle = exStyle
66 .lpszClass = (atom As ULONG_PTR) As LPCTSTR
67 .lpszName = 0
68 .style = style
69 .x = 0
70 .y = 0
71 .cx = 0
72 .cy = 0
73 If IsNothing(parent) Then
74 .hwndParent = 0
75 Else
76 .hwndParent = parent As HWND
77 End If
78 .hMenu = hmenu
79 .hInstance = hInstance
80 End With
81 GetCreateStruct(cs)
82 createImpl(cs, parent)
83 End Sub
84
85 /*!
86 @brief ウィンドウを作成する(子ウィンドウ以外)。
87 @date 2008/08/02
88 */
89 Sub CreateForm(style As DWord, exStyle As DWord, owner = Nothing As Control, hmenu = 0 As HMENU)
90 CreateEx(owner, style, exStyle, hmenu)
91 End Sub
92
93 Sub CreateForm()
94 CreateEx(Nothing, 0, 0, 0)
95 End Sub
96
97 /*!
98 @brief 子ウィンドウを作成する。
99 @date 2008/08/02
100 */
101 Sub Create(parent As Control, style = 0 As DWord, exStyle = 0 As DWord, id = 0 As Long)
102 CreateEx(parent, style Or WS_CHILD Or WS_VISIBLE, exStyle, id As ULONG_PTR As HMENU)
103 End Sub
104
105Protected
106 Abstract Sub GetCreateStruct(ByRef cs As CREATESTRUCT)
107
108 Sub createImpl(ByRef cs As CREATESTRUCT, parent As Control)
109 throwIfAlreadyCreated()
110
111 StartWndProc()
112
113 With cs
114 'よそのウィンドウクラスの場合、WndProcFirst内のhwndの代入が行われないため、ここでも行っておく。
115 hwnd = CreateWindowEx(.dwExStyle, .lpszClass, .lpszName, .style,
116 .x, .y, .cx, .cy, .hwndParent, .hMenu, .hInstance, .lpCreateParams)
117 If hwnd = 0 Then
118 ThrowWithLastErrorNT("Control.CreateEx")
119 End If
120
121 If IsNothing(FromHWndCore(hwnd)) <> False Then
122 AssociateHWnd(hwnd)
123 TlsSetValue(tlsIndex, 0)
124 End If
125 End With
126
127 If IsNothing(parent) = False Then
128 RegisterUnassociateHWnd(parent)
129 End If
130 End Sub
131
132Public
133 Sub Attach(hwndNew As HWND)
134 throwIfAlreadyCreated()
135 If hwndNew = 0 Then
136 Throw New System.ArgumentNullException("Control.Attach")
137 End If
138 registerStandardEvent()
139 AssociateHWnd(hwndNew)
140 End Sub
141
142 Sub BeginSubclass()
143 throwIfNotCreated()
144 prevWndProc = SetWindowLongPtr(GWLP_WNDPROC, AddressOf(WndProcFirst) As LONG_PTR) As WNDPROC
145 End Sub
146
147Private
148 Sub throwIfAlreadyCreated()
149 If hwnd <> 0 Then
150 Throw New System.InvalidOperationException("Window is already created.")
151 End If
152 End Sub
153
154 Sub throwIfNotCreated()
155 If hwnd = 0 Then
156 Throw New System.InvalidOperationException("Window is not already created.")
157 End If
158 End Sub
159
160'--------------------------------
161' ウィンドウプロシージャ
162
163Protected
164 Virtual Function WndProc(msg As DWord, wp As WPARAM, lp As LPARAM) As LRESULT
165 If Not ProcessMessage(msg, wp, lp, WndProc) Then
166 WndProc = DefWndProc(msg, wp, lp)
167 End If
168 End Function
169
170 Virtual Function DefWndProc(msg As DWord, wp As WPARAM, lp As LPARAM) As LRESULT
171 If prevWndProc Then
172 DefWndProc = CallWindowProc(prevWndProc, hwnd, msg, wp, lp)
173 Else
174 DefWndProc = DefWindowProc(hwnd, msg, wp, lp)
175 End If
176 End Function
177Protected
178 Function ProcessMessage(msg As DWord, wp As WPARAM, lp As LPARAM, ByRef lr As LRESULT) As Boolean
179 Dim h = Nothing As MessageHandler
180 Dim b = messageMap.TryGetValue(Hex$(msg), h)
181 If b Then
182 If Not IsNothing(h) Then
183 Dim a = New MessageArgs(hwnd, msg, wp, lp)
184 h(This, a)
185 If a.Handled Then
186 lr = a.LResult
187 ProcessMessage = True
188 Exit Function
189 End If
190 End If
191 End If
192 ProcessMessage = False
193 End Function
194Private
195 Static Function makeKeysFormMsg(e As MessageArgs) As Keys
196 Dim t As DWord
197 t = e.WParam And Keys.KeyCode
198 t Or= (GetKeyState(VK_SHIFT) As Word And &h8000) << 1
199 t Or= (GetKeyState(VK_CONTROL) As Word And &h8000) << 2
200 t Or= (GetKeyState(VK_MENU) As Word And &h8000) << 3
201 makeKeysFormMsg = t As Keys
202 End Function
203
204 Static Function makeMouseEventFromMsg(e As MessageArgs) As MouseArgs
205 Dim wp = e.WParam
206 Dim lp = e.LParam
207 makeMouseEventFromMsg = New MouseArgs(LOWORD(wp) As MouseButtons, 1, GET_X_LPARAM(lp), GET_Y_LPARAM(lp), 0)
208 End Function
209
210Protected
211 /*!
212 @brief 最初にウィンドウプロシージャを使うための前処理を行う関数
213 @date 2008/07/11
214 WndProcFirstを使うときは、この関数を呼んでおく必要がある。
215 */
216 Sub StartWndProc()
217 TlsSetValue(tlsIndex, ObjPtr(This))
218 registerStandardEvent()
219 End Sub
220Private
221 /*!
222 @brief 主なメッセージハンドラの登録を行う関数
223 @date 2008/08/02
224 */
225 Sub registerStandardEvent()
226 AddMessageEvent(WM_ERASEBKGND, AddressOf(OnEraseBackground))
227 Dim md = New MessageHandler(AddressOf(OnMouseDownBase))
228 AddMessageEvent(WM_LBUTTONDOWN, md)
229 AddMessageEvent(WM_RBUTTONDOWN, md)
230 AddMessageEvent(WM_MBUTTONDOWN, md)
231 AddMessageEvent(WM_XBUTTONDOWN, md)
232 Dim mu = New MessageHandler(AddressOf(OnMouseUpBase))
233 AddMessageEvent(WM_LBUTTONUP, mu)
234 AddMessageEvent(WM_RBUTTONUP, mu)
235 AddMessageEvent(WM_MBUTTONUP, mu)
236 AddMessageEvent(WM_XBUTTONUP, mu)
237 Dim mb = New MessageHandler(AddressOf(OnMouseDblClkBase))
238 AddMessageEvent(WM_LBUTTONDBLCLK, mb)
239 AddMessageEvent(WM_RBUTTONDBLCLK, mb)
240 AddMessageEvent(WM_MBUTTONDBLCLK, mb)
241 AddMessageEvent(WM_XBUTTONDBLCLK, mb)
242
243 AddMessageEvent(WM_MOUSEMOVE, AddressOf(OnMouseMoveBase))
244 AddMessageEvent(WM_MOUSELEAVE, AddressOf(OnMouseLeaveBase))
245 AddMessageEvent(WM_MOUSEHOVER, AddressOf(OnMouseHoverBase))
246 AddMessageEvent(WM_PAINT, AddressOf(OnPaintBase))
247 AddMessageEvent(WM_KEYDOWN, AddressOf(OnKeyDownBase))
248 AddMessageEvent(WM_CHAR, AddressOf(OnChar))
249 AddMessageEvent(WM_CREATE, AddressOf(OnCreateBase))
250 AddMessageEvent(WM_SIZE, AddressOf(OnSize))
251 End Sub
252
253 Sub OnEraseBackground(sender As Object, e As MessageArgs)
254 Dim a = New PaintBackgroundArgs(e.WParam, e.LParam)
255 e.Handled = e.Handled And OnPaintBackground(a)
256 e.LResult = a.Painted
257 End Sub
258
259 Sub OnMouseDownBase(sender As Object, e As MessageArgs)
260 e.Handled = e.Handled And OnMouseDown(makeMouseEventFromMsg(e))
261 End Sub
262
263 Sub OnMouseUpBase(sender As Object, e As MessageArgs)
264 Dim me = makeMouseEventFromMsg(e)
265 If doubleClickFired = False Then
266 OnClick(Args.Empty)
267 OnMouseClick(me)
268 Else
269 doubleClickFired = False
270 End If
271 e.Handled = e.Handled And OnMouseUp(me)
272 End Sub
273
274 Sub OnMouseDblClkBase(sender As Object, e As MessageArgs)
275 Dim me = makeMouseEventFromMsg(e)
276 doubleClickFired = True
277 OnMouseDown(me)
278 OnDoubleClick(Args.Empty)
279 e.Handled = e.Handled And OnMouseDoubleClick(me)
280 End Sub
281
282 Sub OnMouseMoveBase(sender As Object, e As MessageArgs)
283 Dim me = makeMouseEventFromMsg(e)
284 If mouseEntered = False Then
285 mouseEntered = True
286 OnMouseEnter(me)
287 trackMouseEvent(TME_LEAVE Or TME_HOVER)
288 End If
289 e.Handled = e.Handled And OnMouseMove(me)
290 End Sub
291
292 Sub OnMouseLeaveBase(sender As Object, e As MessageArgs)
293 e.Handled = e.Handled And OnMouseLeave(Args.Empty)
294 mouseEntered = False
295 End Sub
296
297 Sub OnMouseHoverBase(sender As Object, e As MessageArgs)
298 Dim me = makeMouseEventFromMsg(e)
299 e.Handled = e.Handled And OnMouseHover(me)
300 End Sub
301
302 Sub OnPaintBase(sender As Object, e As MessageArgs)
303 If ActiveBasic.IsNothing(paintDC) Then
304 e.Handled = False
305 Else
306 Dim ps As PAINTSTRUCT
307 BeginPaint(ps)
308 Try
309 OnPaintDC(New PaintDCArgs(ps.hdc, ps.rcPaint))
310 Finally
311 EndPaint(ps)
312 End Try
313 End If
314 End Sub
315
316 Sub OnKeyDownBase(sender As Object, e As MessageArgs)
317 e.Handled = e.Handled And OnKeyDown(New KeyArgs(makeKeysFormMsg(e)))
318 End Sub
319
320 Sub OnKeyUpBase(sender As Object, e As MessageArgs)
321 e.Handled = e.Handled And OnKeyUp(New KeyArgs(makeKeysFormMsg(e)))
322 End Sub
323
324 Sub OnChar(sender As Object, e As MessageArgs)
325 e.Handled = e.Handled And OnKeyPress(New KeyPressArgs(e.WParam As Char))
326 End Sub
327
328 Sub OnCreateBase(sender As Object, e As MessageArgs)
329 e.Handled = e.Handled And OnCreate(New CreateArgs(e.LParam As *CREATESTRUCT))
330 End Sub
331
332 Sub OnSize(sender As Object, e As MessageArgs)
333 e.Handled = e.Handled And OnResize(New ResizeArgs(e.WParam, e.LParam))
334 End Sub
335
336 messageMap As System.Collections.Generic.Dictionary<Object /*DWord*/, MessageHandler>
337
338Public
339 /*!
340 @biref メッセージイベントハンドラを登録する。
341 @date 2007/12/04
342 */
343 Sub AddMessageEvent(message As DWord, h As MessageHandler)
344 If Not IsNothing(h) Then
345 If IsNothing(messageMap) Then
346 messageMap = New System.Collections.Generic.Dictionary<Object, MessageHandler>
347 End If
348 Dim msg = Hex$(message)
349 Dim m = Nothing As MessageHandler
350 If messageMap.TryGetValue(msg, m) Then
351 messageMap.Item[msg] = m + h
352 Else
353 messageMap.Item[msg] = h
354 End If
355 End If
356 End Sub
357
358 /*!
359 @biref メッセージイベントハンドラ登録を解除する。
360 @date 2007/12/04
361 */
362 Sub RemoveMessageEvent(message As DWord, a As MessageHandler)
363 If Not IsNothing(a) Then
364 If Not IsNothing(messageMap) Then
365 Dim msg = Hex$(message)
366 Dim m = messageMap.Item[msg]
367 If Not IsNothing(m) Then
368 messageMap.Item[msg] = m - a
369 End If
370 End If
371 End If
372 End Sub
373
374'--------------------------------
375' ウィンドウメッセージ処理
376
377
378'--------
379'イベント
380
381#include "ControlEvent.sbp"
382
383'--------------------------------
384' インスタンスメンバ変数
385Private
386 /*!
387 @brief サブクラス化前のウィンドウプロシージャ
388 @date 2008/08/23
389 サブクラス化していなければNULL
390 */
391 prevWndProc As WNDPROC
392 /*!
393 @brief マウスカーソルがクライアント領域に入っているかどうかのフラグ
394 外から中に入ってきたときにMouseEnterイベントを発生させるために用意している。
395 */
396 mouseEntered As Boolean
397 /*!
398 @brief ダブルクリックが起こったかどうかのフラグ
399 Click/MouseClickイベントのために用意している。
400 @date 2008/07/12
401 */
402 doubleClickFired As Boolean
403
404'--------------------------------
405' 初期ウィンドウクラス
406Protected
407 Static Function WndProcFirst(hwnd As HWND, msg As DWord, wp As WPARAM, lp As LPARAM) As LRESULT
408 Imports System.Runtime.InteropServices
409
410 Dim rThis = FromHWndCore(hwnd)
411 If IsNothing(rThis) Then
412 rThis = _System_PtrObj(TlsGetValue(tlsIndex)) As Control
413 TlsSetValue(tlsIndex, 0)
414 If IsNothing(rThis) Then
415 Goto *InstanceIsNotFound
416 End If
417 ' ウィンドウが作られて最初にWndProcFirstが呼ばれたとき
418 rThis.AssociateHWnd(hwnd)
419 End If
420 If msg = WM_NCDESTROY Then
421 rThis.UnassociateHWnd()
422 rThis.hwnd = 0
423 End If
424 If msg = WM_DESTROY And IsNothing(rThis.finalDestroy) = False Then
425 Dim f = rThis.finalDestroy
426 f(rThis, Args.Empty)
427 End If
428 WndProcFirst = rThis.WndProc(msg, wp, lp)
429 Exit Function
430
431 *InstanceIsNotFound
432 Dim err = String.Concat("Control.WndProcFirst: The attached instance is not found. msg = &h",
433 Hex$(msg), Ex"\r\n")
434 OutputDebugString(ToTCStr(err))
435 WndProcFirst = DefWindowProc(hwnd, msg, wp, lp)
436 End Function
437
438 /*!
439 @brief Controlインスタンスとウィンドウハンドルを結び付ける。
440 @param[in] hwndNew 結び付けるウィンドウハンドル
441 @date 2008/07/16
442 これを実行することで、UnassociateHWndされるまでControlがGC対象にならなくなると共に、
443 FromHWndでウィンドウハンドルからControlインスタンスの取得が可能となる。
444 */
445 Sub AssociateHWnd(hwndNew As HWND)
446 SetHWnd(hwndNew)
447 Prop[PropertyInstance] = ObjPtr(This) As HANDLE
448 comImpl.AddRef()
449 End Sub
450
451 /*!
452 @brief オーナーの破棄時にこのControlインスタンスとウィンドウハンドルの結び付けを解除するよう登録する。
453 @param[in] owner 結び付けの解除を連動させるControl
454 @date 2008/07/16
455 ownerのFinalDestoryでこのControlとHWNDとの結び付けを解除する。
456 */
457 Sub RegisterUnassociateHWnd(owner As Control)
458 If IsNothing(owner) = False Then
459 Dim e = New Handler(AddressOf(UnassociateHWndOnEvent))
460 If IsNothing(finalDestroy) Then
461 owner.finalDestroy = e
462 Else
463 owner.finalDestroy += e
464 End If
465 End If
466 End Sub
467
468 Sub UnassociateHWndOnEvent(sender As Object, e As Args)
469 UnassociateHWnd()
470 hwnd = 0
471 End Sub
472
473 Sub UnassociateHWnd()
474 comImpl.Release()
475 End Sub
476
477' Static Const WM_CONTROL_INVOKE = WM_USER + 0 As DWord
478' Static Const WM_CONTROL_BEGININVOKE = WM_USER + 1 As DWord
479
480'--------------------------------
481' インタフェース実装
482
483Public
484 Virtual Function QueryInterfaceImpl(ByRef iid As IID, ByRef pv As VoidPtr) As HRESULT
485 QueryInterfaceImpl = E_NOTIMPL
486 End Function
487
488Private
489 /*!
490 @brief ウィンドウの寿命管理
491 Controlには次のAddRef-Releaseの対がある。
492 @li createImpl - WM_NCDESTROY(ウィンドウプロシージャがWndProcFirstの場合)
493 @li createImpl - UnassociateHWnd←UnassociateHWndOnEvent←RegisterUnassociateHWnd(その他のウィンドウクラスの場合)
494 @li Attach - WM_NCDESTROY(サブクラス化された場合)
495 なお、Control派生クラスをサブクラス化すると、後ろ2つが両方適用される。
496 */
497 comImpl As COM.ComClassDelegationImpl
498
499'--------------------------------
500' その他の補助関数
501Private
502 Function trackMouseEvent(flags As DWord) As BOOL
503 If pTrackMouseEvent <> 0 Then
504 Dim tme As TRACKMOUSEEVENT
505 With tme
506 .cbSize = Len(tme)
507 .dwFlags = flags
508 .hwndTrack = hwnd
509 .dwHoverTime = HOVER_DEFAULT
510 End With
511 trackMouseEvent = pTrackMouseEvent(tme)
512 End If
513 End Function
514
515'--------------------------------
516' 初期化終了関連(特にウィンドウクラス)
517Private
518 'ウィンドウ作成時にウィンドウプロシージャへThisを伝えるためのもの
519 Static tlsIndex As DWord
520
521 Static hInstance As HINSTANCE
522 Static atom As ATOM
523 Static hmodComctl As HMODULE
524 Static pTrackMouseEvent As Detail.PTrackMouseEvent
525
526 Static Const WindowClassName = "ActiveBasic.Windows.UI.Control"
527 Static Const PropertyInstance = 0 As ATOM
528Public
529 Static Sub Initialize(hinst As HINSTANCE)
530 tlsIndex = TlsAlloc()
531 If tlsIndex = TLS_OUT_OF_INDEXES Then
532 ThrowWithLastError("Control.Initialize: TlsAlloc failed.")
533 End If
534 hInstance = hinst
535 hmodComctl = LoadLibrary("comctl32.dll")
536 pTrackMouseEvent = GetProcAddress(hmodComctl, ToMBStr("_TrackMouseEvent")) As Detail.PTrackMouseEvent
537
538 Dim PropertyInstanceString = WindowClassName + " " + Hex$(GetCurrentProcessId())
539 PropertyInstance = GlobalAddAtom(ToTCStr(PropertyInstanceString))
540 If PropertyInstance = 0 Then
541 ThrowWithLastError("Control.Initialize: GlobalAddAtom failed.")
542 End If
543
544 Dim wcx As WNDCLASSEX
545 With wcx
546 .cbSize = Len (wcx)
547 .style = CS_HREDRAW Or CS_VREDRAW Or CS_DBLCLKS
548 .lpfnWndProc = AddressOf (WndProcFirst)
549 .cbClsExtra = 0
550 .cbWndExtra = 0
551 .hInstance = hinst
552 .hIcon = 0
553 .hCursor = LoadImage(0, MAKEINTRESOURCE(IDC_ARROW), IMAGE_CURSOR, 0, 0, LR_DEFAULTSIZE Or LR_SHARED) As HCURSOR
554 .hbrBackground = 0
555 .lpszMenuName = 0
556 .lpszClassName = ToTCStr(WindowClassName)
557 .hIconSm = 0
558 End With
559 atom = RegisterClassEx(wcx)
560 If atom = 0 Then
561 ThrowWithLastErrorNT("Control.Initialize: RegisterClasseEx failed.")
562 End If
563 End Sub
564
565 Static Sub Uninitialize()
566 If atom <> 0 Then
567 UnregisterClass(atom As ULONG_PTR As PCSTR, hInstance)
568 End If
569 If tlsIndex <> 0 And tlsIndex <> &hffffffff Then
570 TlsFree(tlsIndex)
571 End If
572 If hmodComctl <> 0 Then
573 FreeLibrary(hmodComctl)
574 End If
575 If PropertyInstance <> 0 Then
576 GlobalDeleteAtom(PropertyInstance)
577 End If
578 End Sub
579End Class
580
581/*!
582@brief WM_COMMANDで通知を行うコントロールの基底クラス
583@date 2008/08/28
584@auther Egtra
585親がWM_COMMANDを受け取ったら、RaiseCommandEventを呼ぶことを意図している。
586(Formで実装済み)
587*/
588Class WmCommandControl
589 Inherits Control
590Public
591 Abstract Function RaiseCommandEvent(notificationCode As Word) As Boolean
592End Class
593
594End Namespace 'UI
595End Namespace 'Widnows
596End Namespace 'ActiveBasic
Note: See TracBrowser for help on using the repository browser.