MFC: Draw dialog border using GDI+ - mfc

I modified my dialog to a polygon region dialog. Then i am trying to frame/draw the border.Using device context the CRgn::FrameRgn, i am bale to draw the border around the dialog. But i want to achieve this using Gdi+. I did as below, but the border is appearing only on left and top of the dialog.
Can someone please help on this.
CPoint vertex[4];
BOOL CPolygonDlg::OnInitDialog()
{
CDialogEx::OnInitDialog();
ModifyStyle(WS_CAPTION,0);
ModifyStyle(WS_BORDER,0);
CRect rect(400,200,900,700);
CRect wr = rect;
AdjustWindowRect( wr, 0, FALSE );
MoveWindow(wr);
GetClientRect( rect );
CRect csr = rect;
ClientToScreen( csr );
vertex[0] = CPoint(rect.left,rect.top);
vertex[1] = CPoint(rect.right,rect.top);
vertex[2] = CPoint(rect.right,rect.bottom);
vertex[3] = CPoint(rect.left,rect.bottom);
m_rgnShape.CreatePolygonRgn( vertex, 4, ALTERNATE );
m_rgnShape.OffsetRgn( CPoint( csr.TopLeft() - wr.TopLeft() ) );
SetWindowRgn( (HRGN)m_rgnShape.Detach(), TRUE );
m_rgnShape.CreatePolygonRgn( vertex, 4, ALTERNATE );
return TRUE; // return TRUE unless you set the focus to a control
}
void CPolygonDlg::OnPaint()
{
Graphics graphics(dc.m_hDC);
CRect rect;
GetClientRect(rect);
GraphicsPath gp;
Point point[4];
point[0] = Point(rect.left,rect.top);
point[1] = Point(rect.right,rect.top);
point[2] = Point(rect.right,rect.bottom);
point[3] = Point(rect.left,rect.bottom);
gp.AddPolygon(point,4);
Pen pen(Color(255, 255, 0, 0));
graphics.DrawPath(&pen, &gp);
}
Thanks

When you call GetClientRect(), it returns the size of the client area of the window - the part you can easily draw on, and the part that is controlled by the device context when you do CPaintDC dc(this); in your OnPaint() method.
The problem you are facing is that your dialog window has a border and you need to handle WM_NCPAINT in order to draw on border area.

Related

Problem with transfer objects between contexts

