Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Transparent RichEdit control

164 views
Skip to first unread message

Matt Slot

unread,
Aug 1, 2000, 3:00:00 AM8/1/00
to
I thought it'd be pretty easy, but I don't see a way to draw my RichEdit
control over the textured background of the parent window. Can someone
point me to a snippet or description for using the Win32 API (no MFC)?

Thanks!

Matt
--
/* Matt Slot, Bitwise Operator * One box, two box, yellow box, blue box *
* fprefect at ambrosiasw.com * <http://www.ambrosiasw.com/~fprefect/> */

Jens Schacherl

unread,
Aug 1, 2000, 3:00:00 AM8/1/00
to
Recently I read a posting from someone who did this using the
EM_FORMATRANGE message. I think the steps are: creating a mem dc in the
WM_PAINT handler, render the richedit control into it using
EM_FORMATRANGE and then bitblt the memory dc transparently on the screen
using the current background color of the control as transparent color.
However there might be some problems calculating the visible range of
the control contents and converting all sizes to twips unit for the
FORMATRANGE struct.

I'm not sure about these steps and have not researched this further but
will do so after my holidays.
I've read this question quite often in different groups so if you find a
solution _please_ post it to codeguru.com and codeproject.com, many
developers (including me!) would be very very happy!

Hope this helps, Jens


Matt Slot schrieb:

Matt Slot

unread,
Aug 1, 2000, 3:00:00 AM8/1/00
to
<Jens.Sc...@prosieben.de> wrote:
> Recently I read a posting from someone who did this using the
> EM_FORMATRANGE message. I think the steps are: creating a mem dc in the
> WM_PAINT handler, render the richedit control into it using
> EM_FORMATRANGE and then bitblt the memory dc transparently on the screen
> using the current background color of the control as transparent color.

Well, after posting, I decided to try something like that. I didn't use
use EM_FORMATRANGE -- I used WM_PRINT to a mem DC, then blitted to the
window's DC. It works pretty well as the text is not editable, but there
is still one problem for me to solve: my transparent blit converts any
colored text to black. I think I'm missing something really basic, but
I tried several things with no luck.

The other path I had considered is to convert the mask into a region. I
could either use a clipping region instead of a transparent blit, or
SetWindowRgn() on the control (except it might shift/rewrap the text).
A region uses less memory, doesn't need to be rebuilt unless the text
changes, and it should be faster.


> I've read this question quite often in different groups so if you find
> a solution _please_ post it to codeguru.com and codeproject.com, many
> developers (including me!) would be very very happy!

I spent some time researching it too, but I can't believe I'm the first
one to solve this. I've attached the source (and cross-posted to .graphics)
to ask for some help finding my colored text bug. Once's it's fixed, I'll
post it.

Thanks!

Matt

/* Matt Slot, Bitwise Operator * One box, two box, yellow box, blue box *
* fprefect at ambrosiasw.com * <http://www.ambrosiasw.com/~fprefect/> */

----

/* File "OVERLAY.C" - Code to overlay a RichText control over a background */

#include <COMMCTRL.H>
#include <RICHEDIT.H>
#include <WINDOWSX.H>
#include <WINDOWS.H>

/* **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** */
/* **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** */
/* Preprocessor Definitions */

#define OVERLAY_CLASS "OVERLAY"
#define PROGRAM_NAME "RichEdit Overlay"
#define STRING "abcde fghij klmno pqrst uvwxyz 01234 56789 "

/* **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** */
/* **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** */
/* Function Prototypes */

LRESULT CALLBACK MyWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);

/* **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** */
/* **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** */
/* Global/Static Definitions */

HWND hwnd = NULL;
HWND hwndRich = NULL;
HDC hdcRich = NULL;
HDC hdcMask = NULL;
HBITMAP hbmRich = NULL;
HBITMAP hbmMask = NULL;
UINT32 h, w;

/* **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** */
/* **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** */

#define kFirstChildWindowID 10000

static BOOL CALLBACK CalcChildWindowProc(HWND hwnd, LPARAM param) {
HMENU * child = (HMENU *) param;

if (*child == (HMENU) GetDlgCtrlID(hwnd)) *child = NULL;

return((*child) ? TRUE : FALSE);
}

