Delphi中实现ListView滚动条的换肤方案

  • 概述
  • 代码

概述

首先是要骗过WM_NCPAINT消息。这个十分容易。WM_NCPAINT消息的wParam是一个区域的句柄。当它不为1时,从它里面CLIP掉滚动条的区域,再传给原窗口过程即可。当它为1时,创建一个包含控件全客户区域的Region,再从中CLIP掉滚动条的区域,传给原窗口过程。
然后是WM_HSCROLL和WM_VSCROLL消息。在调用原窗口过程之前需要去掉窗口的WS_HSCROLL和WS_VSCROLL样式,否则窗口过程就会在消息中绘制滚动条。调用后需要恢复。同时为避免窗口在WM_STYLECHANGING和WM_STYLECHANGED消息中重绘,也需要截获这两个消息。
WM_NCCALCSIZE消息也是必须截获的。如果是在处理WM_HSCROLL和WM_VSCROLL消息的过程中响应WM_NCCALCSIZE,则必须去掉WS_HSCROLL和WS_VSCROLL样式。
然后是WM_ERASEBACKGROUND,WM_MOUSEWHELL消息。在这消息后需要重绘滚动条。
最重要的莫过于WM_NCHITTEST消息了。因为是自绘,所以滚动条的按下和拖动都必须在这里处理。
在自己写的滚动条Track函数中,最头疼的莫过于ThumbTrack了。当你计算好滚动到的绝对位置后,用SendMessage(hWnd, WM_XSCROLL, MAKEWPARAM(SB_THUMBTRACK, Pos), 0)发给窗口时,它居然没有反应。这是因为窗口过程不会从消息中取得TrackPos,而是会调用GetScrollInfo的API取得TrackPos(因为前者只有16位)。但是使用SetScrollInfo是没办法设置TrackPos的。虽然你可以用SIF_POS标志让它同时设置Pos和TrackPos,但当Pos等于TrackPos时,窗口过程不会做任何响应。从windows源代码中我们可以了解到,TrackPos并不会为每个窗口保存一份,实际上,在任一时刻最多只有一个滚动条在做ThumbTrack的操作,因此系统只需要用一个全局变量来保存就可以了。
解决这个问题的办法是HookAPI。在GetScrollInfo中返回我们自己的TrackPos。要注意的是要Hook的不是本模块的API,而是ComCtl32.dll中的GetScrollInfo。因此简单的如往@GetScrollInfo地址写几句跳转的方法是行不通的。必须遍历ComCtl32.dll的pe头。这种技术在很多文章中都有描述。
不多说了,以下是Delphi代码,要点在前面已有描述,源码中没有做特殊说明。
使用说明:
资源中是一张横条的192*16的位图,从左到右依次是:左箭头、右箭头、上箭头、下箭头、左箭头按下、右箭头按下、上箭头按下、下箭头按下、横Thumb条、纵Thumb条、横背景条、纵背景条。
初始化时,调用GetSkinSB.InitSkinSB(ListView1.Handle);即可。窗口销毁前调用GetSkinSB.UninitSkinSB(ListView1.Handle)。
虽然也可针对EDIT(TMemo)和其它使用系统滚动条的控件使用此模块,但效果各有差异,需要分别做特殊处理。
补充:使用此方法后,在调用SetScrollInfo后也必须调用RedrawScrollBars重绘滚动条。Hook本模块的SetScrollInfo API是个好方法。
原文转载自:https://www.cnblogs.com/spiritofcloud/p/3980382.html

代码

