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.

Tuesday, August 25, 2009

Southwest Fox 2009 Early-Bird Deadline Next Week

Southwest Fox is only seven weeks away. This year’s conference has an incredible line-up of speakers and sessions. Not to pick favorites, but I’m especially looking forward to Christof’s Excelporting session, Paul’s Lucene.NET presentation, Jim’s sessions on PEM Editor and FoxCharts, and both of Cathy’s sessions. I’d also really like to see Toni’s session on Subversion and Walt’s session on Vault (especially after seeing his video), but they’re both pre-conference sessions and Thursday is registration day. As an organizer, I’m pretty busy that day.

If you haven’t registered yet, be sure to do so before September 1 to save $50 on your registration fee. If you’ve been to Southwest Fox before, you know what a great training and networking opportunity it is. If you haven’t, read what attendees from past years have said. In addition to the usual perks, this year you’ll get a chance to meet and hang out with the Ceil Silver Ambassador from Brazil.

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

Friday, August 14, 2009

New VFP Blogger

Welcome to the blogosphere, Matt Slay.

Tuesday, August 11, 2009

Outlook 2007 RSS Feeds Not Refreshing

I’ve had this happen several times over the past couple of years: suddenly, Outlook no longer refreshes RSS feeds. The only solution I’d found to make it start re-reading the feeds was to select each one and click Send/Receive (or press F9). When you have more than 100 feeds, that’s time-consuming and painful.

Fortunately, Alun Jones found a faster (but still lame) way: just select each feed and it’ll refresh next time send/receive occurs. Hopefully MS will fix this.

Tuesday, August 04, 2009

Stonefield in the News Again

Grant Buckler of SoftwareCEO wrote an extensive article on Stonefield Software that was published today. It’s very well written and does a nice job of presenting business tips we’ve learned over the years.