Re: Windows Forms General GDI+ button image painting problem
Obaid Riaz
Hi Wright,
Thanks for the reply, following is the code for painting buttons. Please have a look
public override bool OnPaint( PaintEventArgs robjArgs, Control rctlComponent )
{
AbstractButton btnComp = rctlComponent as AbstractButton;
if( btnComp == null )
return false; // do nothing
Graphics objComponentGraphics = robjArgs.Graphics;
//This is done to repaint only that area which is actually invalidated
objComponentGraphics.SetClip( robjArgs.ClipRectangle );
if( mstBkColor.IsEmpty )
mstBkColor = DrawingUtils.SelectSolidColor( this.GetBackColor( btnComp.PlafClassName ), btnComp.ComponentBackColor );
if( mstFrColor.IsEmpty)
mstFrColor = DrawingUtils.SelectSolidColor( this.GetForeColor( btnComp.PlafClassName ), btnComp.ForeColor );
if( mstHoverColor.IsEmpty)
mstHoverColor = DrawingUtils.SelectSolidColor( this.GetHoverColor( btnComp.PlafClassName ), Color.Yellow );
//This rectangle can change its size for e.g. on resize, this is the reason why it hasn't been made part of state
Rectangle mstImageRect = btnComp.ClientRectangle;
mstImageRect = new Rectangle( mstImageRect.X + 1, mstImageRect.Y + 1, mstImageRect.Width - 3, mstImageRect.Height - 3 );
//Setting the button face color
Color stFaceColor = mstBkColor;
if( !btnComp.Enabled )
stFaceColor = Color.Gray;
else if( ( ( btnComp.State & ButtonUIState.Pressed ) == ButtonUIState.Pressed ||
( btnComp.State & ButtonUIState.MouseOver ) == ButtonUIState.MouseOver ) )
stFaceColor = mstHoverColor;
DrawButtonImage( objComponentGraphics, robjArgs.ClipRectangle, mstImageRect, stFaceColor, btnComp );
string strText = btnComp.Text;
if( strText != null && strText.Length != 0 )
{
PaintText( objComponentGraphics, btnComp, strText, mstFrColor, true, OFFSET_BORDER );
}
if( btnComp.Image != null )
PaintIcon( objComponentGraphics, btnComp, true, OFFSET_BORDER );
if( btnComp.Focused && btnComp.Enabled )
PaintFocus( objComponentGraphics, btnComp );
return true;
}
private void DrawButtonImage( Graphics robjArgs, Rectangle rstClipRect, Rectangle rstRect, Color rstBackColor, AbstractButton rbtnComp )
{
string strHash = rstRect.GetHashCode().ToString();
Bitmap bmpImage = (Bitmap) sobjImageMap[ strHash ];
if( bmpImage == null)
{
bmpImage = this.GetButtonFaceImage( rstRect );
sobjImageMap.Add( strHash, bmpImage );
}
//This is done to give the desired look to the button otherwise the button would give a brighter look
ColorMatrix cmMatrix = GetColorMatrix( rstBackColor );
ImageAttributes attrFill = new ImageAttributes();
attrFill.SetColorMatrix( cmMatrix, ColorMatrixFlag.Default, ColorAdjustType.Bitmap );
robjArgs.DrawImage( bmpImage, rstClipRect, rstClipRect.X, rstClipRect.Y, rstClipRect.Width,
rstClipRect.Height, GraphicsUnit.Pixel, attrFill );
//Here, the undesired region of the button is cut or the desired region is identified and added to the button region
if( !mstClientRectangle.Equals( rstRect ) )
{
mstClientRectangle = rstRect;
Region objRegion = sobjRegionMap[ strHash ] as Region;
if( objRegion == null )
{
GraphicsPath objPath = BitmapToGraphicsPath( bmpImage );
objRegion = new Region( objPath );
sobjRegionMap.Add( strHash, objRegion );
objPath.Dispose();
rbtnComp.Region = objRegion;
}
else
rbtnComp.Region = objRegion.Clone();
}
}
private Bitmap GetButtonFaceImage( Rectangle rstRect )
{
Bitmap bmpImage = new Bitmap( rstRect.Width, rstRect.Height, System.Drawing.Imaging.PixelFormat.Format32bppPArgb );
Graphics objGfx = Graphics.FromImage( bmpImage );
int iLeft = simgLeft.Width;
int iRight = simgRight.Width;
int iFill = 0;
if ( simgLeft.Width + simgRight.Width > rstRect.Width )
iLeft = iRight = rstRect.Width / 2;
else
iFill = rstRect.Width - ( iLeft + iRight ) - 1;
//The code below draws the left, right and middle portions of the button. Only the middle portion is streched
//and its proportional to the width of the button.
Rectangle stLeft = new Rectangle( rstRect.Left, rstRect.Top, iLeft, rstRect.Height );
objGfx.DrawImage( simgLeft, stLeft, 0, 0, simgLeft.Width, simgLeft.Height, GraphicsUnit.Pixel );
if ( iFill > 0 )
{
Rectangle stFill = new Rectangle( rstRect.Left + iLeft, rstRect.Top, iFill, rstRect.Height);
ImageAttributes attrFill = new ImageAttributes();
attrFill.SetWrapMode( WrapMode.Tile );
objGfx.DrawImage( simgFill, stFill, 0, 0, simgFill.Width, simgFill.Height, GraphicsUnit.Pixel, attrFill );
}
Rectangle stRight = new Rectangle( rstRect.Width - iRight, rstRect.Top, iRight, rstRect.Height );
objGfx.DrawImage( simgRight, stRight, 0, 0, simgRight.Width, simgRight.Height, GraphicsUnit.Pixel );
objGfx.Dispose();
return bmpImage;
}
public static GraphicsPath BitmapToGraphicsPath( Bitmap robjBitmap )
{
int iHeight = robjBitmap.Height;
int iWidth = robjBitmap.Width;
// Create GraphicsPath for our bitmap calculation
GraphicsPath objGraphicsPath = new GraphicsPath();
int iColNext = 0;
Rectangle stOpaqueRectangle = Rectangle.Empty;
for( int iRow = 0; iRow < iHeight; iRow ++ )
{
for( int iCol = 0; iCol < iWidth; iCol ++ )
{
if( robjBitmap.GetPixel( iCol , iRow).A != 0 )// if color is opaque
{
iColNext = iWidth - iCol;
stOpaqueRectangle.X = iCol;
stOpaqueRectangle.Y = iRow;
stOpaqueRectangle.Width = iColNext - iCol + 1;
stOpaqueRectangle.Height = 1;
objGraphicsPath.AddRectangle( stOpaqueRectangle );
break;
}
}
}
return objGraphicsPath;
}
protected void PaintText( Graphics robjGfx, AbstractButton rbtnComp, string rstrButtonText, Color rstFrColor, bool rbPressEffect, int riBorderWidth )
{
if ( !mbIsLoaded )
{
//Getting the button font in which the button text would be displayed
mobjButtonFont = this.GetFont( rbtnComp.PlafClassName );
if( mobjButtonFont == null )
mobjButtonFont = rbtnComp.Font;
else if( rbtnComp.Font != mobjButtonFont )
mobjButtonFont = new Font( mobjButtonFont.Name, rbtnComp.Font.Size, rbtnComp.Font.Style );
//Setting the string format in which the button text would be shown
meContentAlign = rbtnComp.TextAlign;
mobjStringFormat = GetStringFormat( meContentAlign );
mobjStringFormat.HotkeyPrefix = HotkeyPrefix.Show;
mbIsLoaded = true;
}
//Getting the size of the text, this can change as the button width can be changed on resize etc.
Size stTextSize = robjGfx.MeasureString( rstrButtonText, mobjButtonFont, rbtnComp.Width, StringFormat.GenericDefault ).ToSize();
Rectangle stRect = new Rectangle( 0, 0, stTextSize.Width + 1, stTextSize.Height + 1 );
SetRectLocation( ref stRect, meContentAlign, rbtnComp.Height, rbtnComp.Width, riBorderWidth );
// for efficient text rendering
robjGfx.TextRenderingHint = TextRenderingHint.AntiAlias;
if( ( rbtnComp.State & ButtonUIState.Disabled ) == ButtonUIState.Disabled )
ControlPaint.DrawStringDisabled( robjGfx, rstrButtonText, mobjButtonFont, rstFrColor, stRect, StringFormat.GenericDefault );
else
{
//This is done to show the button press effect by adding a value to the X and Y coordinates of the text rectangle.
if( ( rbPressEffect && ( ( rbtnComp.State & ButtonUIState.Pressed ) == ButtonUIState.Pressed ) ) )
{
stRect.X = stRect.X + 1;
stRect.Y = stRect.Y + 1;
}
robjGfx.DrawString( robjGfx, rstrButtonText, mobjButtonFont, rstFrColor, stRect, mobjStringFormat );
}
}