Basically, I am trying to display the output and then update the font of the display when the window is moved. I am trying to use onMove() method to determine if window is moved. However, my problem is somehow, when my program is initialized, it calls the onMove() method instantly. It does not even wait for the window to be moved. Therefore, the font is already changed before the window is moved. What I am trying to do is that, change the font of the display after the window is moved. Honestly, I don't know if there is a way to call onMove() after the window is initialized. In any case, my program will invoke the onMove(). I don't think this is going to be my solution. Does anybody have any other ideas how could I achieve that? Here is a sample execution of What I am trying to do and when I run the program, the test output is already displayed with new font and color. I would like to do that after the window is moved.
void CMainFrame::OnMove()
{
CFrameWnd::OnMove(x, y);
CDC *dc;
dc = GetDC();
CRect rect;
dc->SetTextColor(RGB(255, 128, 0));
CBrush brush;
CFont oFont;
oFont.CreateFont(25, 0, 0, 0, 400, false, false,
0, ANSI_CHARSET, OUT_DEFAULT_PRECIS,
CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY,
DEFAULT_PITCH | FF_SWISS, _T("Times New Roman"));
dc->SetBkMode(TRANSPARENT);
dc->SelectObject(oFont);
dc->FillRect(&rect, &brush);
Invalidate();
UpdateWindow();
dc->TextOutW(0, 200, _T("TESTTTT"));
}
Related
I have a standard MFC SDI app. In the About Dialog (created with the MFC Wizard), I am trying to change some static text format (labels). This is my code:
BOOL CAboutDlg::OnInitDialog()
{
CDialogEx::OnInitDialog();
auto font_programname = std::make_shared<CFont>();
font_programname->CreateFontW(25, 0, 0, 0, FW_BLACK, 0, 1, 0, 0, 0, 0, 0, 0, _T("Arial"));
_label_programname.SetFont(font_programname.get());
return TRUE; // return TRUE unless you set the focus to a control
// EXCEPTION: OCX Property Pages should return FALSE
}
However, this results only in the font becoming bold, but the size does not change. What am I doing wrong?
The font is becoming bolder, so the CreateFont and SetFont are doing something. This leaves the Font Mapper as the next suspect. The "Arial" is a suggestion, but the font mapper will look at other characteristics first. The font you are seeing is most likely the largest raster (non True Type) font that is available in an FW_BLACK weight. Getting the exact font you want will require filling in other parameters of CreateFont so the desired font is located.
For debugging, follow the CreateFont call with a call to GetLogFont and view the data structure to see what font was actually mapped.
The comment above concerning the lifetime of the font is correct. That may not be the first issue you are facing but it is an issue. The CFont needs to be a dialog-class variable, not a local variable in the function, so the font object lasts as long as the control on the dialog.
It does look like you do not need a CFont object for your purposes, as you let it go out of scope.
Also see WM_SETFONT.
const int iFontSize = 25;
const CString sFont = L"Arial";
HFONT hFont = CreateFontW(iFontSize, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE, ANSI_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH, sFont);
yourControl.SendMessageW(WM_SETFONT, reinterpret_cast<WPARAM>(hFont), TRUE);
The low-order word of lParam specifies whether the control should be redrawn immediately upon setting the font. If this parameter is TRUE, the control redraws itself.
Though you should keep the object(s) in a container too, (E.g. std::vector<HFONT>) and delete the/all object(s) later (destructor).
The application should call the DeleteObject function to delete the font when it is no longer needed; for example, after it destroys the control.
First, I have a picture control with a bitmap1 loaded in a dialog box:
SendMessage(hWnd, STM_SETIMAGE, IMAGE_BITMAP, (LPARAM)LoadImage(NULL, sbitmap1.c_str(), IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE));
Second, at some point I draw a bitmap2 in the picture control using StretchBlt.
hdcImage = GetDC(hWnd)
hMemDC = CreateCompatibleDC( hdcImage );
hOldbm = (HBITMAP)SelectObject( hMemDC, hbitmap2 );
SetStretchBltMode( hdcImage, COLORONCOLOR);
StretchBlt( hdcImage, left, top, width, height, hMemDC, 0, 0, w, h, SRCCOPY );
SelectObject( hMemDC, hOldbm );
The bitmap2 is successfully painted but in certain occasions (for example when I minimize the dialog) the picture control no longer shows bitmap2 but bitmap1 instead.
I think the problem is the repaint event. Is there a way to stop the repaint event or change the bitmap that this event is going to paint?
Edit:
Thanks #Mark and #Edward for your answers.
The problem was this:
after using StretchBlt you need to do SendMessage STM_SETIMAGE
for SendMessage STM_SETIMAGE use a global HBITMAP (preferably)
Something like this:
hbitmapglobal = (HBITMAP)CopyImage(hbitmap2, IMAGE_BITMAP, abs(width), abs(height), LR_COPYRETURNORG);
SendMessage(hWnd, STM_SETIMAGE, IMAGE_BITMAP, (LPARAM)hbitmapglobal);
CopyImage is the real simple way to make a copy of a HBITMAP. Take in consideration that this is a sample and hbitmapglobal must be freed at some point.
Normally all the painting occurs in the WM_PAINT handler.
I would suggest setting up (global) variables when you need to paint the second bitmap and invalidate the rectangle of the picture control.
Check for the variable in the WM_PAINT handler and do the painting there.
I register the window class like this:
WNDCLASSEX wctt;
wctt.cbSize = sizeof(WNDCLASSEX);
wctt.style = CS_DBLCLKS;
wctt.lpfnWndProc = WndProcTooltip;
wctt.cbClsExtra = 0;
wctt.cbWndExtra = 0;
wctt.hInstance = m_hAppInstance;
wctt.hIcon = NULL;
wctt.hCursor = LoadCursor(NULL, IDC_SIZE);
wctt.hbrBackground = NULL;
wctt.lpszMenuName = NULL;
wctt.lpszClassName = _T("myWindow");
wctt.hIconSm = NULL;
RegisterClassEx(&wctt)
As you can see I use wctt.hbrBackground = NULL; so it will have no background.
The window is created like this:
::CreateWindowEx(WS_EX_TOPMOST | WS_EX_TOOLWINDOW,
_T("myWindow"),
NULL,
WS_VISIBLE | WS_POPUP,
50,
50,
150,
100,
NULL,
NULL,
m_hAppInstance,
NULL);
In the paint section I draw icon on the window:
PAINTSTRUCT ps;
HDC hdc;
BITMAP bitmap;
ICONINFO iconinfo;
hdc = ::BeginPaint(hWnd, &ps);
::SetBkMode(hdc,TRANSPARENT);
::GetIconInfo(localIcon, &iconinfo);
::GetObject(iconinfo.hbmColor, sizeof(bitmap), &bitmap);
::DeleteObject(iconinfo.hbmColor);
::DeleteObject(iconinfo.hbmMask);
::DrawIconEx(hdc, 0,0, localIcon, bitmap.bmWidth, bitmap.bmHeight, 0, NULL, DI_NORMAL);
The icon size is smaller than the window size and I get on the background the current view on the window below the popup.
But now when I move the window (or minimize the window below the popup) the background is not changing.
I was trying to make a timer that each time do the flowing:
RECT rcClient;
GetClientRect(hWnd, &rcClient);
InvalidateRect(hWnd,&rcClient,TRUE);
This makes the print function run again but the background of the icon is not changing.
Should I do anything in WM_ERASEBKGND?
Does Anyone have any idea how to make it work?
thanks,
guy
It's not enough to just let the background stay unpainted; you also need to get the window below yours to repaint itself when necessary.
If the windows are part of the same hierarchy, created by the same thread, it is sufficient to give your window the WS_EX_TRANSPARENT extended style. This causes the window underneath to paint itself first so the background is always up-to-date.
Otherwise you need to use SetWindowRgn so that your window actually doesn't exist outside of the borders you wish to paint.
Look at Layered Window. This feature allows creating semi-transparent windows of different shapes.
Add WS_EX_LAYERED extended attribute in your window class.
You can control the transparency of your window with these two functions:
SetLayeredWindowAttributes:
bAlpha controls the opacity of the entire window, if you pass LWA_ALPHA in dwFlags.
When bAlpha is 0, the window is completely transparent. When bAlpha is 255, the window is opaque.
crKey sets the color that would transparent.
All pixels painted by the window in this color will be transparent.
UpdateLayeredWindow gives you precise control over window transparency, you can give different parts of window different levels of transparency.
If you're trying to create a non-rectangular window, this is not sufficient. Setting "no background" simply means the background will not be drawn, and you'll see whatever happens to be in memory at that location.
To create a non-rectangular window, have a look at the SetWindowRgn function.
I have some trouble drawing an ActiveX control. In the screenshot below you see the control after a resize in the VB6 IDE. The control's outline from before the resize is still shown on the left-hand side of the control:
Here is the code that draws a black ellipsis with a red Z:
void CzFileIoXCtrl::OnDraw(CDC* pdc,
const CRect& rcBounds,
const CRect& rcInvalid)
{
if (!pdc)
{
return;
}
pdc->SetBkMode(TRANSPARENT);
pdc->SelectObject(CBrush::FromHandle((HBRUSH)GetStockObject(BLACK_BRUSH)));
pdc->Ellipse(rcBounds.left, rcBounds.top,
rcBounds.left + rcBounds.Width(),
rcBounds.top + rcBounds.Height());
HFONT font = CreateFont(int(rcBounds.Height() * 0.7),
int(rcBounds.Width() * 0.5),
0, 0, FW_BLACK, FALSE, FALSE, FALSE,
ANSI_CHARSET,
OUT_DEFAULT_PRECIS,
CLIP_DEFAULT_PRECIS,
CLEARTYPE_QUALITY,
FF_DECORATIVE, NULL);
pdc->SelectObject(font);
pdc->SetTextColor(RGB(255, 0, 0));
DRAWTEXTPARAMS params = { sizeof(DRAWTEXTPARAMS), 1, 0, 0, 1 };
RECT bounds = rcBounds;
CString z(L"Z");
pdc->DrawTextEx(z, &bounds, DT_CENTER | DT_VCENTER | DT_SINGLELINE, ¶ms);
}
How can I clear the drawing area?
I managed to reproduce this in the vb form editor. It looks like the problem comes because you do not draw anything outside the ellipse. So, you can draw a rectangle in the entire area like this before drawing anything in OnDraw().
pdc->FillRect( rcBounds, &CBrush(TranslateColor( AmbientBackColor() )) );
I tested this and is working fine.
I'm creating what should be a very simple Win32 C++ app whose sole purpose it to ONLY display a semi-transparent PNG. The window shouldn't have any chrome, and all the opacity should be controlled in the PNG itself.
My problem is that the window doesn't repaint when the content under the window changes, so the transparent areas of the PNG are "stuck" with what was under the window when the application was initially started.
Here's the line where I setup the new window:
hWnd = CreateWindowEx(WS_EX_TOPMOST, szWindowClass, szTitle, WS_POPUP, 0, height/2 - 20, 40, 102, NULL, NULL, hInstance, 0);
For the call to RegisterClassEx, I have this set for the background:
wcex.hbrBackground = (HBRUSH)0;
Here is my handler for WM_PAINT message:
case WM_PAINT:
{
hdc = BeginPaint(hWnd, &ps);
Gdiplus::Graphics graphics(hdc);
graphics.DrawImage(*m_pBitmap, 0, 0);
EndPaint(hWnd, &ps);
break;
}
One thing to note is that the application is always docked to the left of the screen and doesn't move. But, what's underneath the application may change as the user opens, closes or moves windows under it.
When the application first starts, it looks perfect. The transparent (and simi-transparent) parts of the PNG show through perfectly. BUT, when the background underneath the application changes, the background DOESN'T update, it just stays the same from when the application first started. In fact, WM_PAINT (or WM_ERASEBKGND does not get called when the background changes).
I've been playing with this for quite a while and have gotten close to getting 100% right, but not quite there. For instance, I've tried setting the background to (HBRUSH) NULL_BRUSH and I've tried handling WM_ERASEBKGND.
What can be done to get the window to repaint when the contents under it changes?
I was able to do exactly what I wanted by using the code from Part 1 and Part 2 of this series:
Displaying a Splash Screen with C++
Part 1: Creating a HBITMAP archive
Part 2: Displaying the window archive
Those blog posts are talking about displaying a splash screen in Win32 C++, but it was almost identical to what I needed to do. I believe the part that I was missing was that instead of just painting the PNG to the window using GDI+, I needed to use the UpdateLayeredWindow function with the proper BLENDFUNCTION parameter. I'll paste the SetSplashImage method below, which can be found in Part 2 in the link above:
void SetSplashImage(HWND hwndSplash, HBITMAP hbmpSplash)
{
// get the size of the bitmap
BITMAP bm;
GetObject(hbmpSplash, sizeof(bm), &bm);
SIZE sizeSplash = { bm.bmWidth, bm.bmHeight };
// get the primary monitor's info
POINT ptZero = { 0 };
HMONITOR hmonPrimary = MonitorFromPoint(ptZero, MONITOR_DEFAULTTOPRIMARY);
MONITORINFO monitorinfo = { 0 };
monitorinfo.cbSize = sizeof(monitorinfo);
GetMonitorInfo(hmonPrimary, &monitorinfo);
// center the splash screen in the middle of the primary work area
const RECT & rcWork = monitorinfo.rcWork;
POINT ptOrigin;
ptOrigin.x = 0;
ptOrigin.y = rcWork.top + (rcWork.bottom - rcWork.top - sizeSplash.cy) / 2;
// create a memory DC holding the splash bitmap
HDC hdcScreen = GetDC(NULL);
HDC hdcMem = CreateCompatibleDC(hdcScreen);
HBITMAP hbmpOld = (HBITMAP) SelectObject(hdcMem, hbmpSplash);
// use the source image's alpha channel for blending
BLENDFUNCTION blend = { 0 };
blend.BlendOp = AC_SRC_OVER;
blend.SourceConstantAlpha = 255;
blend.AlphaFormat = AC_SRC_ALPHA;
// paint the window (in the right location) with the alpha-blended bitmap
UpdateLayeredWindow(hwndSplash, hdcScreen, &ptOrigin, &sizeSplash,
hdcMem, &ptZero, RGB(0, 0, 0), &blend, ULW_ALPHA);
// delete temporary objects
SelectObject(hdcMem, hbmpOld);
DeleteDC(hdcMem);
ReleaseDC(NULL, hdcScreen);
}
Use the SetLayeredWindowAttributesarchive function, this allows you to set a mask color that will become transparent, thus allowing the background to show through.
You will also need to configure your window with the layered flag, e.g.:
SetWindowLong(hwnd, GWL_EXSTYLE, GetWindowLong(hwnd, GWL_EXSTYLE) | WS_EX_LAYERED);
After that it's fairly simple:
// Make red pixels transparent:
SetLayeredWindowAttributes(hwnd, RGB(255,0,0), 0, LWA_COLORKEY);
When your PNG contains semi-transparent pixels that you want to blend with the background, this becomes more complicated. You could try looking at the approach in this CodeProject article:
Cool, Semi-transparent and Shaped Dialogs with Standard Controls for Windows 2000 and Above