Showing posts with label GDIPlusX. Show all posts
Showing posts with label GDIPlusX. Show all posts

Friday, October 23, 2009

Southwest Fox 2009, Day 2

Once again, I was up early but really fired up: today was F1 racing day, something Rick, Cathy, and I have been trashing talking each other over for the past year.

After breakfast, I presented my GDIPlusX session. This was a fun session to do. I started by going over the basics of GDIPlusX: how to draw, using colors, drawing text and images, etc. Then I dug into practical uses, including measuring strings accurately, creating gradient images, and implement a “lightbox” effect Bernard Bout blogged about. Almost all of the examples I showed came from production code I use in Stonefield Query.

clip_image002

Next was my Stonefield Query vendor session. Interestingly, all of the people who showed up were customers or already familiar with Stonefield Query, so rather than doing the usual demo, I covered the same things we discussed at Friday night’s developer meeting. One of the cool new features I showed is “application views”. The problem with most reporting solutions is that they show a database-centric view of the data in an application. Even with nice descriptive names for tables and fields, it still isn’t a natural view of the data for the user. With application views, you can mold the data dictionary so it’s organized exactly like the data entry forms in your application, making it much easier for the user to find exactly which items they want to report on.

I was going to see Alan Steven’s OOP session but started chatting outside with Cathy and Toni (and others who happened by from time to time) and ended up spending the entire session there. That’s one of the nice things about a conference: if you don’t feel like going to a session, you can meet new people, chat with friends, and discuss new solutions to problems.

Lunch was build your own hamburgers with either meat patties or portobello mushrooms. Like some others, I actually went for both, which was a very tasty combination.

After lunch, I went to see Jody Meyer’s Office Automation session. Jody is one of our first-time speakers so I wanted to see how she did, plus learn more about Office Automation. One of my developers, Trevor Mansuy, was also in this session and had lots of questions for Jody, as he’s currently working on a couple of new features for Stonefield Query involving Excel.

Southwest Fox 2009 005

Next up was Christof’s Excelporting session. Christof did his usual excellent job and was even funnier than usual. Creating Excel documents by outputting XML is not only incredibly fast but doesn’t require a license for Excel. Unfortunately, and this was totally not his fault, the three hours of sleep I was getting for the past several nights finally caught up to me and I dozed off several times. Steve Bodnar was quite impressed with the various angles I managed to twist myself into without actually falling off my chair! Thank goodness Christof’s white paper and samples are on the conference CD so I can see what I missed.

The last timeslot of the day was the repeat of my VPC session. Shortly after I started, more people started filing into the room. I mentioned that there were open seats near the front, but they seemed content to stand at the back. Then more people came in. Then more. And even more. By this point, there were at least 40 people standing at the back of the room and a lot just outside the room. I knew something was up but had no idea what was going on. Suddenly, Craig Boyd screamed out and the attack began: I was hit by HUNDREDS of darts from the 40 or so Nerf guns these people hid behind their backs. The attack lasted a minute or so, then Craig yelled “Pull back” and they all filed out, leaving me to stand in a pile of darts. If you want to watch for yourself, at least three people recorded it and put it on YouTube:

http://www.youtube.com/watch?v=rRoKWdG7cV0
http://www.youtube.com/watch?v=jxLKBmhxIvI
http://www.youtube.com/watch?v=VO6MVu1eEJY

Talking to Craig later, this was apparently revenge for my thinking about messing with him on Friday. Let me get this straight: you want to get revenge for something I thought about pretending to do? In the words of the immortal Bugs Bunny, “of course you realize this means war”. You should come to Southwest Fox 2010 just to see what I have planned for Craig!

Rick, Therese, Christof, Emerson, and I were going to meet several others for sushi but couldn’t find the restaurant, so we found a sushi place close to the hotel and settled in there. Wow, was it good! Excellent sushi and large portions.

Finally, the moment we’d been waiting for: racing at the F1 Race Factory. More than 20 people showed up for this year’s event. Rick and I suspected Jody, Toni, and Cathy had something planned: Jody carried a large bag into the building and wouldn’t show us what was in it. Rick and I figured there was a conspiracy against us, with Jody and Toni acting as blockers so Cathy could zoom by. However, what they really planned was WAY cooler:

IMG_4739

The Pink Ladies and Danny Zuko ride again!

Unfortunately, it didn’t help Cathy: Paul Mrozowski finished first, I was second, Rick was third, and Cathy was 99th or something like that (it doesn’t really matter once you’re that far down <gd&r>).

Not as many people were in the second race, and it was a lot more wild (at least for me). I spun out a couple of times and was black-flagged for taking Dave Hanna into a wall. However, it was great fun and we’ll definitely be back next year.

We got back to the Arizona Golf Resort about 11:30 and proceeded to the Feltman’s suite for another evening of socializing. I called it a night at about 1:00 because I had an 8:30 session.

Tuesday, October 20, 2009

VFPX Administrator’s Outstanding Service Award

