Click here to Skip to main content
15,896,502 members
Articles / Programming Languages / Visual C++ 9.0

StyleDialog - A transparent dialog class with an optional custom frame

Rate me:
Please Sign up or sign in to vote.
4.84/5 (21 votes)
3 Aug 2008CPOL6 min read 74.6K   10K   95  
StyleDialog may be used independently or in conjunction with the Style Toolkit.
// Border.cpp: implementation of the Border class.
//
// Author:  Darren Sessions
//          
//
// Description:
//     Border Draws or Fills rounded rectangles for GDI+.  It was implemented
//	   to overcome the asymmetric issues associated with GDI+ round rectangles
//
// History
//     Version 1.0 - 2008 June 24
//     - Initial public release
//
// License:
//     This software is released under the Code Project Open License (CPOL),
//     which may be found here:  http://www.codeproject.com/info/eula.aspx
//     You are free to use this software in any way you like, except that you 
//     may not sell this source code.
//
//     This software is provided "as is" with no expressed or implied warranty.
//     I accept no liability for any damage or loss of business that this 
//     software may cause.
//
///////////////////////////////////////////////////////////////////////////////

#include "stdafx.h"
#include "Border.h"

#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif

//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////

Border::Border()
{
}

Border::~Border()
{

}

//=============================================================================
//
// GetPath()
//
// Purpose:     Defines a the appropriate path and places it in the GraphicsPath
//
// Parameters:  pPath		- [out] pointer to GraphicsPath that will recieve the 
//									path data
//				r			- [in]	Rect that defines the round rectangle boundaries
//
// Returns:     None
//
void Border::GetPath(GraphicsPath *pPath, Rect r)
{	
	if(shape == RECTANGLE)
	{
		GetRectPath(pPath, r);
	}
	else
	{
		GetRoundRectPath(pPath, r);
	}
}


//=============================================================================
//
// GetRectPath()
//
// Purpose:     Defines a Rectangle and places it in the GraphicsPath
//
// Parameters:  pPath		- [out] pointer to GraphicsPath that will recieve the 
//									path data
//				r			- [in]	Rect that defines the round rectangle boundaries
//
// Returns:     None
//
void Border::GetRectPath(GraphicsPath *pPath, Rect r)
{
	pPath->Reset();
	pPath->AddRectangle(r);
	pPath->CloseFigure();
}

//=============================================================================
//
// GetRoundRectPath()
//
// Purpose:     Defines a Rounded Rectangle and places it in the GraphicsPath
//
// Parameters:  pPath		- [out] pointer to GraphicsPath that will recieve the 
//									path data
//				r			- [in]	Rect that defines the round rectangle boundaries
//
// Returns:     None
//
void Border::GetRoundRectPath(GraphicsPath *pPath, Rect r)
{
	// set the diameter
	int dia = 2*radius;

	// this isn't a round rect anymore but it 
	// can occur with progress bars
	if( (dia > r.Width) || (dia > r.Height) )
	{
		r.Inflate(0, -(dia/r.Width)); // add other case
		pPath->AddEllipse(r);
		return;
	}

	// define a corner 
	Rect Corner(r.X, r.Y, dia, dia);

	// begin path
	pPath->Reset();

	// top left
	pPath->AddArc(Corner, 180, 90);	

	// tweak needed for radius of 10 (dia of 20)
	if(dia == 20)
	{
		Corner.Width += 1; 
		Corner.Height += 1; 
		r.Width -=1; r.Height -= 1;
	}

	// top right
	Corner.X += (r.Width - dia - 1);
	pPath->AddArc(Corner, 270, 90);	
	
	// bottom right
	Corner.Y += (r.Height - dia - 1);
	pPath->AddArc(Corner,   0, 90);	
	
	// bottom left
	Corner.X -= (r.Width - dia - 1);
	pPath->AddArc(Corner,  90, 90);

	// end path
	pPath->CloseFigure();
}