static HMENU CalcChildWindowID(HWND hwnd) {
HMENU child;
UINT i;

for(i=kFirstChildWindowID, child=NULL; !child; child = (HMENU) ++i)
EnumChildWindows(hwnd, CalcChildWindowProc, (LPARAM) &child);

return(child);
}

/* **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** */
/* **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** */

LRESULT CALLBACK MyWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {
LRESULT result = 0;
HDC hdc;
PAINTSTRUCT ps;
HBITMAP hbmSaveRich = NULL;
HBITMAP hbmSaveMask = NULL;
COLORREF backColor;
COLORREF textColor;

switch(msg) {
case WM_PAINT:
hdc = BeginPaint(hwnd, &ps);

hbmSaveRich = SelectBitmap(hdcRich,hbmRich);
hbmSaveMask = SelectBitmap(hdcMask,hbmMask);
SendMessage(hwndRich, WM_PRINT, (WPARAM) hdcRich,
PRF_CLIENT | PRF_ERASEBKGND);

if (0)

TransparentBlt(hdc,50,50,w,h,hdcRich,0,0,w,h,GetBkColor(hdcRich));
else {
BitBlt(hdcMask,0,0,w,h,hdcRich,0,0,SRCCOPY);
BitBlt(hdc,50,50,w,h,hdcMask,0,0,SRCAND);

textColor = SetTextColor(hdcRich,RGB(255,255,255));
backColor = SetBkColor(hdcRich,RGB(0,0,0));
BitBlt(hdcRich,0,0,w,h,hdcMask,0,0,0x00220326); // SRCNOTAND
SetTextColor(hdcRich,textColor);
SetBkColor(hdcRich,backColor);
BitBlt(hdc,50,50,w,h,hdcRich,0,0,SRCPAINT);
}

SelectBitmap(hdcRich,hbmSaveRich);
SelectBitmap(hdcMask,hbmSaveMask);

EndPaint(hwnd, &ps);
result = 1;
break;

case WM_DESTROY:
PostQuitMessage(0);
break;

default:
result = DefWindowProc(hwnd, msg, wParam, lParam);
}

return(result);
}

/* **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** */
/* **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** */

int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrev, LPSTR lpszCmdLine,
int nCmdShow) {
MSG lpMsg;
WNDCLASS wc;
RECT rect;
HDC hdc;
UINT i, j;
CHARFORMAT cf;

if (!hPrev) {
wc.lpszClassName = OVERLAY_CLASS;
wc.hInstance = hInst;
wc.lpfnWndProc = MyWndProc;
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wc.lpszMenuName = NULL;
wc.hbrBackground = CreateHatchBrush(HS_BDIAGONAL, RGB(255,0,0));
wc.style = 0;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;

if (!RegisterClass(&wc)) return FALSE;
}

/* Create the window */
hwnd = CreateWindow(OVERLAY_CLASS, PROGRAM_NAME, WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
(HWND) NULL, (HMENU) NULL, (HANDLE)hInst, (LPSTR) NULL);

InitCommonControls();
LoadLibrary("RICHED32.DLL");
GetClientRect(hwnd, &rect);
hwndRich = CreateWindowEx(0, "RICHEDIT", "", WS_CHILD | ES_LEFT |
ES_SAVESEL | ES_MULTILINE | ES_AUTOVSCROLL, 1000 + rect.left + 50,
1000 + rect.top + 50, rect.right - rect.left - 100, rect.bottom -
rect.top - 100, hwnd, CalcChildWindowID(hwnd), (HINSTANCE)
GetWindowLong(hwnd, GWL_HINSTANCE), NULL);
Edit_Enable(hwndRich, FALSE);
hdc = GetDC(hwndRich);

ZeroMemory(&cf, sizeof(CHARFORMAT));
cf.cbSize = sizeof(CHARFORMAT);
cf.dwMask = CFM_BOLD | CFM_COLOR | CFM_FACE | CFM_ITALIC |
CFM_SIZE | CFM_UNDERLINE;
cf.dwEffects = 0;
cf.yHeight = 120 / GetDeviceCaps(hdc, LOGPIXELSY);
cf.yOffset = 0;
cf.crTextColor = RGB(0,0,0);
cf.bCharSet = ANSI_CHARSET;
cf.bPitchAndFamily = DEFAULT_PITCH;
strcpy(cf.szFaceName, "System");
for(i=0, j=sizeof(STRING)-sizeof(""); i<27; i++) {
/* Add new text */
Edit_SetSel(hwndRich, j*i, j*i);
Edit_ReplaceSel(hwndRich, STRING);
Edit_SetSel(hwndRich, j*i, j*(i+1));

/* Format the text */
cf.crTextColor = RGB((i%3)*0x55,((i/3)%3)*0x55,((i/9)%3)*0x55);
SendMessage(hwndRich, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf);
}
Edit_SetSel(hwndRich, 0, 0);

GetClientRect(hwndRich, &rect);
w = rect.right - rect.left;
h = rect.bottom - rect.top;
SetBkColor(hdc, RGB(255,255,255)); /* Any unused color will suffice */
hdcRich = CreateCompatibleDC(hdc);
hbmRich = CreateCompatibleBitmap(hdcRich, w, h);
hdcMask = CreateCompatibleDC(hdc);
hbmMask = CreateBitmap(w, h, 1, 1, NULL);
ReleaseDC(hwndRich, hdc);

ShowWindow(hwnd, nCmdShow);
UpdateWindow(hwnd);
while(GetMessage(&lpMsg, NULL, 0, 0)) {
TranslateMessage(&lpMsg);
DispatchMessage(&lpMsg);
}
return(lpMsg.wParam);
}