At the keynote presentation for Southwest Fox 2009, the administrators of VFPX (Craig Boyd, Rick Schummer, and I) presented Cesar Chalom with the 2009 VFPX Administrator’s Outstanding Service Award (you can watch the presentation at http://www.ustream.tv/channel/swfoxtv). Cesar is well-deserving of this award due to his work on the GDIPlusX and FoxCharts projects, both of which were covered in sessions at this year’s conference. Unfortunately, Cesar couldn’t make it to Southwest Fox this year, so the Ceil Silver Ambassador, Emerson Santon Reed, accepted it on his behalf and will present it to Cesar when he returns to Brazil.

Wednesday, August 26, 2009

More on TreeViews and GDIPlusX

In my blog post Disabling a TreeView Control, I showed some code that visually disables a TreeView. It doesn’t really do that; instead, it uses GDIPlusX to take a snapshot of the TreeView, converts the snapshot to have a “disabled” appearance, hides the TreeView, and displays the snapshot in its place.

I just found another use for this technique. Suppose you want a VFP control to temporarily appear on top of a TreeView control. As you’re likely aware, the TreeView is an ActiveX control, and although it doesn’t visually appear this way, ActiveX controls display in their own window. As a result, any VFP control occupying the same space of a TreeView appears behind the TreeView, regardless of ZOrder.

Here’s an example. I’m adding a new feature to Stonefield Query that allows the developer to display fields organized by screen/form rather than by table. The idea is to make it easier for the user to find the fields they’re interested in because the fields map directly to what the user sees in their application. In this case, the Accounts Receivable module has three sub-modules: A/R Customers, A/R Setup, and A/R Transactions. A/R Customers has four forms: Customer Groups, National Accounts, Customers, and Ship-To Locations. The user chooses the desired form from a drop-down control (the SFComboTree control discussed in my article in the September 2009 issue of FoxRockX) and a TreeView control displays a list of the pages in that form and the fields on each page in a hierarchical view.

one

Notice the problem in this screen shot? The border around the dropped down SFComboTree disappears where it overlaps the available fields TreeView control. The drop-down part is a Shape surrounding another TreeView. While the dropped-down TreeView overlaps the fields TreeView as expected (an ActiveX control can overlap another ActiveX control), the Shape disappears behind the fields TreeView.

Fortunately, there’s an easy solution for this: use the same lightbox technique I discussed earlier but this time don’t make the image appear disabled: just take a snapshot of the TreeView, hide the TreeView, and place the snapshot where the TreeView is. Here’s the result:

two

The border for the dropped-down control now appears because the TreeView is temporarily hidden and what you see underneath is a snapshot of the TreeView.

The code is even simpler than the lightbox code because it doesn’t have to change colors. This is the code used when the SFComboTree is dropped down:

with This.Parent
loTree = .oTree
lnWidth = loTree.Width
lnHeight = loTree.Height
with .imgLightBox
.Top = loTree.Top
.Left = loTree.Left
.Width = lnWidth
.Height = lnHeight
endwith
loBmp = _screen.System.Drawing.Bitmap.FromScreen(.imgLightBox)
with .imgLightBox
.PictureVal = loBmp.GetPictureValFromHBitmap()
.ZOrder(0)
.Visible = .T.
endwith
loTree.Visible = .F.
endwith


The more I use GDIPlusX, the more I love it! My GDIPlusX session at Southwest Fox 2009 will cover other interesting uses for GDIPlusX. I’m also doing that session and an introduction to GDIPlusX session at the 2009 German DevCon. Hope to see you there.

Thursday, August 20, 2009

Disabling a TreeView Control

In my post “Setting the Background Color of a TreeView Control”, I presented some code that sets the background color of a TreeView to make it appear disabled. However, a reader named Alberto pointed out that after running that code, indented nodes have an ugly white background in front of them. I played with this a bit but couldn’t come up with a way to fix it.

Fortunately, there’s another solution: using a “lightbox”. Bernard Bout blogged about using the VFPX GDIPluxX project to create an image with a darker representation of a form and overlaying the form with the image to make the entire form appear disabled. I adapted his code to do the same thing with a TreeView control. Pass this code up to four parameters:

  • A reference to the form the TreeView is on.
  • A reference to the TreeView itself.
  • .T. to enable the TreeView or .F. to disable it
  • Optionally, an RGB value to use as the disabled color. If this isn’t passed, grey—RGB(240, 240, 240)—is used. This parameter isn’t used if the third parameter is .T.

This code creates an image object, takes a snapshot of the TreeView contents, makes the snapshot darker so it appears disabled, and uses the snapshot as the picture for the image. Note that a VFP control can’t cover an ActiveX control, so the code hides the TreeView. Of course, to the user, it looks like the TreeView is still there but disabled because the image is placed exactly where the TreeView is and contains a picture that looks just like a disabled version of the TreeView.

This code requires System.APP, which comes with GDIPlusX.

lparameters toForm, ;
toTreeView, ;
tlEnable, ;
tnColor
local llImage, ;
lnWidth, ;
lnHeight, ;
lnColor, ;
lnFactor, ;
lnRed, ;
lnGreen, ;
lnBlue, ;
loClrMatrix, ;
loBmp, ;
loGfx, ;
lcFile

* See if the specified form has a LightBox image.

llImage = pemstatus(toForm, 'imgLightBox', 5)
do case

* It does and we're supposed to enable the TreeView, so hide the image and
* redisplay the TreeView.

case tlEnable and llImage
toForm.imgLightBox.Visible = .F.
toForm.imgLightBox.PictureVal = ''
toTreeView.Visible = .T.

* We're supposed to disable the TreeView, so ensure GDIPlusX libraries are
* open and create the image if necessary.

case not tlEnable
do System.app
if not llImage
toForm.AddObject('imgLightBox', 'Image')
endif not llImage

* Size the image as necessary.

lnWidth = toTreeView.Width
lnHeight = toTreeView.Height
with toForm.imgLightBox
.Top = toTreeView.Top
.Left = toTreeView.Left
.Width = lnWidth
.Height = lnHeight
endwith

* If we weren't passed a color to use, use grey.

if vartype(tnColor) = 'N'
lnColor = tnColor
else
lnColor = rgb(240, 240, 240)
endif vartype(tnColor) = 'N'

* Get the colors we'll need.

lnFactor = 0.90 && 0 = Dark 1 = Bright
lnRed = bitand(lnColor, 0x000000FF) / 255 * lnFactor
lnGreen = bitrshift(bitand(lnColor, 0x0000FF00), 8) / 255 * lnFactor
lnBlue = bitrshift(bitand(lnColor, 0x00FF0000), 16) / 255 * lnFactor

* Create a BMP of the desired color and use it as the picture for the image.

with _Screen.System.Drawing as xfcSystem of Source\System.PRG
loClrMatrix = .Imaging.ColorMatrix.New( ;
lnRed, 0, 0, 0, 0, ;
0, lnGreen, 0, 0, 0, ;
0, 0, lnBlue, 0, 0, ;
0, 0, 0, 1, 0, ;
0, 0, 0, 0, 1)
loBmp = .Bitmap.FromScreen(toForm.imgLightBox)
loBmp.ApplyColorMatrix(loClrMatrix)
loGfx = .Graphics.FromImage(loBmp)
loGfx.FillRectangle(.SolidBrush.New(.Color.FromARGB(10, 0, 0, 0)), ;
0, 0, lnWidth, lnHeight)
endwith
with toForm.imgLightBox
.PictureVal = loBmp.GetPictureValFromHBitmap()
.ZOrder(0)
.Visible = .T.
endwith
toTreeView.Visible = .F.
endcase

Wednesday, January 14, 2009

Changing the Color and Orientation of Images

Recently, I needed to change the color of an image. I have a container that includes an image and depending on the user's color choices, the container's background color might change. That means certain colors in the image must also change so the image blends in with the container properly, and rather than creating a number of images with pre-defined colors, I wanted to change the colors programmatically. One other wrinkle: because the orientation of the container might also change under some circumstances, I also wanted to rotate the image.

Fortunately, the VFPX GDIPlusX project makes this a snap and thanks to a recent blog posting by Cesar, it didn't take too long to figure out what to do. The following code reads the color of the upper left pixel in the image whose file name is in the lcImageFile variable and converts that color to the one specified in This.BackColor. It also rotates the image if it's wide and short but the container is narrow and tall. It then writes the new image directly to the PictureVal property of the imgImage object so there's no additional disk I/O.

* Create the GDIPlusX objects if necessary.

if type('_screen.System.Name') <> 'O'
do System.APP
endif type('_screen.System.Name') <> 'O'
with _screen.System.Drawing

* Load the image.

loBmp = .Bitmap.FromFile(lcImageFile)

* Get the color of the upper left pixel.

loColor = loBmp.GetPixel(0, 0)

* Create a color map and tell it to convert from the color of
* the pixel to our background color.

loColorMap = .Imaging.ColorMap.New()
loColorMap.OldColor = loColor
loColorMap.NewColor = .Color.FromRGB(This.BackColor)

* Create an attributes object and make it use the color map.

loAttr = .Imaging.ImageAttributes.New()
loAttr.SetRemapTable(loColorMap)

* Create a new image and draw the old image onto it using the new
* color mapping.

loDestBmp = .Bitmap.New(loBmp.Width, loBmp.Height, ;
.Imaging.PixelFormat.Format24bppRGB)
loGfx = .Graphics.FromImage(loDestBmp)
loRect = loBmp.GetBounds()
loGfx.DrawImage(loBmp, loRect, loRect, .GraphicsUnit.Pixel, loAttr)

* Flip the image if its orientation doesn't match ours.

if This.Width > This.Height and loBmp.Height > loBmp.Width
loDestBmp.RotateFlip(.RotateFlipType.Rotate90FlipNone)
endif This.Height > This.Width ...

* Write the image directly to the PictureVal property of our image
* object.

loDestBmp.Save(This.imgImage, .Imaging.ImageFormat.Bmp)
endwith