2006-06-13

Gradient Backgrounds in your forms with GDI+

After reading this post, read also: Gradient backgrounds in your forms with gdi+ part 2



 

Gdi+ brings us the possibility to create many cool effects such as gradient colors. This feature was not included in _gdiplus.vcx, but can also be accessed easily with a simple call to the flat API. A great part of the codes below that are related to the creation of the gradient brush is from Bo Durban.

To apply this to forms is really easy. add the codes below to the load, resize and destroy events of any form:

 

load event

LOCAL lcGradFile
m.lcGradFile = ADDBS(SYS(2023)) + SYS(2015) + ".bmp"
THIS.ADDPROPERTY("cTempGradFile", m.lcGradFile)

IF FILE(m.lcGradFile)
    CLEAR RESOURCES (m.lcGradFile)
ENDIF

LOCAL lnRgbColor1
m.lnRgbColor1 = RGB(60,30,180) && blue

* create gradient image 
SET CLASSLIB TO HOME() + "ffc/_gdiplus.vcx" ADDITIVE

* declare API 
DECLARE LONG gdipCreateLinebrushI IN GDIPLUS.DLL ;
    STRING point1, STRING point2, ;
    LONG color1, LONG color2, ;
    LONG wrapmode, LONG @linegradient

* create a colorobject and store argb color values to variables 
LOCAL loClr AS gpColor OF HOME() + "ffc/_gdiplus.vcx"
LOCAL lnColor1, lnColor2
m.loClr = CREATEOBJECT("gpcolor")
m.loClr.FoxRgb = m.lnRgbColor1
m.lnColor1 = m.loClr.ARGB
m.loClr.FoxRgb = RGB(255,255,255) && white
m.lnColor2 = m.loClr.ARGB

* create a bitmap 
LOCAL loBmp AS gpBitmap OF HOME() + "ffc/_gdiplus.vcx"
m.loBmp = CREATEOBJECT("gpbitmap")
m.loBmp.CREATE(1,THISFORM.HEIGHT)

* get a bitmap graphics object 
LOCAL loGfx AS gpGraphics OF HOME() + "ffc/_gdiplus.vcx"
m.loGfx = CREATEOBJECT("gpgraphics")
m.loGfx.CreateFromImage(m.loBmp)

* get a gradient brush 
LOCAL loBrush AS gpBrush OF HOME() + "ffc/_gdiplus.vcx"
LOCAL hBrush && brush handle
m.hBrush = 0
gdipCreateLinebrushI(BINTOC(0,"4rs") + BINTOC(0,"4rs"), ;
      BINTOC(0,"4rs") + BINTOC(THISFORM.HEIGHT,"4rs"), ;
      m.lnColor1, m.lnColor2, 0, @m.hBrush)
m.loBrush = CREATEOBJECT("gpbrush")
m.loBrush.sethandle(m.hBrush, .T.)

* fill the bitmap with our gradient 
m.loGfx.FillRectangle(m.loBrush,0,0,1,THISFORM.HEIGHT)
m.loBmp.SaveToFile(m.lcGradFile,"image/bmp")

THISFORM.ADDOBJECT("imgbackground","image")
WITH THISFORM.imgbackground
    .STRETCH = 2
    .WIDTH = THISFORM.WIDTH
    .HEIGHT = THISFORM.HEIGHT
    .PICTURE = m.lcGradFile
    .VISIBLE = .T.
ENDWITH
RETURN


resize event

Thisform.imgbackground.Width = Thisform.Width 
Thisform.imgbackground.Height = Thisform.Height


destroy event

WITH THISFORM
    IF FILE(.cTempGradFile)
        CLEAR RESOURCES (.cTempGradFile)
        ERASE (.cTempGradFile)
    ENDIF
ENDWITH



New GDI+ classes

The code I presented in the Load event works pretty fine, but sounds really ugly, when comparing to what we are able to do with GdiPlusX

The code below will replace all codes related to the creation of the gradient color in the load event.

* create a GDI+ linear gradient image using GdiPlusX
DO LOCFILE("system.app")

LOCAL loBmp AS xfcBitmap
LOCAL loGfx AS xfcGraphics
LOCAL loBrush AS xfcBrush
WITH _SCREEN.SYSTEM.Drawing
    m.loBmp = .BITMAP.New(1, THISFORM.HEIGHT)
    m.loGfx = .Graphics.FromImage(m.loBmp)
    m.loBrush = .Drawing2d.LinearGradientBrush.New( ;
          .Rectangle.New(0, 0, 1, THISFORM.HEIGHT), ;
          .COLOR.FromRgb(lnRgbColor1), .COLOR.White, 1)
    m.loGfx.FillRectangle(m.loBrush, m.loBrush.Rectangle)
    m.loBmp.SAVE(lcGradFile, .Imaging.ImageFormat.bmp)
ENDWITH




 

Related post: Gradient backgrounds in your VFP forms with GDI+ Part 2

