📄 编写 windows 标准控件㈡ .txt
字号:
mov [ebx].iMaxWidth,eax
.endif
invoke ReleaseDC,hList,ps.hdc
.endif
invoke SendMessage,hList,WM_SIZE,0,0
pop eax ;返回索引
设置项目的信息,如果包含文本,则复制到内存中,并且,计算文本的总长度,与结构中的最大长度比较,更长则替换,之后,WM_SIZE消息发送,水平滚动条自动变化
.elseif SDWORD ptr edx>=0 && edx<[ebx].iCount
mov eax,[edi+edx*4]
jmp @@SetExistItem
.endif
设置一个项目的信息,直接返回对应的地址,并跳转到公共部分
MLM_GETSTRING 获得字串
mov MI.imask,MIF_TEXT
mov MI.cbText,0FFFFh
mov eax,lParam
mov MI.lpszText,eax
invoke SendMessage,hList,MLM_GETITEM,wParam,addr MI
获得项目的文本,wParam为索引,lParam==指向返回字串的缓存地址
MLM_GETITEM ;获得项目数据
mov eax,MLM_ERROR
mov esi,lParam
mov ecx,wParam
.if SDWORD ptr ecx>=0 && ecx<[ebx].iCount
mov edi,[ebx].lpItems
mov edi,[edi+ecx*4]
.if [esi].imask & MIF_IMAGE
mov ecx,[edi].MYLIST_ITEM.iImage
mov [esi].iImage,ecx
.endif
.if [esi].imask & MIF_USERDATA
mov ecx,[edi].MYLIST_ITEM.dwData
mov [esi].dwData,ecx
.endif
mov eax,wParam ;返回索引
.if [esi].imask & MIF_TEXT
mov ecx,[edi].MYLIST_ITEM.cbText
inc ecx
.if ecx>=[esi].cbText
mov ecx,[esi].cbText
.endif
push ecx
invoke lstrcpyn,[esi].lpszText,addr [edi].MYLIST_ITEM.lpText,ecx
;如果获得字串,返回大小
pop eax
.endif
.endif
assume esi:nothing
获得项目数据,wParam为索引,lParam==指向返回信息的结构地址
成功返回非零值。失败为-1
MLM_SETIMAGELIST 设置图像列表
invoke ParentNotify,hList,MLN_IMAGECHANGED,wParam
or [ebx].fStyle ,MLFS_SHARED
mov eax,[ebx].hImage
mov ecx,wParam
mov [ebx].hImage,ecx
设置图像列表,wParam为句柄,lParam==0
返回前一次的图像列表。
函数替换默认的图像列表,并且设置标志”共享的图像列表“,如果不再需要,请删除旧的图像列表。
在替换之前,发送消息给父窗口,因为使用者有可能获得图像列表之后,保存了起来,然后操作它。替换后,通知一下,方便使用者手动更新。
MLM_GETIMAGELIST 获得图像列表
mov eax,[ebx].hImage
直接返回当前使用的图像列表
MLM_ADDIMAGE ;添加图片到图像列表中.返回索引
xor eax,eax
.if lParam!=eax
.if wParam==IMAGE_BITMAP
invoke CopyImage,lParam,IMAGE_BITMAP,0,0,LR_COPYRETURNORG
mov edi,eax
invoke ImageList_Add,[ebx].hImage,lParam,eax
push eax
invoke DeleteObject,edi
pop eax
.elseif wParam==IMAGE_ICON
invoke ImageList_AddIcon,[ebx].hImage,lParam
.endif
.endif
添加一个图片或者一个图标到当前图像列表中。
wParam指定格式,lParam指定句柄
成功返回图像索引,项目的图像是直接使用此索引的。失败返回-1
MLM_RESTORE 初始化状态
invoke HeapCreate,HEAP_NO_SERIALIZE,0,0
.if eax!=0
mov esi,eax
invoke HeapAlloc,esi,HEAP_ZERO_MEMORY or HEAP_NO_SERIALIZE,1024*4
.if eax!=0
mov [ebx].lpItems,eax
.if [ebx].hHeap!=0
invoke HeapDestroy,[ebx].hHeap
.endif
mov [ebx].hHeap,esi
mov [ebx].cbItems,1024
xor eax,eax
mov [ebx].iTop,eax
mov [ebx].iLeft,eax
mov [ebx].iMaxWidth,eax
mov [ebx].iCount,eax
mov [ebx].iSel,-1
.if !([ebx].fStyle & MLFS_SHARED) && wParam!=0
invoke ImageList_Remove,[ebx].hImage,-1
.endif
invoke SendMessage,hList,WM_SIZE,0,0
.endif
.endif
初始化控件,重新创建新堆,新内存,新的图像列表。旧的全部删除。
在这里判断了图像列表是否是共享的,是则不管它。
之后,直接WM_SIZE,之后,滚动条会自动全部设置好。
MLM_GETITEMRECT 获得项目区域
mov eax,[ebx].rt.bottom
xor edx,edx
div [ebx].iHeight
.if edx!=0
inc eax
.endif
mov ecx,[ebx].iTop
add eax,ecx
.if wParam>=ecx && wParam<=eax
mov edx,[ebx].iCount
cmp wParam,edx ;在可视范围
jge @@Error
mov eax,wParam
sub eax,ecx
xor edx,edx
mov ecx,[ebx].iHeight
mul ecx
add eax,[ebx].rt.top
add ecx,eax
invoke SetRect,lParam,[ebx].rt.left,eax,[ebx].rt.right,ecx
mov eax,TRUE
.else
xor eax,eax
.endif
获得指定项目的区域,目标项目必须是在可视客户区内才会成功。
wParam为项目索引,lParam指向RECT结构返回区域坐标
成功非零,失败为0
MLM_GETCURSEL 获得当前选择
mov eax,[ebx].iSel
直接返回当前选择的索引项目
MLM_SETCURSEL 设置当前选择
mov edi,wParam
.if sdword ptr edi<0 || edi>=[ebx].iCount
mov edi,-1
.endif
.if lParam & MSC_SETFOCUS
invoke SetFocus,hList
.endif
invoke ParentNotify,hList,NMN_SELCHANGING,edi
.if eax==0
.if lParam & MSC_SETVISIBLE && edi !=-1
invoke SendMessage,hList,MLM_ITEMVISIBLE,edi,0
.endif
invoke SendMessage,hList,MLM_GETITEMRECT,[ebx].iSel,addr rt
mov esi,[ebx].iSel
mov [ebx].iSel,edi
.if eax==TRUE
invoke DrawItem,hList,0,ebx,esi,addr rt,fStyle
.endif
invoke SendMessage,hList,MLM_GETITEMRECT,[ebx].iSel,addr rt
.if eax==TRUE
invoke DrawItem,hList,0,ebx,[ebx].iSel,addr rt,fStyle
.endif
invoke UpdateWindow,hList
invoke ParentNotify,hList,MLN_SELCHANGED,esi
.endif
mov eax,edi
设置当前选择项目.成功返回当前选择。wParam为索引,lParam指定设置风格
如果不是有效的项目索引,将自动设置为未选择状态。
在设置之前,会发送消息给父窗口,如果返回值非零,则取消之后的操作。
否则刷新并设置新的选择索引。这里为什么需要获得区域并自己画呢?这就是初步的无闪烁刷新处理了,如果每次更改之后,都全部刷新,在操作快的时候,容易看出闪动,这里这样处理,就算是重绘了二次,但是不会闪烁。
MLM_ITEMVISIBLE 确保指定项目是可见的
mov edi,wParam
mov eax,[ebx].rt.bottom
xor edx,edx
div [ebx].iHeight
mov ecx,[ebx].iTop
mov edx,eax
add edx,ecx
.if edi>=ecx && edi<=edx ;已经是可视的
xor eax,eax
jmp @@Ret
.elseif edi>=edx
mov ecx,edi
sub ecx,eax
inc ecx
.else
mov ecx,edi
.endif
mov sci.nPos,ecx
call @@SetScrollPos
invoke InvalidateRect,hList,0,0
mov eax,TRUE
wParam指定索引,判断后,如果项目不可见,将它设置为可见。
如果已经是可见的,返回0,否则返回TRUE
MLM_SETHEIGHT || uMsg==MLM_GETHEIGHT 设置高度/获得高度
mov eax,[ebx].iHeight
.if uMsg==MLM_SETHEIGHT
mov ecx,lParam
.if ecx<MIN_HEIGHT
mov ecx,MIN_HEIGHT
.endif
mov [ebx].iHeight,ecx
invoke SendMessage,hList,WM_SIZE,0,0
.endif
设置高度或获得高度,返回以前的高度值。之后WM_SIZE重新设置滚动条。
MLM_GETCOUNT 获得项目总数
mov eax,[ebx].iCount
直接返回当前项目的总个数。0表示没有。
MLM_SETDATA 设置用户数据
mov eax,[ebx].dwData
mov ecx,lParam
mov [ebx].dwData,ecx
返回窗口用户数据。注意这里的是窗口数据,而不是项目的。
MLM_GETDATA 获得窗口数据
mov eax,[ebx].dwData
直接返回当前数据
WM_DESTROY ;控件销毁
.if !([ebx].fStyle & MLFS_SHARED)
invoke ImageList_Destroy,[ebx].hImage
.endif
invoke HeapDestroy,[ebx].hHeap
invoke LocalFree,ebx
;所有使用的内存全部是在这个堆中分配的,只需要销毁这个堆即可.同时,如果图像列表不是共享的,删除它。
到这里为止,控件就差不多了。大家可详细地使用一下。参照说明文档,也即是Control.inc文件,即可知道控件是很好用的。几乎不需要特别地学习,只要以前有SDK控件使用经历,花上十来分钟,这个控件你就会用了,费了这么多心思写标准控件,其实是为了方便使用者。
事实上,本系列的教程,其实没有什么技术含量,对于有基础的朋友来说,这文章不看也罢,直接看代码去了,因为就算是代码,也不过是基础的Windows编程的组合,看几遍,就可以自己动手干了。前面也有说,在这个控件写之前,其实我写的是扩展与增强上一章的控件的,但是思来想去,还是决定再写一个新控件,因为新控件至少还告诉大家如何操作多元素。
我在考虑,这一章是否是系列的完结,因为我感觉有点费力不讨好,要写得太简单了,没看头,写得太复杂了,初学的朋友也看不懂,最主要是的,写控件涉及到各方各面,如果不解释涉及的部分,感觉不行。如果要解释,那可不是一般的难。。郁闷啊。。。
下一期,我看情况,如果有好的灵感,我就再接着写一篇,如果实在是不知道写什么,或者说是如何写,就暂时到此为止,有任何问题大家可到论坛提出。最重要的是,如果你真的想写控件,那一定得动手才行。可不是当成泡菜尝过就没了。。。
再留一个习题吧.
列表控件没有插入操作的功能,添加消息功能,MLM_INSERTADDSTRING,MLM_INSERTITEM,在指定的位置插入项目。
在项目结构中添加一个新成员,保存项目的状态。然后添加一个窗口风格 MLS_MULSELECT,并实现多选功能。这个有点难度。但是稍想一想,要实现其实不难。呵呵,当然关键在于,这里的变化,需要多个地方更改。练练手吧。增强一下,这个控件就可以直接拿来使用了。呵呵。
--------------------------------------------------------------------------------
<<<上一篇 欢迎访问AoGo汇编小站:http://www.aogosoft.com 下一篇>>>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -