Skip to content

Latest commit

 

History

History
299 lines (257 loc) · 10.5 KB

sample_392.md

File metadata and controls

299 lines (257 loc) · 10.5 KB

Home

Subclassing CommandButton control to create BackColor property

Before you begin:


Each time the caption of a button is set -- use Caption1 property and SetCaption method to do this -- the object generates a temporary bitmap file printing the text on it and pass this file to the Picture property.


Code:

PUBLIC obj
obj = CreateObject("Tform")
obj.Visible = .T.
* end of main

DEFINE CLASS TForm As Form
	Width=500
	Height=200
	Caption=" Tbutton subclassed from CommandButton"
	Autocenter=.T.

	ADD OBJECT Tbutton1 As Tbutton WITH;
	Left=10, Top=10, Width=150, Height=32, Caption1="Tbutton1",;
	BackColor=Rgb(250,230,230), ForeColor=Rgb(192,128,128), FontBold=.T.
	
	ADD OBJECT Tbutton2 As Tbutton WITH;
	Left=170, Top=10, Width=150, Height=32, Caption1="Tbutton2",;
	BackColor=Rgb(230,250,230), FontItalic=.T.

	ADD OBJECT Tbutton3 As Tbutton WITH;
	Left=330, Top=10, Width=150, Height=32, Caption1="Tbutton3",;
	BackColor=Rgb(230,230,250), ForeColor=Rgb(128,128,192), FontBold=.T.

	ADD OBJECT Tbutton4 As Tbutton WITH;
	Left=10, Top=50, Width=150, Height=140,;
	FrameColor=Rgb(230,210,0), BackColor=Rgb(250,230,0),;
	MarginTop=7, MarginBottom=7, MarginLeft=7, MarginRight=7,;
	PaddingTop=10, PaddingLeft=5, PaddingRight=5,;
	Caption1="This class"+Chr(13)+"is a subclass"+Chr(13)+"of the CommandButton"

	ADD OBJECT Tbutton5 As Tbutton WITH;
	Left=170, Top=50, Width=310, Height=48,;
	FrameColor=Rgb(227,199,168), BackColor=Rgb(227,199,168),;
	ForeColor=Rgb(123,84,40), FontBold=.T.,;
	MarginTop=5,;
	Caption1="BackColor property is now available"

	ADD OBJECT Tbutton6 As Tbutton WITH;
	Left=330, Top=110, Width=150, Height=80,;
	Caption1="Close this form",;
	FrameColor=THIS.BackColor, BackColor=Rgb(220,220,220),;
	PaddingTop=20, Cancel=.T.

PROCEDURE Tbutton6.Click
	ThisForm.Release
ENDDEFINE

DEFINE CLASS Tbutton As CommandButton
	FontSize=12
	Caption=""  && keep it empty
	Caption1="" && replaces regular Caption property
	Bitmapfile=""
	FrameColor=Rgb(192,192,192)  && long awaited property
	MarginLeft=2
	MarginTop=2
	MarginRight=2
	MarginBottom=2
	BackColor=Rgb(192,192,192)
	PaddingLeft=2
	PaddingTop=2
	PaddingRight=2
	PaddingBottom=2

PROCEDURE Init
	THIS.Bitmapfile=SUBSTR(SYS(2015), 3) + ".bmp"
	THIS.SetCaption

PROCEDURE Destroy
	IF FILE(THIS.Bitmapfile)
		DELETE FILE (THIS.Bitmapfile)
	ENDIF

PROCEDURE SetCaption(cCaption)
#DEFINE ANSI_CHARSET        0
#DEFINE OUT_OUTLINE_PRECIS  8
#DEFINE CLIP_STROKE_PRECIS  2
#DEFINE PROOF_QUALITY       2
#DEFINE ANTIALIASED_QUALITY 4
#DEFINE DEFAULT_PITCH       0
#DEFINE VARIABLE_PITCH      2
#DEFINE LOGPIXELSY          90
#DEFINE DT_CENTER           1
#DEFINE DT_WORDBREAK        16
	DECLARE INTEGER CreateCompatibleDC IN gdi32 INTEGER hdc
	DECLARE INTEGER GetDesktopWindow IN user32
	DECLARE INTEGER GetWindowDC IN user32 INTEGER hwnd
	DECLARE INTEGER ReleaseDC IN user32 INTEGER hwnd, INTEGER hdc
	DECLARE INTEGER SelectObject IN gdi32 INTEGER hdc, INTEGER hObject
	DECLARE INTEGER DeleteDC IN gdi32 INTEGER hdc
	DECLARE INTEGER DeleteObject IN gdi32 INTEGER hObject
	DECLARE INTEGER CreateSolidBrush IN gdi32 LONG crColor
	DECLARE INTEGER SetBkMode IN gdi32 INTEGER hdc, INTEGER iBkMode
	DECLARE INTEGER SetTextColor IN gdi32 INTEGER hdc, INTEGER crColor
	DECLARE INTEGER GetDeviceCaps IN gdi32 INTEGER hdc, INTEGER nIndex

	DECLARE INTEGER DrawText IN user32;
		INTEGER hDC, STRING lpString, INTEGER nCount,;
		STRING @lpRect, INTEGER uFormat

	DECLARE INTEGER CreateFont IN gdi32;
		INTEGER nHeight, INTEGER nWidth, INTEGER nEscapement,;
		INTEGER nOrientation, INTEGER fnWeight, INTEGER fdwItalic,;
		INTEGER fdwUnderline, INTEGER fdwStrikeOut, INTEGER fdwCharSet,;
		INTEGER fdwOutPrecis, INTEGER fdwClipPrecis, INTEGER fdwQuality,;
		INTEGER fdwPitchAndFamily, STRING lpszFace

	DECLARE INTEGER FillRect IN user32;
		INTEGER hDC, STRING @RECT, INTEGER hBrush

	DECLARE INTEGER CreateCompatibleBitmap IN gdi32;
		INTEGER hdc, INTEGER nWidth, INTEGER nHeight

	IF VARTYPE(m.cCaption) = "C"
		THIS.Caption1 = m.cCaption
	ENDIF
	LOCAL nBaseWidth, nBaseHeight, BaseRect, InnerRect, TextRect,;
		hDesktop, hDesktopDC, hMemDC, hMemBmp, hBrush, hFont
	nBaseWidth = THIS.Width-4
	nBaseHeight = THIS.Height-4
	BaseRect = num2dword(0) + num2dword(0) +;
		num2dword(nBaseWidth) + num2dword(nBaseHeight)
	InnerRect = num2dword(THIS.MarginLeft) +;
		num2dword(THIS.MarginTop) +;
		num2dword(nBaseWidth-THIS.MarginRight) +;
		num2dword(nBaseHeight-THIS.MarginBottom)
	TextRect = num2dword(THIS.MarginLeft+THIS.PaddingLeft) +;
		num2dword(THIS.MarginTop+THIS.PaddingTop) +;
		num2dword(nBaseWidth-THIS.MarginRight-THIS.PaddingRight) +;
		num2dword(nBaseHeight-THIS.MarginTop-THIS.PaddingTop)

	hDesktop = GetDesktopWindow()
	hDesktopDC = GetWindowDC(hDesktop)
	hMemDC = CreateCompatibleDC(hDesktopDC)
	hMemBmp = CreateCompatibleBitmap(hDesktopDC, nBaseWidth, nBaseHeight)
	= SelectObject(hMemDC, hMemBmp)
	= ReleaseDC(hDesktop, hDesktopDC)

	hBrush = CreateSolidBrush(THIS.FrameColor)
	= FillRect(hMemDC, @BaseRect, hBrush)
	= DeleteObject(hBrush)

	hBrush = CreateSolidBrush(THIS.BackColor)
	= FillRect(hMemDC, @InnerRect, hBrush)
	= DeleteObject(hBrush)

	= SetBkMode(hMemDC, 1)  && transparent
	= SetTextColor(hMemDC, THIS.ForeColor)
	hFont = CreateFont(-THIS.FontSize * GetDeviceCaps(hMemDC, LOGPIXELSY) / 72,;
		0,0,0, Iif(THIS.FontBold,700,400), Iif(THIS.FontItalic,1,0),0,0,;
		ANSI_CHARSET, OUT_OUTLINE_PRECIS, CLIP_STROKE_PRECIS,;
		PROOF_QUALITY, DEFAULT_PITCH, THIS.FontName)
	= SelectObject(hMemDC, hFont))
	= DrawText(hMemDC, THIS.Caption1, Len(THIS.Caption1),;
		@TextRect, DT_WORDBREAK+DT_CENTER)

	IF BitmapToFile(hMemDC, hMemBmp, nBaseWidth, nBaseHeight, THIS.Bitmapfile)
		THIS.Picture = THIS.Bitmapfile
	ENDIF
	= DeleteObject(hMemBmp)
	= DeleteDC(hMemDC)
ENDDEFINE