Matt Slot

unread,
Aug 3, 2000, 3:00:00 AM8/3/00
to
/* File "OVERLAY.C" - Code to overlay a RichText control over a background */
/* Code by Matt Slot <fpre...@ambrosiasw.com> and placed into public domain */

#include <COMMCTRL.H>
#include <RICHEDIT.H>
#include <WINDOWSX.H>
#include <WINDOWS.H>

/* **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** */
/* **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** */
/* Preprocessor Definitions */

#define OVERLAY_CLASS "OVERLAY"
#define PROGRAM_NAME "RichEdit Overlay"
#define STRING "abcde fghij klmno pqrst uvwxyz 01234 56789 "

/* **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** */
/* **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** */
/* Function Prototypes */

typedef BOOL WINAPI (*TransparentBltProc)(IN HDC,IN int,IN int,IN int,
IN int,IN HDC,IN int,IN int,IN int,IN int,IN UINT);

BOOL RichEditOverlayHWND(HWND hwnd, HDC hdc, HWND hwndRich);
BOOL RichEditOverlayHDC(HDC hdc, DWORD x, DWORD y, HWND hwndRich);

/* **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** */
/* **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** */

/* Overlay the contents of a HWND with a RichEdit control (its child window) */

BOOL RichEditOverlayHWND(HWND hwnd, HDC hdc, HWND hwndRich) {
POINT point = { 0, 0 };

/* Just get the window location from our parent window */
if (IsChild(hwnd, hwndRich)) MapWindowPoints(hwndRich, hwnd, &point, 1);

return(RichEditOverlayHDC(hdc, point.x, point.y, hwndRich));


}

/* **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** */
/* **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** */

/* Overlay the contents of the HDC at the given point with a RichEdit control */