I try to make some easy application which scrolling ECG singal which is drawing on bitmap grid. Environment which I use is Visual Studio 2013 with C++ MFC.
My problem is with transfer gdi object like LineTo or Rectangle() function from dcMemory to my main device context (cdc). Before I make similar application using WinAPI and all go well. I spent a lot time with studying msdn and looking answer with google, and I have no idea why only bitmap from Bitmap.LoadBitmapW(IDB_BITMAP2) is printing. Can anybody help me?
Message when button from menu was calling:
void CToradex_MFC_BitmapView::OnBitmapDraw()
{
Bitmap.LoadBitmapW(IDB_BITMAP2);
cdc.CreateDC(L"DISPLAY", NULL, NULL, NULL);
dcMemory.CreateCompatibleDC(&cdc);
LoadData();
GetObject(Bitmap, sizeof(bmpInfo), &bmpInfo);
dcMemory.SelectObject(&Pen);
dcMemory.Rectangle(100, 100, 200, 200);
dcMemory.LineTo(100, 300);
dcMemory.SelectObject(&Bitmap);
druk.DrawECG(&dcMemory, pointer, 3, SIGN_LEN);
}
Message on mouse button down:
void CToradex_MFC_BitmapView::OnLButtonDown(UINT,CPoint)
{
CToradex_MFC_BitmapDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
POINT p;
GetCursorPos(&p);
x_start = p.x;
y_start = p.y;
}
Message on mouse move:
void CToradex_MFC_BitmapView::OnMouseMove(UINT, CPoint)
{
POINT d;
//CDC * pDC = this->GetDC();
//this->GetClientRect(&rect);
//rect = CRect(rect.left, rect.top, rect.right, rect.bottom);
if (GetCursorPos(&d))
{
move_x = d.x - x_start;
move_y = d.y - y_start;
cdc.BitBlt(move_x, move_y, bmpInfo.bmWidth, bmpInfo.bmHeight, &dcMemory, 0, 0, SRCCOPY);
x_start = d.x;
y_start = d.y;
//Invalidate();
Sleep(10);
}
Below link for all .cpp file:
https://pastebin.com/h7hcLJbz
You need to select a bitmap into your DC first, then draw on top of it:
dcMemory.SelectObject(&Bitmap);
dcMemory.SelectObject(&Pen);
dcMemory.Rectangle(100, 100, 200, 200);
dcMemory.LineTo(100, 300);
druk.DrawECG(&dcMemory, pointer, 3, SIGN_LEN);

How to change CTabCtrl tab colors?

Hello and happy new year, (it is acceptable to say it until Thursday)
I am trying to change the color of the tabs in the CTabCtrl class. I am trying to create my own ReskinCTablCtrl so that I can just call it in separate classes and easily use it throughout my program.
Currently I am able to change the background color of the CTabCtrl but I cannot modify the tab's themselves.
I used ON_WM_ERASEBKGND() for painting the background and it worked without a problem:
BOOL ReskinCTabCtrl::OnEraseBkgnd(CDC* pDC)
{
CRect rect;
GetClientRect(&rect);
CBrush myBrush(RGB(51, 51, 51)); // dialog background color
BOOL bRes = pDC->PatBlt(0, 0, rect.Width(), rect.Height(), PATCOPY);
pDC->SetBkColor(RGB(51, 51, 51));
pDC->FillRect(&rect, &myBrush);
return bRes;
}
However, I have been unsuccesfull at changing the tab colors themselves. They are still the default MFC colors. I have tried to implement ON_WM_PAINT() and ON_WM_DRAWITEM() without any success. I think I can get to the specific tab rect with using both OnDraw and DrawItem similar to the second link example that I have posted in the end of this question.
void ReskinCTabCtrl::OnPaint() {
...
// paint the tabs first and then the borders
int nTab = GetItemCount();
int nSel = GetCurSel();
if (!nTab) // no pages added
return;
while (nTab--)
{
if (nTab != nSel)
{
dis.itemID = nTab;
dis.itemState = 0;
VERIFY(GetItemRect(nTab, &dis.rcItem));
dis.rcItem.bottom -= 2;
DrawItem(&dis);
DrawItemBorder(&dis);
}
}
...
}
I would really appreciate at least some direction to go about this problem, perhaps some more examples or what methods I should focus on using. I don't need the tabs to be different colors, maybe there is an easy way of doing this?
I've been trying to follow some examples like the following links but I still couldn't figure out the right way to do it.
https://support.microsoft.com/en-us/help/179909/how-to-change-the-background-color-of-a-tab-control
https://www.codeproject.com/Articles/1786/Ownerdraw-Tab-Controls-Borders-and-All
Enable OwnerDraw for tab control, either in resource editor, or set TCS_OWNERDRAWFIXED in OnInitDialog
CTabCtrl has message reflection for WM_DRAWITEM therefore we don't want to override WM_DRAWITEM/OnDrawItem from parent class. Instead override in CTabCtrl::DrawItem(LPDRAWITEMSTRUCT).
Unfortunately the result is rather ugly. It's sort of like overriding DrawItem in a button.
If Visual Style is available and enabled, then you can override CTabCtrl::OnPaint and draw everything manually. Example:
void CMyTabCtrl::OnPaint()
{
CPaintDC dc(this);
dc.SelectObject(GetFont());
CPen pen, pen_active;
COLORREF color_off = RGB(240, 240, 240);
COLORREF color_active = RGB(200, 240, 240);
CBrush brush_off, brush_active;
brush_off.CreateSolidBrush(color_off);
brush_active.CreateSolidBrush(color_active);
pen.CreatePen(PS_SOLID, 1, RGB(200, 200, 200));
pen_active.CreatePen(PS_SOLID, 1, color_active);
CRect rcitem;
GetItemRect(0, &rcitem);
CRect rc;
GetClientRect(&rc);
rc.bottom = rcitem.bottom;
dc.FillSolidRect(&rc, GetSysColor(COLOR_3DFACE));
GetClientRect(&rc);
rc.top = rcitem.bottom - 1;
dc.SelectObject(&pen);
dc.SelectObject(&brush_active);
dc.Rectangle(&rc);
for(int i = 0; i < GetItemCount(); i++)
{
dc.SelectObject(&pen);
if(i == GetCurSel())
{
dc.SelectObject(&brush_active);
dc.SetBkColor(color_active);
}
else
{
dc.SelectObject(&brush_off);
dc.SetBkColor(color_off);
}
GetItemRect(i, &rcitem);
rcitem.right++;
dc.Rectangle(&rcitem);
if(i == GetCurSel())
{
dc.SelectObject(pen_active);
dc.MoveTo(rcitem.left+1, rcitem.bottom - 1);
dc.LineTo(rcitem.right, rcitem.bottom - 1);
}
TCITEM item = { 0 };
wchar_t buf[32];
item.pszText = buf;
item.cchTextMax = 32;
item.mask = TCIF_TEXT;
GetItem(i, &item);
dc.DrawText(buf, &rcitem, DT_CENTER | DT_VCENTER | DT_SINGLELINE);
}
}
BOOL CMyTabCtrl::OnEraseBkgnd(CDC*)
{
return TRUE;
}

CClientDC and DC not Drawing on ChtmlEditCtrl

Hi all I am working with a CHtmlEditCtrl in MFC. I want to draw some random rectangles and lines inside a function handling right click event.
The ChtmlEditCtrl control is created from static using this snippet:
bool CHtmlEditCtrlEx::CreateFromStatic( UINT nID, CWnd* pParent ) {
CStatic wndStatic;
if ( !wndStatic.SubclassDlgItem(nID, pParent)) {
return false;
}
CRect rc;
wndStatic.GetWindowRect( &rc );
pParent->ScreenToClient( &rc );
if (Create( 0, (WS_CHILD | WS_VISIBLE), rc, pParent, nID, 0 )) {
...
}
Then I override the CWnd::pretranslate() function as thus:
CClientDC dcc(this);
switch (pMsg->message) {
case WM_RBUTTONUP: // Right-click
// Just some dummy values
DrawSquigly(dcc, 600, 240, 20);
break;
}
the DrawSquigly() function is defined as thus:
void CHtmlEditCtrlEx::DrawSquigly(CDC &dcc, int iLeftX, int iWidth, int iY)
{
CAMTrace trace;
trace.Trace("Drawing Squiggly");
//dcc.TextOut(10, 10, CString(_T("I used a client DC!")));
CPen * oldPen;
CBrush * oldBrush;
oldPen = (CPen *) dc.SelectStockObject(WHITE_PEN);
dcc.MoveTo(5,10);
dcc.LineTo(80, 10);
dcc.SelectObject(oldPen);
//GDI 002_2: Create custom pen with different Line thickness.
CPen thick_pen(PS_SOLID, 3, RGB(0,255,0));
oldPen = dc.SelectObject(&thick_pen);
dcc.MoveTo(5, 20);
dcc.LineTo(80,20);
dcc.SelectObject(oldPen);
//GDI 002_3: Create a Rectangle now
dcc.Draw3dRect(5,30,80,70, RGB(25,25,255), RGB(120,120,120));
//GDI 002_4: Create a Brush that we can use for filling the
// closed surfaces
CBrush brush(RGB(255,0,255));
oldBrush = dc.SelectObject(&brush);
dcc.Rectangle(5,110,80,140);
dcc.SelectObject(oldBrush);
//GDI 002_5: Hatch Brush is useful to apply a pattern in stead
//of solid fill color
CBrush* hatBrush = new CBrush();
hatBrush->CreateHatchBrush(HS_CROSS, RGB(255,0,255));
oldBrush = dc.SelectObject(hatBrush);
dcc.FillRect(new CRect(5,160,80,190), hatBrush);
dcc.SelectObject(oldBrush);
}
but no drawing happens when I right click. I think I am missing something especially because I am new to MFC.
I have added a trace to the top of the event handler to be sure that the function is getting called and it is.
Can anyone please point me the right direction?
There are actually 2 device context in your code: one you pass as parameter in the call (we don't know where it comes from) and the other created locally in the drawing function.
Normally, when the systems gives you a DC it expects you draw something in it, not that you draw into something else.
If the window you are working on is layered, the system gives you a memory context you draw in that is - upon clearing - blit-ted onto the window itself with some window manager effect.
My suspect is that -by allocating a second dc- your drawing are ovewritten when the first one (you left blank) is cleared upon returning from the message handler.

How to shift the start coordinates of the client area on the window?

I have refereed below article to draw a custom frame area with DWM.
Custom Window Frame Using DWM
After removing the standard frame, non client area is not exist in the frame.
void CMainFrame::OnNcCalcSize(BOOL bCalcValidRects, NCCALCSIZE_PARAMS* lpncsp)
{
int nTHight = 30; /*The title bar height*/
RECT * rc;
RECT aRect;
RECT bRect;
RECT bcRect;
if(bCalcValidRects == TRUE)
{
CopyRect(&aRect,&lpncsp->rgrc[1]);
CopyRect(&bRect,&lpncsp->rgrc[0]);
bcRect.left = bRect.left;
bcRect.top = bRect.top - nTHight;
bcRect.right = bRect.right;
bcRect.bottom = bRect.bottom;
CopyRect(&lpncsp->rgrc[0],&bcRect);
CopyRect(&lpncsp->rgrc[1],&bRect);
CopyRect(&lpncsp->rgrc[2],&aRect);
}
else
{
rc = (RECT *)lpncsp;
rc->left = rc->left;
rc->top = rc->top - nTHight;
rc->right = rc->right;
rc->bottom = rc->bottom;
}
CFrameWnd::OnNcCalcSize(bCalcValidRects, lpncsp);
}
Because the entire window is client region, I have to adjust the UI control placement for the frame, but I don't know how to handle this problem.
For example, below red rectangle (all UI component) should be shifted into the original coordinate of the client area before removing the non client part.
CWnd::GetWindowRect gives you the rectangle of the window on screen. The dimensions of the caption, border, and scroll bars, if present, are included.
CWnd::GetClientRect gives you the client rectangel of the window. The left and top members will be 0. The right and bottom members will contain the width and height of the window.
CWnd::ScreenToClientand CWnd::ClientToScreen calculate a point or rectangle from the client area to screen coordinates and back to screen.
AdjustWindowRect calculates the required window rectangle, based on the client rectangle of the window.
Here is afunction which calcualtes the margins of a window:
void CalculateWndMargin( const CWnd &wnd, int &leftM, int &rightM , int &topM, int &bottomM )
{
CRect wndRect;
wnd.GetWindowRect( wndRect );
CRect screenRect;
wnd.GetClientRect( screenRect );
wnd.ClientToScreen( screenRect );
leftM = screenRect.left - wndRect.left;
rightM = wndRect.right - screenRect.right;
topM = screenRect.top - wndRect.top;
bottomM = wndRect.bottom - screenRect.bottom;
}

why do cx/cy from GetWindowRect(rcWindow2) differ from cx/cy fed into OnSize?

I want to get the cx and cy during OnInitDialog of a CDialog.
I can do this with the following code:
myDialog::OnInitDialog()
{
CRect rcWindow2;
this->GetWindowRect(rcWindow2);
CSize m_szMinimum = rcWindow2.Size();
int width = m_szMinimum.cx;
int height = m_szMinimum.cy;
}
However, the cx and cy in the OnInitDialog is not the same as cx and cy which got into OnSize:
void myDialog::OnSize(UINT nType, int cx, int cy)
From OnInitDialog: cx=417, cy=348
From OnSize : cx=401, cy=310
looks like might be the borders but i can't figure it out.
Suggestions as to how to get the same xy data in OnInitDialog as is fed into OnSize would be appreciated.
Inheritance:
myDialog -> CDialog -> CWnd
GetWindowRect returns window's Top-Left position in screen coordinates. Width and height of window includes border thickness and the height of the caption.
GetClientRect always returns zero for Top-Left corner. Width and height is the same values as OnSize.
While we are on the subject, this can also get confusing when it comes to moving child windows. Because SetWindowPos needs client coordinates, while GetWindowRect returns screen coordinates only. Screen/Client conversion will be needed like this:
void GetWndRect(CRect &rc, HWND child, HWND parent)
{
GetWindowRect(child, &rc);
CPoint offset(0, 0);
ClientToScreen(parent, &offset);
rc.OffsetRect(-offset);
}
Now we can move a button in dialog box:
CWnd *child = GetDlgItem(IDOK);
CRect rc;
GetWndRect(rc, child->m_hWnd, m_hWnd);
rc.OffsetRect(-5, -5);
child->SetWindowPos(0, rc.left, rc.top, 0, 0, SWP_NOSIZE);