unit SkinSB;interfaceusesSysUtils, Classes, Windows, Messages, Graphics;constSKINSB_PROP = '{8BC6661E-5880-4353-878D-C3B3784CFC5F}';typeTBarPosCode = (bpcNone, bpcHArrowL, bpcHArrowR, bpcHPageL, bpcHPageR,bpcHThumb, bpcVArrowU, bpcVArrowD, bpcVPageU, bpcVPageD, bpcVThumb,bpcCross);TWindowProc = function(hWnd: hWnd; uMsg: UINT; wParam: wParam; lParam: lParam): LRESULT; stdcall;PSkinSBInfo = ^TSkinSBInfo;TSkinSBInfo = packed recordOldWndProc: TWindowProc;Prevent: Boolean; // prevent style change messageScrolling: Boolean;Style: Cardinal; // real styleThumbTrack: Boolean;ThumbPos: Integer;Tracking: Boolean; // tracking: click arrow or track thumbend;TSkinSB = classprotectedFBitmap: TBitmap;constructor CreateInstance;publicconstructor Create;destructor Destroy; override;procedure InitSkinSB(H: hWnd);procedure UnInitSkinSB(H: hWnd);procedure DrawElem(H: hWnd; Code: TBarPosCode; R: TRect; Down: Boolean);end;function GetSkinSB: TSkinSB;function SkinSBWndProc(hWnd: hWnd; uMsg: UINT; wParam: wParam; lParam: lParam): LRESULT; stdcall;
function GetSkinSBInfo(hWnd: hWnd): PSkinSBInfo;implementationusesCommCtrl;{$R *.res}varl_SkinSB: TSkinSB;l_SkinSB_Prop: TATOM;typePImageImportDescriptor = ^TImageImportDescriptor;TImageImportDescriptor = packed recordoriginalFirstThunk: DWORD; // or Characteristics: DWORDTimeDateStamp: DWORD;ForwarderChain: DWORD;Name: DWORD;FirstThunk: DWORD;end;PImageChunkData = ^TImageChunkData;TImageChunkData = packed recordcase Integer of0:(ForwarderString: DWORD);1:(Func: DWORD);2:(ordinal: DWORD);3:(AddressOfData: DWORD);end;PImageImportByName = ^TImageImportByName;TImageImportByName = packed recordHint: Word;Name: array [0 .. 0] of Byte;end;typePHookRec = ^THookRec;THookRec = packed recordOldFunc: Pointer;NewFunc: Pointer;end;var_HookGetScrollInfo: THookRec;procedure HookApiInMod(ImageBase: Cardinal; ApiName: PChar; PHook: PHookRec);
varpidh: PImageDosHeader;pinh: PImageNtHeaders;pSymbolTable: PIMAGEDATADIRECTORY;piid: PImageImportDescriptor;pitd_org, pitd_1st: PImageChunkData;piibn: PImageImportByName;pAPIFunction: Pointer;written, oldAccess: DWORD;
beginif ImageBase = 0 thenExit;pidh := PImageDosHeader(ImageBase);pinh := PImageNtHeaders(DWORD(ImageBase) + Cardinal(pidh^._lfanew));pSymbolTable := @pinh^.OptionalHeader.DataDirectory[1];piid := PImageImportDescriptor(DWORD(ImageBase) +pSymbolTable^.VirtualAddress);repeatpitd_org := PImageChunkData(DWORD(ImageBase) + piid^.originalFirstThunk);pitd_1st := PImageChunkData(DWORD(ImageBase) + piid^.FirstThunk);repeatpiibn := PImageImportByName(DWORD(ImageBase) + LPDWORD(pitd_org)^);pAPIFunction := Pointer(pitd_1st^.Func);if StrComp(ApiName, @piibn^.Name) = 0 thenbeginPHook^.OldFunc := pAPIFunction;VirtualProtect(@(pitd_1st^.Func), SizeOf(DWORD), PAGE_WRITECOPY,oldAccess);WriteProcessMemory(GetCurrentProcess(), @(pitd_1st^.Func),@PHook^.NewFunc, SizeOf(DWORD), written);VirtualProtect(@(pitd_1st^.Func), SizeOf(DWORD), oldAccess, oldAccess);end;Inc(pitd_org);Inc(pitd_1st);until pitd_1st^.Func = 0;Inc(piid);until piid^.FirstThunk + piid^.originalFirstThunk + piid^.ForwarderChain +piid^.Name = 0;
end;function GetSkinSBInfo(hWnd: hWnd): PSkinSBInfo;
beginResult := PSkinSBInfo(GetProp(hWnd, MAKEINTATOM(l_SkinSB_Prop)));
end;function GetSkinSB: TSkinSB;
beginif l_SkinSB = nil thenl_SkinSB := TSkinSB.CreateInstance;Result := l_SkinSB;
end;function CalcScrollBarRect(H: hWnd; nBarCode: Cardinal): TRect;
varStyle, ExStyle: Cardinal;
beginSetRect(Result, 0, 0, 0, 0);Style := GetWindowLong(H, GWL_STYLE);ExStyle := GetWindowLong(H, GWL_EXSTYLE);if (nBarCode = SB_HORZ) and ((Style and WS_HSCROLL) = 0) thenExit;if (nBarCode = SB_VERT) and ((Style and WS_VSCROLL) = 0) thenExit;GetWindowRect(H, Result);OffsetRect(Result, -Result.Left, -Result.Top);if ((ExStyle and WS_EX_DLGMODALFRAME) <> 0) or((ExStyle and WS_EX_CLIENTEDGE) <> 0) thenbeginInflateRect(Result, -GetSystemMetrics(SM_CXEDGE),-GetSystemMetrics(SM_CYEDGE));end;// special: returns the crossif nBarCode = SB_BOTH thenbeginif ((Style and WS_HSCROLL) = 0) or ((Style and WS_VSCROLL) = 0) thenbeginSetRect(Result, 0, 0, 0, 0);Exit;end;Result.Top := Result.Bottom - GetSystemMetrics(SM_CYVSCROLL);if (ExStyle and WS_EX_LEFTSCROLLBAR) <> 0 thenResult.Right := Result.Left + GetSystemMetrics(SM_CXHSCROLL)elseResult.Left := Result.Right - GetSystemMetrics(SM_CXHSCROLL);Exit;end;if nBarCode = SB_HORZ thenbegin// if (ExStyle and WS_EX_TOPSCROLLBAR) <> 0 then Result.Bottom := Result.Top + GetSystemMetrics(SM_CYVSCROLL)Result.Top := Result.Bottom - GetSystemMetrics(SM_CYVSCROLL);if ((Style and WS_VSCROLL) <> 0) thenDec(Result.Right, GetSystemMetrics(SM_CYVSCROLL));end;if nBarCode = SB_VERT thenbeginif (ExStyle and WS_EX_LEFTSCROLLBAR) <> 0 thenResult.Right := Result.Left + GetSystemMetrics(SM_CXHSCROLL)elseResult.Left := Result.Right - GetSystemMetrics(SM_CXHSCROLL);if ((Style and WS_HSCROLL) <> 0) thenDec(Result.Bottom, GetSystemMetrics(SM_CXHSCROLL));end;
end;typeTBarElem = (beArrow1, beBG, beThumb, beArrow2);TBarElemRects = array [TBarElem] of TRect;function CalcBarElemRects(hWnd: hWnd; nBarCode: Integer): TBarElemRects;
varR: TRect;SI: TScrollInfo;ThumbSize: Integer;X, L, H, BlockH, BlockV: Integer;
beginR := CalcScrollBarRect(hWnd, nBarCode);SI.cbSize := SizeOf(SI);SI.fMask := SIF_ALL;GetScrollInfo(hWnd, nBarCode, SI);Result[beArrow1] := R;Result[beArrow2] := R;Result[beBG] := R;Result[beThumb] := R;if nBarCode = SB_VERT thenbeginBlockV := GetSystemMetrics(SM_CYVSCROLL);L := Result[beArrow1].Top + BlockV;H := Result[beArrow2].Bottom - BlockV;Result[beArrow1].Bottom := L;Result[beArrow2].Top := H;// Inc(L);// Dec(H);Result[beBG].Top := L;Result[beBG].Bottom := H;endelsebeginBlockH := GetSystemMetrics(SM_CXHSCROLL);L := Result[beArrow1].Left + BlockH;H := Result[beArrow2].Right - BlockH;Result[beArrow1].Right := L;Result[beArrow2].Left := H;// Inc(L);// Dec(H);Result[beBG].Left := L;Result[beBG].Right := H;end;if SI.nMax - SI.nMin - Integer(SI.nPage) + 1 <= 0 thenbegin// max thumb, no thumbif nBarCode = SB_VERT thenbeginResult[beThumb].Top := L;Result[beThumb].Bottom := H;endelsebeginResult[beThumb].Left := L;Result[beThumb].Right := H;end;Exit;end;ThumbSize := MulDiv(H - L, SI.nPage, SI.nMax - SI.nMin + 1);X := L + MulDiv(SI.nTrackPos, H - ThumbSize - L, SI.nMax - Integer(SI.nPage) -SI.nMin + 1);if nBarCode = SB_VERT thenbeginResult[beThumb].Top := X;Result[beThumb].Bottom := X + ThumbSize;endelsebeginResult[beThumb].Left := X;Result[beThumb].Right := X + ThumbSize;end;
end;function GetPtBarPos(H: hWnd; Pt: TPoint): TBarPosCode;
varR: TRect;BR: TBarElemRects;
beginResult := bpcNone;R := CalcScrollBarRect(H, SB_HORZ);InflateRect(R, GetSystemMetrics(SM_CXEDGE), GetSystemMetrics(SM_CYEDGE));if PtInRect(R, Pt) thenbeginBR := CalcBarElemRects(H, SB_HORZ);if PtInRect(BR[beArrow1], Pt) thenResult := bpcHArrowLelse if PtInRect(BR[beThumb], Pt) thenResult := bpcHThumbelse if PtInRect(BR[beArrow2], Pt) thenResult := bpcHArrowRelse if Pt.X < BR[beThumb].Left thenResult := bpcHPageLelseResult := bpcHPageR;Exit;end;R := CalcScrollBarRect(H, SB_VERT);InflateRect(R, GetSystemMetrics(SM_CXEDGE), GetSystemMetrics(SM_CYEDGE));if PtInRect(R, Pt) thenbeginBR := CalcBarElemRects(H, SB_VERT);if PtInRect(BR[beArrow1], Pt) thenResult := bpcVArrowUelse if PtInRect(BR[beThumb], Pt) thenResult := bpcVThumbelse if PtInRect(BR[beArrow2], Pt) thenResult := bpcVArrowDelse if Pt.Y < BR[beThumb].Top thenResult := bpcVPageUelseResult := bpcVPageD;Exit;end;
end;typeTGetScrollInfoFunc = function(H: hWnd; Code: Integer; var SI: TScrollInfo): Boolean; stdcall;function _SkinSB_GetScrollInfo(H: hWnd; Code: Integer; var SI: TScrollInfo): Boolean; stdcall;
varP: PSkinSBInfo;
beginResult := TGetScrollInfoFunc(_HookGetScrollInfo.OldFunc)(H, Code, SI);P := GetSkinSBInfo(H);if (P <> nil) and P^.ThumbTrack and ((SI.fMask and SIF_TRACKPOS) <> 0) thenbeginSI.nTrackPos := P^.ThumbPos;end;
end;{ TSkinSB }constructor TSkinSB.Create;
beginraise Exception.Create('use GetSkinSB.');
end;constructor TSkinSB.CreateInstance;
begininherited;_HookGetScrollInfo.OldFunc := nil;_HookGetScrollInfo.NewFunc := @_SkinSB_GetScrollInfo;HookApiInMod(GetModuleHandle('comctl32.dll'), 'GetScrollInfo',@_HookGetScrollInfo);FBitmap := TBitmap.Create;FBitmap.LoadFromResourceName(hInstance, 'scrollbar');
end;destructor TSkinSB.Destroy;
beginFreeAndNil(FBitmap);inherited;
end;procedure TSkinSB.DrawElem(H: hWnd; Code: TBarPosCode; R: TRect; Down: Boolean);
varCanvas: TCanvas;
beginCanvas := TCanvas.Create;tryCanvas.Handle := GetWindowDC(H);trycase Code ofbpcHArrowL:beginif Down thenBitBlt(Canvas.Handle, R.Left, R.Top, 16, 16,FBitmap.Canvas.Handle, 64, 0, SRCCOPY)elseBitBlt(Canvas.Handle, R.Left, R.Top, 16, 16,FBitmap.Canvas.Handle, 0, 0, SRCCOPY);Exit;end;bpcHArrowR:beginif Down thenBitBlt(Canvas.Handle, R.Left, R.Top, 16, 16,FBitmap.Canvas.Handle, 80, 0, SRCCOPY)elseBitBlt(Canvas.Handle, R.Left, R.Top, 16, 16,FBitmap.Canvas.Handle, 16, 0, SRCCOPY);Exit;end;bpcHThumb:beginBitBlt(Canvas.Handle, R.Left, R.Top, 2, 16, FBitmap.Canvas.Handle,128, 0, SRCCOPY);BitBlt(Canvas.Handle, R.Right - 2, R.Top, 2, 16,FBitmap.Canvas.Handle, 142, 0, SRCCOPY);StretchBlt(Canvas.Handle, R.Left + 2, R.Top, R.Right - R.Left - 4,16, FBitmap.Canvas.Handle, 130, 0, 12, 16, SRCCOPY);Exit;end;bpcHPageL, bpcHPageR:beginif R.Right - R.Left < 4 thenbeginStretchBlt(Canvas.Handle, R.Left, R.Top, R.Right - R.Left, 16,FBitmap.Canvas.Handle, 160, 0, 16, 16, SRCCOPY);endelsebeginBitBlt(Canvas.Handle, R.Left, R.Top, 2, 16, FBitmap.Canvas.Handle,160, 0, SRCCOPY);BitBlt(Canvas.Handle, R.Right - 2, R.Top, 2, 16,FBitmap.Canvas.Handle, 174, 0, SRCCOPY);StretchBlt(Canvas.Handle, R.Left + 2, R.Top, R.Right - R.Left - 4,16, FBitmap.Canvas.Handle, 162, 0, 12, 16, SRCCOPY);end;Exit;end;bpcVArrowU:beginif Down thenBitBlt(Canvas.Handle, R.Left, R.Top, 16, 16,FBitmap.Canvas.Handle, 96, 0, SRCCOPY)elseBitBlt(Canvas.Handle, R.Left, R.Top, 16, 16,FBitmap.Canvas.Handle, 32, 0, SRCCOPY);Exit;end;bpcVArrowD:beginif Down thenBitBlt(Canvas.Handle, R.Left, R.Top, 16, 16,FBitmap.Canvas.Handle, 112, 0, SRCCOPY)elseBitBlt(Canvas.Handle, R.Left, R.Top, 16, 16,FBitmap.Canvas.Handle, 48, 0, SRCCOPY);Exit;end;bpcVThumb:beginBitBlt(Canvas.Handle, R.Left, R.Top, 16, 2, FBitmap.Canvas.Handle,144, 0, SRCCOPY);BitBlt(Canvas.Handle, R.Left, R.Bottom - 2, 16, 2,FBitmap.Canvas.Handle, 144, 14, SRCCOPY);StretchBlt(Canvas.Handle, R.Left, R.Top + 2, 16,R.Bottom - R.Top - 4, FBitmap.Canvas.Handle, 144, 2, 16,12, SRCCOPY);Exit;end;bpcVPageU, bpcVPageD:beginif R.Bottom - R.Top < 4 thenbeginStretchBlt(Canvas.Handle, R.Left, R.Top, 16, R.Bottom - R.Top,FBitmap.Canvas.Handle, 176, 0, 16, 16, SRCCOPY);endelsebeginBitBlt(Canvas.Handle, R.Left, R.Top, 16, 2, FBitmap.Canvas.Handle,176, 0, SRCCOPY);BitBlt(Canvas.Handle, R.Left, R.Bottom - 2, 16, 2,FBitmap.Canvas.Handle, 176, 14, SRCCOPY);StretchBlt(Canvas.Handle, R.Left, R.Top + 2, 16,R.Bottom - R.Top - 4, FBitmap.Canvas.Handle, 176, 2, 16,12, SRCCOPY);end;Exit;end;end;Canvas.Pen.Color := clBlack;Canvas.Brush.Color := clWhite;Canvas.Rectangle(R);finallyReleaseDC(H, Canvas.Handle);end;finallyCanvas.Handle := 0;FreeAndNil(Canvas);end;
end;procedure TSkinSB.InitSkinSB(H: hWnd);
varPInfo: PSkinSBInfo;
beginPInfo := GetSkinSBInfo(H);if PInfo <> nil thenExit; // already initedNew(PInfo);PInfo^.OldWndProc := TWindowProc(GetWindowLong(H, GWL_WNDPROC));PInfo^.Style := GetWindowLong(H, GWL_STYLE);PInfo^.Prevent := False;PInfo^.Scrolling := False;PInfo^.ThumbTrack := False;SetWindowLong(H, GWL_WNDPROC, Cardinal(@SkinSBWndProc));SetProp(H, MAKEINTATOM(l_SkinSB_Prop), Cardinal(PInfo));
end;procedure TSkinSB.UnInitSkinSB(H: hWnd);
varPInfo: PSkinSBInfo;
beginPInfo := GetSkinSBInfo(H);if PInfo = nil thenExit; // not initedRemoveProp(H, MAKEINTATOM(l_SkinSB_Prop));SetWindowLong(H, GWL_WNDPROC, Cardinal(@PInfo^.OldWndProc));Dispose(PInfo);
end;constWM_REPEAT_CLICK = WM_USER + $6478;procedure OnRepeatClickTimer(hWnd: hWnd; uMsg: UINT; idEvent: UINT;dwTime: DWORD); stdcall;
beginKillTimer(0, idEvent);PostThreadMessage(MainThreadID, WM_REPEAT_CLICK, 0, 0);
end;procedure RedrawScrollBars(hWnd: hWnd);
varRHBar, RVBar, RCross: TRect;BR: TBarElemRects;
beginRHBar := CalcScrollBarRect(hWnd, SB_HORZ);if not IsRectEmpty(RHBar) thenbeginBR := CalcBarElemRects(hWnd, SB_HORZ);GetSkinSB.DrawElem(hWnd, bpcHPageL, Rect(BR[beBG].Left, BR[beBG].Top,BR[beThumb].Left, BR[beBG].Bottom), False);GetSkinSB.DrawElem(hWnd, bpcHPageR, Rect(BR[beThumb].Right, BR[beBG].Top,BR[beBG].Right, BR[beBG].Bottom), False);GetSkinSB.DrawElem(hWnd, bpcHThumb, BR[beThumb], False);GetSkinSB.DrawElem(hWnd, bpcHArrowL, BR[beArrow1], False);GetSkinSB.DrawElem(hWnd, bpcHArrowR, BR[beArrow2], False);end;RVBar := CalcScrollBarRect(hWnd, SB_VERT);if not IsRectEmpty(RVBar) thenbeginBR := CalcBarElemRects(hWnd, SB_VERT);GetSkinSB.DrawElem(hWnd, bpcVPageU, Rect(BR[beBG].Left, BR[beBG].Top,BR[beBG].Right, BR[beThumb].Top), False);GetSkinSB.DrawElem(hWnd, bpcVPageD, Rect(BR[beBG].Left, BR[beThumb].Bottom,BR[beBG].Right, BR[beBG].Bottom), False);GetSkinSB.DrawElem(hWnd, bpcVThumb, BR[beThumb], False);GetSkinSB.DrawElem(hWnd, bpcVArrowU, BR[beArrow1], False);GetSkinSB.DrawElem(hWnd, bpcVArrowD, BR[beArrow2], False);end;RCross := CalcScrollBarRect(hWnd, SB_BOTH);if not IsRectEmpty(RCross) thenbeginGetSkinSB.DrawElem(hWnd, bpcCross, RCross, False);end;
end;procedure TrackBar(hWnd: hWnd; nBarCode: Integer; PosCode: TBarPosCode;BarElem: TBarElem; MsgCode: Integer);
varBR: TBarElemRects;Msg: tagMSG;Pt: TPoint;R: TRect;ScrollMsg: Cardinal;RepeatClick: Boolean;idEvent: UINT;SI: TScrollInfo;procedure RefreshRect;beginBR := CalcBarElemRects(hWnd, nBarCode);R := BR[BarElem];end;beginRepeatClick := False;BR := CalcBarElemRects(hWnd, nBarCode);R := BR[BarElem];GetScrollInfo(hWnd, nBarCode, SI);if nBarCode = SB_HORZ thenScrollMsg := WM_HSCROLLelseScrollMsg := WM_VSCROLL;if BarElem = beBG thenbeginif PosCode = bpcHPageL thenR.Right := BR[beThumb].Leftelse if PosCode = bpcHPageR thenR.Left := BR[beThumb].Rightelse if PosCode = bpcVPageU thenR.Bottom := BR[beThumb].Topelse if PosCode = bpcVPageD thenR.Top := BR[beThumb].Bottom;end;GetSkinSB.DrawElem(hWnd, PosCode, R, True);GetSkinSBInfo(hWnd)^.Tracking := True;idEvent := 0;trySetCapture(hWnd);idEvent := SetTimer(0, 0, 1000, @OnRepeatClickTimer);while GetCapture = hWnd dobeginif not GetMessage(Msg, 0, 0, 0) thenBreak;if (Msg.hWnd = 0) and (Msg.message = WM_REPEAT_CLICK) thenbeginGetCursorPos(Pt);ScreenToClient(hWnd, Pt);if PtInRect(R, Pt) thenbeginRepeatClick := True;SendMessage(hWnd, ScrollMsg, MsgCode, 0);SendMessage(hWnd, ScrollMsg, SB_ENDSCROLL, 0);RefreshRect;GetSkinSB.DrawElem(hWnd, PosCode, R, True);// if MsgCode = SB_LINEDOWN then SetScrollPos(hWnd, nBarCode, GetScrollPos(hWnd, nBarCode) + 1, False);if MsgCode = SB_PAGEDOWN thenSetScrollPos(hWnd, nBarCode, GetScrollPos(hWnd, nBarCode) +Integer(SI.nPage), False);// if MsgCode = SB_LINEUP then SetScrollPos(hWnd, nBarCode, GetScrollPos(hWnd, nBarCode) - 1, False);if MsgCode = SB_PAGEUP thenSetScrollPos(hWnd, nBarCode, GetScrollPos(hWnd, nBarCode) -Integer(SI.nPage), False);RedrawScrollBars(hWnd);SetTimer(0, 0, 80, @OnRepeatClickTimer);end;endelse if Msg.hWnd = hWnd thenbegincase Msg.message ofWM_LBUTTONUP:beginif RepeatClick thenBreak;GetCursorPos(Pt);ScreenToClient(hWnd, Pt);if PtInRect(R, Pt) thenbeginSendMessage(hWnd, ScrollMsg, MsgCode, 0);SendMessage(hWnd, ScrollMsg, SB_ENDSCROLL, 0);RefreshRect;// if MsgCode = SB_LINEDOWN then SetScrollPos(hWnd, nBarCode, GetScrollPos(hWnd, nBarCode) + 1, False);if MsgCode = SB_PAGEDOWN thenSetScrollPos(hWnd, nBarCode, GetScrollPos(hWnd, nBarCode) +Integer(SI.nPage), False);// if MsgCode = SB_LINEUP then SetScrollPos(hWnd, nBarCode, GetScrollPos(hWnd, nBarCode) - 1, False);if MsgCode = SB_PAGEUP thenSetScrollPos(hWnd, nBarCode, GetScrollPos(hWnd, nBarCode) -Integer(SI.nPage), False);end;Break;end;end;end;DispatchMessage(Msg);end;finallyif idEvent <> 0 thenKillTimer(0, idEvent);if IsWindow(hWnd) thenbeginif GetCapture = hWnd thenReleaseCapture;GetSkinSB.DrawElem(hWnd, PosCode, R, False);GetSkinSBInfo(hWnd)^.Tracking := False;end;end;
end;procedure TrackThumb(hWnd: hWnd; nBarCode: Integer; PosCode: TBarPosCode;BarElem: TBarElem);
varBR: TBarElemRects;Msg: tagMSG;Pt: TPoint;DragX: Integer;R: TRect;ScrollMsg: Cardinal;SI, SI2: TScrollInfo;Pos: Integer;H, L, ThumbSize, X: Integer;Pushed: Boolean;function ValidDragArea(ARect: TRect; APt: TPoint): Boolean;beginif nBarCode = SB_HORZ thenResult := Abs((ARect.Bottom + ARect.Top) div 2 - APt.Y) < 150elseResult := Abs((ARect.Left + ARect.Right) div 2 - APt.X) < 150;end;function CalcPos(ARect: TRect; APt: TPoint; ADragX: Integer): Integer;varNewX: Integer;beginif nBarCode = SB_HORZ thenNewX := APt.X - ADragXelseNewX := APt.Y - ADragX;Result := SI.nMin + MulDiv(NewX - L, SI.nMax - Integer(SI.nPage) - SI.nMin +1, H - L - ThumbSize);if Result < SI.nMin thenResult := SI.nMin;if Result > SI.nMax - Integer(SI.nPage) + 1 thenResult := SI.nMax - Integer(SI.nPage) + 1;end;procedure UpdateDragBar(ADown: Boolean; APos: Integer = -10000);varW: Integer;beginBR := CalcBarElemRects(hWnd, nBarCode);R := BR[BarElem];if nBarCode = SB_HORZ thenbeginif APos <> -10000 thenbeginW := R.Right - R.Left;if APos < BR[beArrow1].Right thenAPos := BR[beArrow1].Right;if APos + W > BR[beArrow2].Left thenAPos := BR[beArrow2].Left - W;R.Left := APos;R.Right := APos + W;end;GetSkinSB.DrawElem(hWnd, bpcHPageL, Rect(BR[beBG].Left, BR[beBG].Top,R.Left, BR[beBG].Bottom), False);GetSkinSB.DrawElem(hWnd, bpcHPageR, Rect(R.Right, BR[beBG].Top,BR[beBG].Right, BR[beBG].Bottom), False);endelsebeginif APos <> -10000 thenbeginW := R.Bottom - R.Top;if APos < BR[beArrow1].Bottom thenAPos := BR[beArrow1].Bottom;if APos + W >= BR[beArrow2].Top thenAPos := BR[beArrow2].Top - W - 1;R.Top := APos;R.Bottom := APos + W;end;GetSkinSB.DrawElem(hWnd, bpcVPageU, Rect(BR[beBG].Left, BR[beBG].Top,BR[beBG].Right, R.Top), False);GetSkinSB.DrawElem(hWnd, bpcVPageD, Rect(BR[beBG].Left, R.Bottom,BR[beBG].Right, BR[beBG].Bottom), False);end;GetSkinSB.DrawElem(hWnd, PosCode, R, ADown);OutputDebugString(PChar(Format('R=(%d,%d,%d,%d)', [R.Left, R.Top, R.Right,R.Bottom])));end;beginBR := CalcBarElemRects(hWnd, nBarCode);R := BR[BarElem];if nBarCode = SB_HORZ thenScrollMsg := WM_HSCROLLelseScrollMsg := WM_VSCROLL;SI.cbSize := SizeOf(SI);SI.fMask := SIF_ALL;GetScrollInfo(hWnd, nBarCode, SI);GetCursorPos(Pt);ScreenToClient(hWnd, Pt);if nBarCode = SB_HORZ thenbeginDragX := Pt.X - BR[beThumb].Left;ThumbSize := BR[beThumb].Right - BR[beThumb].Left;L := BR[beArrow1].Right;H := BR[beArrow2].Left;endelsebeginDragX := Pt.Y - BR[beThumb].Top;ThumbSize := BR[beThumb].Bottom - BR[beThumb].Top;L := BR[beArrow1].Bottom;H := BR[beArrow2].Top;end;{ if nBarCode = SB_HORZ then SendMessage(hWnd, WM_SYSCOMMAND, SC_HSCROLL, MAKELPARAM(Pt.X, Pt.Y))else SendMessage(hWnd, WM_SYSCOMMAND, SC_VSCROLL, MAKELPARAM(Pt.X, Pt.Y)); }GetSkinSBInfo(hWnd)^.Tracking := True;UpdateDragBar(True);trySetCapture(hWnd);while GetCapture = hWnd dobeginif not GetMessage(Msg, 0, 0, 0) thenBreak;if Msg.hWnd = hWnd thenbegincase Msg.message ofWM_MOUSEMOVE:beginPushed := ValidDragArea(R, Pt);GetCursorPos(Pt);ScreenToClient(hWnd, Pt);if ValidDragArea(R, Pt) thenbeginPos := CalcPos(R, Pt, DragX);if nBarCode = SB_HORZ thenX := Pt.X - DragXelseX := Pt.Y - DragX;endelsebeginPos := SI.nPos;X := DragX;end;GetSkinSBInfo(hWnd)^.ThumbPos := Pos;GetSkinSBInfo(hWnd)^.ThumbTrack := True;SendMessage(hWnd, ScrollMsg, MAKEWPARAM(SB_THUMBTRACK, Pos), 0);GetSkinSBInfo(hWnd)^.ThumbTrack := False;UpdateDragBar(Pushed, X);end;WM_LBUTTONUP:beginGetCursorPos(Pt);ScreenToClient(hWnd, Pt);if ValidDragArea(R, Pt) thenbeginPos := CalcPos(R, Pt, DragX);SI2.cbSize := SizeOf(SI2);SI2.fMask := SIF_ALL;GetScrollInfo(hWnd, nBarCode, SI2);SI2.nPos := Pos;SI2.nTrackPos := Pos;SetScrollInfo(hWnd, nBarCode, SI2, False);SI2.nTrackPos := 0;SI2.nPos := 0;GetScrollInfo(hWnd, nBarCode, SI2);SendMessage(hWnd, ScrollMsg,MAKEWPARAM(SB_THUMBPOSITION, Pos), 0);SendMessage(hWnd, ScrollMsg, SB_ENDSCROLL, 0);end;Break;end;end;end;DispatchMessage(Msg);end;finallyif IsWindow(hWnd) thenbeginif GetCapture = hWnd thenReleaseCapture;GetSkinSBInfo(hWnd)^.Tracking := False;end;UpdateDragBar(False);end;
end;function SkinSBWndProc(hWnd: hWnd; uMsg: UINT; wParam: wParam;lParam: lParam): LRESULT;
varPInfo: PSkinSBInfo;Style, ExStyle: Cardinal;R, RHBar, RVBar, RCross: TRect;Pt: TPoint;Rgn, Rgn2: HRGN;PR: PRect;BR: TBarElemRects;XBar, YBar: Integer;
beginPInfo := GetSkinSBInfo(hWnd);if PInfo = nil thenResult := DefWindowProc(hWnd, uMsg, wParam, lParam)/// / error!!!elsebegincase uMsg ofWM_NCHITTEST:beginGetCursorPos(Pt);ScreenToClient(hWnd, Pt);case GetPtBarPos(hWnd, Pt) ofbpcHArrowL:beginif (GetKeyState(VK_LBUTTON) and $8000) <> 0 thenbeginif GetCapture <> hWnd thenTrackBar(hWnd, SB_HORZ, bpcHArrowL, beArrow1, SB_LINELEFT);end;Result := HTNOWhere;Exit;end;bpcHArrowR:beginif (GetKeyState(VK_LBUTTON) and $8000) <> 0 thenbeginif GetCapture <> hWnd thenTrackBar(hWnd, SB_HORZ, bpcHArrowR, beArrow2, SB_LINERIGHT);end;Result := HTNOWhere;Exit;end;bpcHPageL:beginif (GetKeyState(VK_LBUTTON) and $8000) <> 0 thenbeginif GetCapture <> hWnd thenbeginTrackBar(hWnd, SB_HORZ, bpcHPageL, beBG, SB_PAGELEFT);RedrawScrollBars(hWnd);end;end;Result := HTNOWhere;Exit;end;bpcHPageR:beginif (GetKeyState(VK_LBUTTON) and $8000) <> 0 thenbeginif GetCapture <> hWnd thenbeginTrackBar(hWnd, SB_HORZ, bpcHPageR, beBG, SB_PAGERIGHT);RedrawScrollBars(hWnd);end;end;Result := HTNOWhere;Exit;end;bpcHThumb:beginif (GetKeyState(VK_LBUTTON) and $8000) <> 0 thenbeginif GetCapture <> hWnd thenTrackThumb(hWnd, SB_HORZ, bpcHThumb, beThumb);end;Result := HTNOWhere;Exit;end;bpcVArrowU:beginif (GetKeyState(VK_LBUTTON) and $8000) <> 0 thenbeginif GetCapture <> hWnd thenTrackBar(hWnd, SB_VERT, bpcVArrowU, beArrow1, SB_LINELEFT);end;Result := HTNOWhere;Exit;end;bpcVArrowD:beginif (GetKeyState(VK_LBUTTON) and $8000) <> 0 thenbeginif GetCapture <> hWnd thenTrackBar(hWnd, SB_VERT, bpcVArrowD, beArrow2, SB_LINERIGHT);end;Result := HTNOWhere;Exit;end;bpcVPageU:beginif (GetKeyState(VK_LBUTTON) and $8000) <> 0 thenbeginif GetCapture <> hWnd thenbeginTrackBar(hWnd, SB_VERT, bpcVPageU, beBG, SB_PAGELEFT);RedrawScrollBars(hWnd);end;end;Result := HTNOWhere;Exit;end;bpcVPageD:beginif (GetKeyState(VK_LBUTTON) and $8000) <> 0 thenbeginif GetCapture <> hWnd thenbeginTrackBar(hWnd, SB_VERT, bpcVPageD, beBG, SB_PAGERIGHT);RedrawScrollBars(hWnd);end;end;Result := HTNOWhere;Exit;end;bpcVThumb:beginif (GetKeyState(VK_LBUTTON) and $8000) <> 0 thenbeginif GetCapture <> hWnd thenTrackThumb(hWnd, SB_VERT, bpcVThumb, beThumb);end;Result := HTNOWhere;Exit;end;end;end;WM_HSCROLL:beginPInfo^.Scrolling := True;Style := GetWindowLong(hWnd, GWL_STYLE);PInfo^.Style := Style;PInfo^.Prevent := True;trySetWindowLong(hWnd, GWL_STYLE,Style and (not(WS_VSCROLL or WS_HSCROLL)));finallyPInfo^.Prevent := False;end;Result := CallWindowProc(@PInfo^.OldWndProc, hWnd, uMsg,wParam, lParam);RedrawScrollBars(hWnd);PInfo^.Prevent := True;trySetWindowLong(hWnd, GWL_STYLE, Style);finallyPInfo^.Prevent := False;end;PInfo^.Scrolling := False;Exit;end;WM_VSCROLL:beginPInfo^.Scrolling := True;Style := GetWindowLong(hWnd, GWL_STYLE);PInfo^.Style := Style;PInfo^.Prevent := True;trySetWindowLong(hWnd, GWL_STYLE,Style and (not(WS_VSCROLL or WS_HSCROLL)));finallyPInfo^.Prevent := False;end;Result := CallWindowProc(@PInfo^.OldWndProc, hWnd, uMsg,wParam, lParam);PInfo^.Prevent := True;trySetWindowLong(hWnd, GWL_STYLE, Style);finallyPInfo^.Prevent := False;end;PInfo^.Scrolling := False;Exit;end;WM_STYLECHANGED:beginif wParam = GWL_STYLE thenbeginif PInfo^.Prevent thenbeginResult := 0;Exit;endelsebeginPInfo^.Style := GetWindowLong(hWnd, GWL_STYLE);end;end;end;WM_NCCALCSIZE:beginStyle := GetWindowLong(hWnd, GWL_STYLE);ExStyle := GetWindowLong(hWnd, GWL_EXSTYLE);XBar := GetSystemMetrics(SM_CXVSCROLL);YBar := GetSystemMetrics(SM_CYHSCROLL);if PInfo^.Scrolling thenbeginPInfo^.Prevent := True;trySetWindowLong(hWnd, GWL_STYLE,Style and (not(WS_HSCROLL or WS_VSCROLL))); // real stylefinallyPInfo^.Prevent := False;end;end;Result := CallWindowProc(@PInfo^.OldWndProc, hWnd, uMsg,wParam, lParam);if PInfo^.Scrolling thenbeginPR := PRect(lParam);if (PInfo^.Style and WS_VSCROLL) <> 0 thenbeginif (ExStyle and WS_EX_LEFTSCROLLBAR) <> 0 thenInc(PR^.Left, XBar)elseDec(PR^.Right, XBar);end;if (PInfo^.Style and WS_HSCROLL) <> 0 thenbeginDec(PR^.Bottom, YBar);end;end;if PInfo^.Scrolling thenbeginPInfo^.Prevent := True;trySetWindowLong(hWnd, GWL_STYLE, Style); // old stylefinallyPInfo^.Prevent := False;end;end;Exit;end;WM_NCPAINT:beginGetWindowRect(hWnd, R);Pt := R.TopLeft;if wParam = 1 thenbeginRgn := CreateRectRgn(Pt.X, Pt.Y, Pt.X + R.Right, Pt.Y + R.Bottom);endelseRgn := wParam;RHBar := CalcScrollBarRect(hWnd, SB_HORZ);OffsetRect(RHBar, Pt.X, Pt.Y);if not IsRectEmpty(RHBar) thenbeginBR := CalcBarElemRects(hWnd, SB_HORZ);GetSkinSB.DrawElem(hWnd, bpcHPageL,Rect(BR[beBG].Left, BR[beBG].Top, BR[beThumb].Left,BR[beBG].Bottom), False);GetSkinSB.DrawElem(hWnd, bpcHPageR,Rect(BR[beThumb].Right, BR[beBG].Top, BR[beBG].Right,BR[beBG].Bottom), False);GetSkinSB.DrawElem(hWnd, bpcHThumb, BR[beThumb], False);GetSkinSB.DrawElem(hWnd, bpcHArrowL, BR[beArrow1], False);GetSkinSB.DrawElem(hWnd, bpcHArrowR, BR[beArrow2], False);end;Rgn2 := CreateRectRgn(RHBar.Left, RHBar.Top, RHBar.Right,RHBar.Bottom);CombineRgn(Rgn, Rgn, Rgn2, RGN_DIFF);DeleteObject(Rgn2);RVBar := CalcScrollBarRect(hWnd, SB_VERT);if not IsRectEmpty(RVBar) thenbeginBR := CalcBarElemRects(hWnd, SB_VERT);GetSkinSB.DrawElem(hWnd, bpcVPageU,Rect(BR[beBG].Left, BR[beBG].Top, BR[beBG].Right,BR[beThumb].Top), False);GetSkinSB.DrawElem(hWnd, bpcVPageD,Rect(BR[beBG].Left, BR[beThumb].Bottom, BR[beBG].Right,BR[beBG].Bottom), False);GetSkinSB.DrawElem(hWnd, bpcVThumb, BR[beThumb], False);GetSkinSB.DrawElem(hWnd, bpcVArrowU, BR[beArrow1], False);GetSkinSB.DrawElem(hWnd, bpcVArrowD, BR[beArrow2], False);end;OffsetRect(RVBar, Pt.X, Pt.Y);Rgn2 := CreateRectRgn(RVBar.Left, RVBar.Top, RVBar.Right,RVBar.Bottom);CombineRgn(Rgn, Rgn, Rgn2, RGN_DIFF);DeleteObject(Rgn2);RCross := CalcScrollBarRect(hWnd, SB_BOTH);if not IsRectEmpty(RCross) thenbeginGetSkinSB.DrawElem(hWnd, bpcCross, RCross, False);end;OffsetRect(RCross, Pt.X, Pt.Y);Rgn2 := CreateRectRgn(RCross.Left, RCross.Top, RCross.Right,RCross.Bottom);CombineRgn(Rgn, Rgn, Rgn2, RGN_DIFF);DeleteObject(Rgn2);Result := CallWindowProc(@PInfo^.OldWndProc, hWnd, uMsg, Rgn, lParam);if wParam = 1 thenDeleteObject(Rgn);Exit;end;WM_ERASEBKGND:beginStyle := GetWindowLong(hWnd, GWL_STYLE);PInfo^.Prevent := True;trySetWindowLong(hWnd, GWL_STYLE,Style and (not(WS_VSCROLL or WS_HSCROLL)));finallyPInfo^.Prevent := False;end;Result := CallWindowProc(@PInfo^.OldWndProc, hWnd, uMsg,wParam, lParam);PInfo^.Prevent := True;trySetWindowLong(hWnd, GWL_STYLE, Style); // old stylefinallyPInfo^.Prevent := False;end;Exit;end;WM_MOUSEWHEEL, WM_MOUSEMOVE:beginResult := CallWindowProc(@PInfo^.OldWndProc, hWnd, uMsg,wParam, lParam);if PInfo^.Tracking thenExit;if (uMsg = WM_MOUSEMOVE) and ((wParam and MK_LBUTTON) = 0) thenExit;RedrawScrollBars(hWnd);Exit;end;end;Result := CallWindowProc(@PInfo^.OldWndProc, hWnd, uMsg, wParam, lParam);end;
end;initializationl_SkinSB := nil;
l_SkinSB_Prop := GlobalAddAtom(SKINSB_PROP);finalizationif Assigned(l_SkinSB) thenFreeAndNil(l_SkinSB);end.