BOOL RichEditOverlayHDC(HDC hdc, DWORD x, DWORD y, HWND hwndRich) {
static TransparentBltProc gTransparentBltProc = NULL;
static BOOL gTransparentBltFirst = TRUE;
BOOL success = TRUE;
HDC hdcSave = NULL;
HDC hdcRich = NULL, hdcMask = NULL;
HBITMAP hbmRich = NULL, hbmSaveRich = NULL;
HBITMAP hbmMask = NULL, hbmSaveMask = NULL;
COLORREF bkColor;
DWORD bits, w, h;
POINT point;
RECT rect;

/* On our first call, we try to dynamically load TransparentBlt() */
if (gTransparentBltFirst) {
HINSTANCE hModule = LoadLibrary("MSIMG32.DLL");
if (hModule)
gTransparentBltProc = GetProcAddress(hModule, "TransparentBlt");
gTransparentBltFirst = FALSE; /* Don't bother trying again */
}

/* Establish our coordinates from the start. */


GetClientRect(hwndRich, &rect);
w = rect.right - rect.left;
h = rect.bottom - rect.top;

/* Get the relevant characteristics of the source window */
hdcSave = GetDC(hwndRich);
if (!hdcSave) { success = FALSE; goto CLEANUP; }
bkColor = GetBkColor(hdcSave);
bits = GetDeviceCaps(hdcSave, BITSPIXEL);

/* Create offscreen drawing contexts and bitmap for the pixels */
hdcRich = CreateCompatibleDC(hdcSave);
if (!hdcRich) { success = FALSE; goto CLEANUP; }
hbmRich = CreateBitmap(w, h, 1, bits, NULL);
if (!hbmRich) { success = FALSE; goto CLEANUP; }
hbmSaveRich = SelectBitmap(hdcRich, hbmRich);

/* Create offscreen drawing contexts and bitmap for the mask */
if (!gTransparentBltProc) {
hdcMask = CreateCompatibleDC(hdcSave);
if (!hdcMask) { success = FALSE; goto CLEANUP; }


hbmMask = CreateBitmap(w, h, 1, 1, NULL);

if (!hbmMask) { success = FALSE; goto CLEANUP; }
hbmSaveMask = SelectBitmap(hdcMask, hbmMask);
}

/* Draw the control into our offscreen drawing context */


SendMessage(hwndRich, WM_PRINT, (WPARAM) hdcRich,
PRF_CLIENT | PRF_ERASEBKGND);

if (gTransparentBltProc)
/* We'll gladly use it if it's available */
success = (*gTransparentBltProc)(hdc,x,y,w,h,hdcRich,0,0,w,h,bkColor);
else {
/* Otherwise use a mask and draw it manually */
BitBlt(hdcMask,0,0,w,h,hdcRich,0,0,SRCCOPY);
BitBlt(hdc,x,y,w,h,hdcMask,0,0,SRCAND);
BitBlt(hdcRich,0,0,w,h,hdcMask,0,0,0x00220326); /* SRCNOTAND */
success = BitBlt(hdc,x,y,w,h,hdcRich,0,0,SRCPAINT);
}

CLEANUP:

/* Release all of our objects in order */
if (hbmSaveMask) SelectBitmap(hdcMask, hbmSaveMask);
if (hbmMask) DeleteObject(hbmMask);
if (hbmSaveRich) SelectBitmap(hdcRich, hbmSaveRich);
if (hbmRich) DeleteObject(hbmRich);
if (hdcSave) ReleaseDC(hwndRich, hdcSave);

return(success);
}

/* **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** */
/* **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** */

/* Private utility routines */

#define kFirstChildWindowID 10000

static BOOL CALLBACK CalcChildWindowProc(HWND hwnd, LPARAM param) {
HMENU * child = (HMENU *) param;

if (*child == (HMENU) GetDlgCtrlID(hwnd)) *child = (HMENU) NULL;

return((*child) ? TRUE : FALSE);
}


/* **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** */
/* **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** */

static HMENU CalcChildWindowID(HWND hwnd) {
HMENU child;
UINT i;

for(i=kFirstChildWindowID, child=(HMENU) NULL; !child; child = (HMENU) ++i)


EnumChildWindows(hwnd, CalcChildWindowProc, (LPARAM) &child);

return(child);
}

/* **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** */
/* **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** */

static LRESULT CALLBACK MyWndProc(HWND hwnd, UINT msg, WPARAM wParam,

LPARAM lParam) {
LRESULT result = 0;

HWND hwndRich;
HDC hdc;
PAINTSTRUCT ps;



switch(msg) {
case WM_PAINT:
hdc = BeginPaint(hwnd, &ps);

/* Grab the RichEdit handle from the parent window */
hwndRich = (HWND) GetWindowLong(hwnd, GWL_USERDATA);
RichEditOverlayHWND(hwnd, hdc, hwndRich);

EndPaint(hwnd, &ps);
result = 1;
break;

case WM_DESTROY:
PostQuitMessage(0);
break;

default:
result = DefWindowProc(hwnd, msg, wParam, lParam);
}

return(result);
}