PROCEDURE BitmapToFile
PARAMETERS hMemDC, hMemBmp, nWidth, nHeight, cFilename
#DEFINE cnBitsPerPixel         24
#DEFINE BHDR_SIZE              40  && BITMAPINFOHEADER
#DEFINE BFHDR_SIZE             14  && BITMAPFILEHEADER
#DEFINE GENERIC_WRITE          0x40000000
#DEFINE FILE_SHARE_WRITE       2
#DEFINE CREATE_ALWAYS          2
#DEFINE FILE_ATTRIBUTE_NORMAL  128
	DECLARE INTEGER GlobalAlloc IN kernel32 INTEGER wFlags, INTEGER dwBytes
	DECLARE INTEGER GlobalFree IN kernel32 INTEGER hMem
	DECLARE INTEGER CloseHandle IN kernel32 INTEGER hObject
	DECLARE INTEGER GetDIBits IN gdi32;
		INTEGER hdc, INTEGER hbmp, INTEGER uStartScan,;
		INTEGER cScanLines, INTEGER lpvBits, STRING @lpbi,;
		INTEGER uUsage
	DECLARE RtlZeroMemory IN kernel32 As ZeroMemory;
		INTEGER dest, INTEGER numBytes
	DECLARE INTEGER CreateFile IN kernel32;
		STRING lpFileName, INTEGER dwDesiredAccess,;
		INTEGER dwShareMode, INTEGER lpSecurityAttr,;
		INTEGER dwCreationDisp, INTEGER dwFlagsAndAttrs,;
		INTEGER hTemplateFile

	PRIVATE nBytesPerScan, nBitsArray, nBitsSize, nRgbQuadSize, cBInfo
	STORE 0 TO nBytesPerScan, nRgbQuadSize, nBitsArray, nBitsSize
	STORE "" TO cBInfo
	DO InitBitmapData
	DO SaveBitmapFile
	= GlobalFree(nBitsArray)

PROCEDURE InitBitmapData
	nBytesPerScan = nWidth * 3
	IF Mod(nBytesPerScan, 4) <> 0
		nBytesPerScan = nBytesPerScan + 4 - Mod(nBytesPerScan, 4)
	ENDIF
	cBInfo = num2dword(BHDR_SIZE) + num2dword(nWidth) + num2dword(nHeight) +;
		num2word(1) + num2word(cnBitsPerPixel) + Repli(Chr(0),24)
	nBitsSize = nHeight * nBytesPerScan
	nBitsArray = GlobalAlloc(0, nBitsSize)
	= ZeroMemory(nBitsArray, nBitsSize)
	= GetDIBits(hMemDC, hMemBmp, 0, nHeight, nBitsArray, @cBInfo, 0)

PROCEDURE SaveBitmapFile
	LOCAL hFile, lnOffBits, lnFileSize, cBFileHdr
	lnFileSize = BFHDR_SIZE + BHDR_SIZE + nRgbQuadSize + nBitsSize
	lnOffBits = BFHDR_SIZE + BHDR_SIZE + nRgbQuadSize
	cBFileHdr = "BM" + num2dword(lnFileSize) + num2dword(0) + num2dword(lnOffBits)
	hFile = CreateFile(m.cFilename, GENERIC_WRITE, FILE_SHARE_WRITE, 0,;
				CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0)
	IF hFile <> -1
		= String2File(hFile, @cBFileHdr)
		= String2File(hFile, @cBInfo)
		= Ptr2File(hFile, nBitsArray, nBitsSize)
		= CloseHandle (hFile)
	ENDIF
RETURN (hFile <> -1)

PROCEDURE String2File(hFile, lcBuffer)
* appends string buffer to a file
	DECLARE INTEGER WriteFile IN kernel32;
		INTEGER hFile, STRING @lpBuffer, INTEGER nBt2Write,;
		INTEGER @lpBtWritten, INTEGER lpOverlapped
	= WriteFile(hFile, @lcBuffer, Len(lcBuffer), 0, 0)

PROCEDURE Ptr2File(hFile, lnPointer, lnBt2Write)
* appends memory block to a file
	DECLARE INTEGER WriteFile IN kernel32;
		INTEGER hFile, INTEGER lpBuffer, INTEGER nBt2Write,;
		INTEGER @lpBtWritten, INTEGER lpOverlapped
	= WriteFile(hFile, lnPointer, lnBt2Write, 0, 0)

FUNCTION num2dword(lnValue)
#DEFINE m0  256
#DEFINE m1  65536
#DEFINE m2  16777216
	IF lnValue < 0
		lnValue = 0x100000000 + lnValue
	ENDIF
	LOCAL b0, b1, b2, b3
	b3 = Int(lnValue/m2)
	b2 = Int((lnValue - b3*m2)/m1)
	b1 = Int((lnValue - b3*m2 - b2*m1)/m0)
	b0 = Mod(lnValue, m0)
RETURN Chr(b0)+Chr(b1)+Chr(b2)+Chr(b3)

FUNCTION num2word(lnValue)
RETURN Chr(MOD(m.lnValue,256)) + CHR(INT(m.lnValue/256))  

Listed functions:

CloseHandle
CreateCompatibleBitmap
CreateCompatibleDC
CreateFile
CreateFont
CreateSolidBrush
DeleteDC
DeleteObject
DrawText
FillRect
GetDIBits
GetDesktopWindow
GetDeviceCaps
GetWindowDC
GlobalAlloc
GlobalFree
ReleaseDC
SelectObject
SetBkMode
SetTextColor
WriteFile

Comment:

Bitmap file creation takes no time, and the file is deleted on Destroy event for the object.

The Caption property is not used and should be empty every time. All font properties are available. Padding and Margin properties provide positioning the text inside the button.

Unfortunately hot keys are not supported (there should be a trade anyway). The WordWrap property is ignored (practically it is on all the time). As for the rest the object behaves like an ordinary CommandButton.