Delphi中实现ListView滚动条的换肤方案相关推荐

  1. 换肤方案,换肤策略,App插件式换肤实现方案

    UI换皮肤或白天黑夜模式,从产品上来看,是两种不同产品设计模式:白天黑夜模式只有两种模式:而换皮肤可以有多套,可以进行商业化,并盈利. 换肤的本质就是去替换资源文件.我们知道,Android应用程序由 ...

  2. Android可更换布局的换肤方案

    换肤,顾名思义,就是对应用中的视觉元素进行更新,呈现新的显示效果.一般来说,换肤的时候只是更新UI上使用的资源,如颜色,图片,字体等等.本文介绍一种笔者自己使用的基于布局的Android换肤方案,不仅 ...

  3. Android 主题切换/换肤方案 研究(四) - qq和qq空间

    4. qq和qq空间 (独立app) 分析时用的是: 1. 夜神android模拟器(因为用android studio自带的模拟器运行x86架构的镜像提示不能安装qq空间,安装arm架构的镜像运行又 ...

  4. Android 应用换肤方案的总结

    虽然现在已经有很多不错的换肤方案,但是这些方案或多或少都存在自己的问题.在这篇文章中,我将对 Android 现有的一些动态换肤方案进行梳理,对其底层实现原理进行分析,然后对开发一个新的换肤方案的可能 ...

  5. 对 Android 应用换肤方案的总结

    作者:me 虽然现在已经有很多不错的换肤方案,但是这些方案或多或少都存在自己的问题.在这篇文章中,我将对 Android 现有的一些动态换肤方案进行梳理,对其底层实现原理进行分析,然后对开发一个新的换 ...

  6. 主流技术之网易云换肤方案

    上一篇说到了View的加载过程,并且对TextView进行了拦截,让页面上所有的TextView内容都变成了我们想要的helloworld,这次我们一起来研究一下网易云音乐的动态换肤技术.说到换肤,有 ...

  7. Android 换肤方案详解(一)

    引言 在我们的开发中,也许有些项目会有换肤的需求,这个时候会比较头疼怎么做才能做到一键换肤呢?大家肯定是希望只要一行代码就能调用最好.下面我们先分析一下换肤的本质是什么? 原理 换肤,其本质无非就是更 ...

  8. vue换肤方案scss

    1.配置主题定义scss:_theme.scss $themes: (blue: (base_color: $blue-1,light_color: rgba($blue-1, .1)),purple ...

  9. iOS客户端节日换肤方案探究

    转自:https://www.ianisme.com的博客 一.前言: tip: 本来这篇文章在圣诞节就已经准备好了,但是由于种种原因一直没有写完,今天将它写出来,也算是2018年的第一篇文章了.你好 ...

  10. iOS拓展---【转载】iOS客户端节日换肤方案探究

    [转载]iOS客户端节日换肤方案探究 一.前言: Tip: 本来这篇文章在圣诞节就已经准备好了,但是由于种种原因一直没有写完,今天将它写出来,也算是2018年的第一篇文章了.你好,2018! 过去圣诞 ...

最新文章

  1. JS中的let和var的区别
  2. oracle 创建表中又有表,oracle创建表
  3. JS向后台传递json数组对象
  4. LeetCode 第 36 场双周赛(304/2204,前13.8%)
  5. table表格的增删查改、图片的处理、数据的导入导出
  6. linux目录名乱码,为什么挂载中文目录或文件名是乱码?
  7. html悬停显示图片,JS实现悬停单元格显示图片
  8. 加入域时出现以下错误 登陆失败 该目标账户名称不正确_Windows 10 20H1新加入的这些功能,你应该用得上...
  9. html2张图片垂直居中,任意图片实现垂直居中的三种方法(兼容性还不错)
  10. BOOST库介绍(四)——文件系统相关
  11. 用微PE安装KALI LINUX到U盘,【U盘安装kali】U盘+kali+pe三合一教程!装机,存储(自己用来做U盘使用的空间)...
  12. 寄生虫技术计算机软件怎么样,2019寄生虫软件-某寄生虫软件分析
  13. 用计算机按45乘5CE再按,2015年4月全国自学考试计算机应用基础真题
  14. masm32汇编基础
  15. Windows平台下GTK 窗口在任务栏的隐藏问题以及解决办法
  16. 关于 VB6 透明 PNG 图像生成的一个解决方案
  17. 上善若水 (9月18日)
  18. Symbol - 看似平凡的Symbol其实我们每天都在用 - 对象操作
  19. jQuery播放音乐
  20. UnsupportedOperationException; ImmutableCollections.uoe

热门文章

  1. docker打包informix镜像
  2. 终点与起点——诺基亚 N9 初步上手
  3. N9(Meego系统)删除自带软件方法------笔者亲测,可行,无副作用
  4. My97DatePicker日历实现开始日期小于结束日期验证
  5. CDH运维常见问题-cloudera-scm-agent 已死,但 pid 文件存在
  6. 编码的奥秘txt_编码的奥秘(Charles Petzold著) PDF扫描版[9MB]
  7. 代理模式———动态代理
  8. NoSQL数据库的介绍、NoSQL的产品、NoSQL数据库的分类等;
  9. 我的高拍仪自动阅卷系统
  10. 大数据Hadoop生态系统介绍