/* **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** */
/* **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** */

int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrev, LPSTR lpszCmdLine,
int nCmdShow) {

HWND hwnd = (HWND) NULL;
HWND hwndRich = (HWND) NULL;


MSG lpMsg;
WNDCLASS wc;
RECT rect;
HDC hdc;
UINT i, j;
CHARFORMAT cf;

/* Create our class and use an interesting background brush */


if (!hPrev) {
wc.lpszClassName = OVERLAY_CLASS;
wc.hInstance = hInst;
wc.lpfnWndProc = MyWndProc;

wc.hCursor = LoadCursor((HINSTANCE) NULL, IDC_ARROW);
wc.hIcon = LoadIcon((HINSTANCE) NULL, IDI_APPLICATION);
wc.lpszMenuName = (char *) NULL;


wc.hbrBackground = CreateHatchBrush(HS_BDIAGONAL, RGB(255,0,0));
wc.style = 0;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;

if (!RegisterClass(&wc)) return FALSE;
}

/* Create the window */
hwnd = CreateWindow(OVERLAY_CLASS, PROGRAM_NAME, WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,

(HWND) NULL, (HMENU) NULL, (HINSTANCE) hInst, (LPSTR) NULL);

/* Create the RichEdit control -- notice, we never make it visible! */


InitCommonControls();
LoadLibrary("RICHED32.DLL");
GetClientRect(hwnd, &rect);
hwndRich = CreateWindowEx(0, "RICHEDIT", "", WS_CHILD | ES_LEFT |

ES_SAVESEL | ES_MULTILINE | ES_AUTOVSCROLL, rect.left + 50,


rect.top + 50, rect.right - rect.left - 100, rect.bottom -
rect.top - 100, hwnd, CalcChildWindowID(hwnd), (HINSTANCE)
GetWindowLong(hwnd, GWL_HINSTANCE), NULL);
Edit_Enable(hwndRich, FALSE);
hdc = GetDC(hwndRich);

/* Fill the image with some interesting text, 1 run at a time */


ZeroMemory(&cf, sizeof(CHARFORMAT));
cf.cbSize = sizeof(CHARFORMAT);
cf.dwMask = CFM_BOLD | CFM_COLOR | CFM_FACE | CFM_ITALIC |
CFM_SIZE | CFM_UNDERLINE;
cf.dwEffects = 0;
cf.yHeight = 120 / GetDeviceCaps(hdc, LOGPIXELSY);
cf.yOffset = 0;
cf.crTextColor = RGB(0,0,0);
cf.bCharSet = ANSI_CHARSET;
cf.bPitchAndFamily = DEFAULT_PITCH;
strcpy(cf.szFaceName, "System");
for(i=0, j=sizeof(STRING)-sizeof(""); i<27; i++) {

Edit_SetSel(hwndRich, j*i, j*i);
Edit_ReplaceSel(hwndRich, STRING);
Edit_SetSel(hwndRich, j*i, j*(i+1));

/* Format the text */

cf.crTextColor = RGB((i%3)*0x44,((i/3)%3)*0x44,((i/9)%3)*0x44);


SendMessage(hwndRich, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf);
}
Edit_SetSel(hwndRich, 0, 0);

ReleaseDC(hwndRich, hdc);

/* Store the handle to the child window so we can access it */
SetWindowLong(hwnd, GWL_USERDATA, (LONG) hwndRich);

/* Sit and spin */
ShowWindow(hwnd, nCmdShow);
UpdateWindow(hwnd);
while(GetMessage(&lpMsg, (HWND) NULL, 0, 0)) {
TranslateMessage(&lpMsg);
DispatchMessage(&lpMsg);
}

DestroyWindow(hwnd);
return(lpMsg.wParam);
}
--

0 new messages