14 comments:

  1. Fantastic. I love your blog and your great examples. This is a good one. Too easy!

    ReplyDelete
  2. Great post Cesar. I believe this is one of the first examples using the new GDIPlusX library that has been posted outside of SednaX. You've made my day.

    ReplyDelete
  3. Hey Thanks Cesar. My question about using RGB colours has been answered by this:
    .Color.FromRGB(lnRGBColor1),

    I saw the Method but did not know how to use it.

    Also a tutorial on how the xfc classes are instantiated using XFCSystem :
    and how the _access events are used to create new objects, etc.

    I am totally confused.
    Hey Bernard, I'm glad to know it helped you. There's still a lot of work to do in the new classes. But you can easily get great information from MSDN. Their target is to immitate the GDI+ classes from VB .NET. In MSDN and in other links you'll find lots of examples using these classes. Start using the keywords "system.drawing".
    Regards
    Cesar

    ReplyDelete
  4. Cesar!

    Yes! Another exciting code example! Beautiful!!! For those following this thread, Cesar's technique can also be used to add gradient backgrounds to containers as well.

    Obrigado!

    Some additional ideas:

    1. You can eliminate your Resize() code using VFP 9's awesome Anchor property:

    * make following changes to Load method
    *!* .Width = Thisform.Width
    *!* .Height = Thisform.Height
    .Move( 0, 0, Thisform.Width, Thisform.Height )
    .Anchor = 15

    2. You can eliminate your Destroy() code using VFP 9's not-so-awesome PictureVal property:

    * make following changes to Load method
    *!* .Picture = lcGradFile
    .PictureVal = filetostr( lcGradFile )
    delete file (lcGradFile)

    The reason I say 'not-so-awesome' is that using the PictureVal property *significantly* slows down form painting during a form resize and, interestingly enough, also seems to display a slightly different appearance than when the image is loaded from file via .Picture.

    Anyone have any feedback on why PictureVal performs so poorly and/or a workaround? Intuitively, I would have thought that PictureVal would have been faster than Picture since the image is in memory vs. on disk.

    3. If you have a fixed size form (or container!) you can skip the image object altogether and set the form's or container's Picture property directly to the gradient file you generated (lcGradFile).

    4. If you're thinking about using this technique in many areas, Cesar's code is perfect for building a self-contained image gradient class that sizes (and anchors) to its parent and whose destroy event automatically deletes the temp gradient file.

    A question for you and your audience:

    Any ideas on a technique for generating gradients that match XP color schemes? Office 2003 and many 3rd party menu/toolbar components use a technique that generates various types of gradients based on the current XP theme colors. Since all these applications do this consistently, there must be a common technique being used.

    If we (VFP community) want to make our applications look like a million dollars then this is the MILLION DOLLAR question :)

    Thanks again Cesar!

    Malcolm

    ReplyDelete
  5. Thank you all for the kind words.

    Craig Boyd, you don't need to be so humble, because your and Bo's great work is going to change the lives of VFP users completely. In my honest opinion, after you release the new GDI+ classes, we will be able to tell the world that VFP10 was released. I'm a big fan of you both. You can't imagine how much I've learned just looking at your code. Thank you. Thank you. Thank you.

    ReplyDelete
  6. Hey Malcolm,
    All your comments, ideas and fixes are always welcome !
    I hope to apply your ideas in my next post.
    Regards
    Cesar

    ReplyDelete
  7. Cool.

    How can I make the gradient be in the diagonal ?

    ReplyDelete
  8. Hi Claudio,
    Pls Check this link for diagonal LinearGradientBrushes
    http://weblogs.foxite.com/cesarchalom/archive/2006/06/22/1906.aspx

    ReplyDelete
  9. I try using this codes for making my forms more beautifull, but when i build a executable then it doesnt work, i dont know what else i should do, i just copy and pasted the code in my forms
    You probably need to add the library _gdiplus.vcx to your project. What's the error you are getting ?

    ReplyDelete
  10. Versión en Español de este artículo en / Spanish version at http://www.portalfox.com/article.php?sid=2248

    ReplyDelete
  11. Dear All,

    I was used this method at my application where i build with vfp 9.0, however this method is running well and smooth if i run the apps with vfp (.pjx)...

    The problem is appear when i build my application to executable (.exe)and i put the *.DLL in the same directory with executable file, like gdiplus.dll,mscomctl.dll,msvcr71.dll,vfp9enu.dll,vfp9r.dll,vfp9renu.dll and zdlib.dll. I was distrust the gdiplus.dll file is the root of the problem, but i was tried to copy many kinds size(1.607kb,1.661kb,1.672kb,1.607kb) of the gdiplus file to that directory,the result is same where the colour is grey and can't be change with other colour from GRB setting.

    IF after that i run the application (.pjx)from vfp without remove all .dll file the problem still happened,but if i remove all .dll files from that direktory and the application(.pjx) running from VFP,the application is back running normally and i can change the colour.

    there is any kinds of solution for my problem? Thanks

    ReplyDelete
  12. Dear All,

    I was used this method at my application where i build with vfp 9.0, however this method is running well and smooth if i run the apps with vfp (.pjx)...

    The problem is appear when i build my application to executable (.exe)and i put the *.DLL in the same directory with executable file, like gdiplus.dll,mscomctl.dll,msvcr71.dll,vfp9enu.dll,vfp9r.dll,vfp9renu.dll and zdlib.dll. I was distrust the gdiplus.dll file is the root of the problem, but i was tried to copy many kinds size(1.607kb,1.661kb,1.672kb,1.607kb) of the gdiplus file to that directory,the result is same where the colour is grey and can't be change with other colour from GRB setting.

    IF after that i run the application (.pjx)from vfp without remove all .dll file the problem still happened,but if i remove all .dll files from that direktory and the application(.pjx) running from VFP,the application is back running normally and i can change the colour.

    there is any kinds of solution for my problem? Thanks

    ReplyDelete