//=============================================================================
//
// Draw()
//
// Purpose:     Draws a border with a solid pen
//
// Parameters:  pGraphics	- [in]	pointer to the Graphics device
//				r			- [in]	Rect that defines the round rectangle boundaries
//				ulclr		- [in]	Color value for the pen
//				brclr		- [in]  Bottom right pen color (SQUARE border only)
//				width		- [in]  width of the border
//		
// Returns:     None
//
void Border::Draw(Graphics* pGraphics, Rect r,  Color ulclr, Color brclr, int width)
{
	if(shape == RECTANGLE)
	{
		DrawRect(pGraphics, r, ulclr, brclr, width);
	}
	if(shape == ELLIPSE)
	{
		Pen pen(ulclr, 1);
		pen.SetAlignment(PenAlignmentCenter);
		RectF rf((float)r.X-1, (float)r.Y-1, (float)r.Width+1, (float)r.Height+1);
		pGraphics->DrawEllipse(&pen, rf);
	}
	else if(shape == TRANSITION)
	{
		// disable smoothing for the clip fill
		SmoothingMode oldMode = pGraphics->GetSmoothingMode();
		pGraphics->SetSmoothingMode(SmoothingModeNone);

		// fill the transition zone using a clip region
		pGraphics->SetClip(r, CombineModeReplace);

		// fill one pixel width into the round rect if width > 1
		// this supresses any transition antialiasing
		Rect cr(r); if(width > 1) cr.Inflate(-1, -1);

		// use local clip function for round rect
		SetBorderClip(pGraphics, cr, CombineModeExclude);

		// fill with the border color
		SolidBrush sbr(ulclr);
		pGraphics->FillRectangle(&sbr, r);

		// clear the clipping region
		pGraphics->ResetClip();

		// restore any smoothing mode
		pGraphics->SetSmoothingMode(oldMode);

		// now draw the round border
		DrawRoundRect(pGraphics, r, ulclr, width);
	
	}
	else
	{
		DrawRoundRect(pGraphics, r, ulclr, width);
	}
}

//=============================================================================
//
// DrawRect()
//
// Purpose:     Draws a rectangle with a solid pen
//
// Parameters:  pGraphics	- [in]	pointer to the Graphics device
//				r			- [in]	Rect that defines the round rectangle boundaries
//				color		- [in]	Color value for the brush
//				width		- [in]  width of the border
//		
// Returns:     None
//
void Border::DrawRect(Graphics* pGraphics, Rect r,  Color ulclr, Color brclr, int width)
{
	int i, left, top, bottom, right;

	// set to pixel mode
	Unit oldPageUnit = pGraphics->GetPageUnit();
	pGraphics->SetPageUnit(UnitPixel);

	// define the upper left pen
	Pen ulpen(ulclr, 1); ulpen.SetAlignment(PenAlignmentCenter);

	// define the bottom right pen
	Pen brpen(brclr, 1); brpen.SetAlignment(PenAlignmentCenter);

	// not sure why
	Rect rc(r); 

	for(i = 0; i < width; i++)
	{
		left = rc.X; top = rc.Y; bottom = rc.GetBottom()-1; right = rc.GetRight()-1;

		// left
		pGraphics->DrawLine(&ulpen, left, top, left, bottom);

		// top
		pGraphics->DrawLine(&ulpen, left, top, right, top);

		// right
		pGraphics->DrawLine(&brpen, right, top+1, right, bottom);

		// bottom
		pGraphics->DrawLine(&brpen, left+1, bottom, right, bottom);

		rc.Inflate(-1, -1);
	}

	// restore page unit
	pGraphics->SetPageUnit(oldPageUnit);

}

//=============================================================================
//
// DrawRoundRect()
//
// Purpose:     Draws a rounded rectangle with a solid pen
//
// Parameters:  pGraphics	- [in]	pointer to the Graphics device
//				r			- [in]	Rect that defines the round rectangle boundaries
//				color		- [in]	Color value for the brush
//				width		- [in]  width of the border
//		
// Returns:     None
//
void Border::DrawRoundRect(Graphics* pGraphics, Rect r,  Color color, int width)
{
	// set to pixel mode
	Unit oldPageUnit = pGraphics->GetPageUnit();
	pGraphics->SetPageUnit(UnitPixel);

	// define the pen
	Pen pen(color, 1);	
	pen.SetAlignment(PenAlignmentCenter);

	// get the corner path
	GraphicsPath path;

	// get path
	GetRoundRectPath(&path, r);

	// draw the round rect
	pGraphics->DrawPath(&pen, &path);	

	// save smoothing mode
	SmoothingMode oldMode = pGraphics->GetSmoothingMode(); 

	// if width > 1
	for(int i=1; i<width; i++)
	{
		// disable smoothing for brclr lines
		if(i == 1)				pGraphics->SetSmoothingMode(SmoothingModeNone);
		// re enable for last line
		if(i == (width - 1))	pGraphics->SetSmoothingMode(oldMode);

		// left stroke
		r.Inflate(-1, 0);

		// get the path
		GetRoundRectPath(&path, r);
		
		// draw the round rect
		pGraphics->DrawPath(&pen, &path);

		// up stroke
		r.Inflate(0, -1);

		// get the path
		GetRoundRectPath(&path, r);
		
		// draw the round rect
		pGraphics->DrawPath(&pen, &path);
	}

	// restore page unit
	pGraphics->SetPageUnit(oldPageUnit);
}

//=============================================================================
//
// FillRoundRect()
//
// Purpose:     Fills a rounded rectangle with a solid brush.  Draws the border
//				first then fills in the rectangle.
//
// Parameters:  pGraphics	- [in]	pointer to the Graphics device
//				r			- [in]	Rect that defines the round rectangle boundaries
//				color		- [in]	Color value for the brush
//				radius		- [in]  radius of the rounded corner
//		
// Returns:     None
//
void Border::FillRoundRect(Graphics* pGraphics, Rect r,  Color color, int radius)
{
	SolidBrush sbr(color);
	FillRoundRect(pGraphics, &sbr, r, color, radius);
}

//=============================================================================
//
// FillRoundRect()
//
// Purpose:     Fills a rounded rectangle with the passed brush.  Fills the 
//				rectangle then drawst the border.
//
// Parameters:  pGraphics	- [in]	pointer to the Graphics device
//				pBrush		- [in]  pointer to a Brush
//				r			- [in]	Rect that defines the round rectangle boundaries
//				color		- [in]	Color value for the border (needed in case the 
//									brush is a type other than solid)
//				radius		- [in]  radius of the rounded corner
//		
// Returns:     None
//
void Border::FillRoundRect(Graphics* pGraphics, Brush* pBrush, Rect r, Color border, int radius)
{
	int dia	= 2*radius;

	// set to pixel mode
	Unit oldPageUnit = pGraphics->GetPageUnit();
	pGraphics->SetPageUnit(UnitPixel);

	// define the pen
	Pen pen(border, 1);	
	pen.SetAlignment(PenAlignmentCenter);

	// get the corner path
	GraphicsPath path;

	// get path
	GetRoundRectPath(&path, r);

	// fill
	pGraphics->FillPath(pBrush, &path);

	// draw the border last so it will be on top in case the color is different
	pGraphics->DrawPath(&pen, &path);

	// restore page unit
	pGraphics->SetPageUnit(oldPageUnit);
}

//=============================================================================
//
// SetBorderClip()
//
// Purpose:     Sets the clipping region to the inside of and including this border.
//				The SetClip(path) of GDI+ has the same problem that a fill path does 
//				on a round rectangle, so use GDI's function instead
//
// Parameters:  pGraphics	- [in]	pointer to the Graphics device
//		
// Returns:     None
//
void Border::SetBorderClip(Graphics* pGraphics, Rect rc, CombineMode mode)
{
	CRgn frgn; int dia;
	
	dia = 2*radius;

	if(shape == UNDEFINED)
	{
		// the border needs a shape
		ASSERT(FALSE);
	}
	if(shape == RECTANGLE)
	{
		frgn.CreateRectRgn(rc.X, rc.Y, rc.GetRight()+1, rc.GetBottom()+1);
	}
	else if(shape == ELLIPSE)
	{
		frgn.CreateEllipticRgn(rc.X-1, rc.Y-1, rc.GetRight()+2, rc.GetBottom()+2);
	}
	// handle this special case - TODO: add for Height < dia
	else if(rc.Width < dia)
	{
		rc.Width++; rc.Height++;
		Rect lrc(rc); lrc.Width = 2*dia; 
		Rect rrc(rc); rrc.Width = 2*dia; rrc.X = rrc.X - (rrc.Width - rc.Width);
		CRgn lrgn, rrgn;
		lrgn.CreateRoundRectRgn(lrc.X, lrc.Y, lrc.GetRight()+1, lrc.GetBottom()+1, dia+1, dia+1);
		rrgn.CreateRoundRectRgn(rrc.X, rrc.Y, rrc.GetRight()+1, rrc.GetBottom()+1, dia+1, dia+1);
		frgn.CreateRectRgn(0,0,0,0);
		frgn.CombineRgn(&lrgn, &rrgn, RGN_AND);
	}
	else
	{
		frgn.CreateRoundRectRgn(rc.X, rc.Y, rc.GetRight()+1, rc.GetBottom()+1, dia+1, dia+1);
	}

	Region grgn(frgn);

	pGraphics->SetClip(&grgn, mode);

}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior)
United States United States
I am currently working as a consultant in Southern California.

I have worked as a Hardware Engineer, Firmware Engineer, Software Engineer and Applications Engineer.

I spent 13 years in the Disk Drive industry and the last 7 working in GPS.

Comments and Discussions