Wednesday, June 25, 2008

Southwest Fox 2008 Early-Bird Deadline Next Week

Hear that sound? Tick, tick, tick, tick...

That's the countdown clock getting closer to the early-bird deadline for Southwest Fox! July 1 is less than a week away and we thought we would pass along a reminder just in case you forgot to type it into your task list, or stick it on your monitor on a yellow sticky note.

Southwest Fox takes place October 16-19, 2008 in Mesa, Arizona and we really hope you can be there. We would hate to see you miss out on the $75 discount, the free pre-conference session, and a chance at the $300 scholarship from White Light Computing.

Already registered? Thanks. Can you help us remind your fellow VFP developers who have been procrastinating that the deadline is looming?

Now if the great sessions, networking opportunities, Arizona weather, fun and merriment, discount, and freebie pre-con are not enough to entice you to come, how about a chance to win an Microsoft's Visual Studio Team System 2008 Team Suite and MSDN Premium Subscription (1-year), valued at $11,000. We've got three of them to give away!

Okay, so maybe that is not enough. What if we told you the moment we process your registration, we'll send you a code for a free license for xSQL Software's xSQL Data Compare Pro (a $399 value). This product allows you to compare and synchronize data in two SQL Server databases. That's right, you get back almost US $400 of the registration cost even before you reach Mesa!

Where are those ginsu knives?

We have lots of raffle prizes from our sponsors and vendors to give away during the conference.

Special thanks to all our sponsors: White Light Computing, Stonefield Query, Tomorrow's Solutions LLC, Sybase iAnywhere, Servoy, xSQL Software, FoxRockX, ApexSQL Software, Sweet Potato Software, Redgate, Information Technology Associates, and Visual Extend.

If you're a user group leader, you probably want to contact us if you have not done so already and let us know you want in on the $25 per registration for your group. Free money to help your organization fund your activities.

You cannot go wrong with this, but you have to let us know by July 1st that you are interested. Send contact information to info@swfox.net.

Got suggestions? info@swfox.net

Got questions? info@swfox.net

Got registrations? register@swfox.net

Or you can call the Geek Gatherings' World Headquarters at 586.254.2530.

Read about the registration process and get the registration form here:

http://www.swfox.net/register.aspx

Check out our list of amazing speakers: http://www.swfox.net/speakers.aspx

Dig into our five session tracks: http://www.swfox.net/sessionstrack.aspx

Follow the news about the conference on our blog:

http://swfox.net/blog/index.htm

Use our brochure to convince your boss (or spouse or SO) to let you go:

http://www.swfox.net/brochure.pdf

So please beat the rush so we don't have to test the scalability of the registration tracking application next week!

113 days until we gather in Mesa.

Tuesday, June 24, 2008

Another Day at the Races

On Saturday, I competed in my second Echo Challenge, a fund raiser for our local YMCA to help disadvantaged kids go to summer camp and many other important programs run by the Y. Our team finished much better this year, placing 11th of 19 teams. That's not bad, considering we finished last in 2007 and many of the teams are comprised of 20 year old athletes.

Conditions were ideal this year: warm (about 24 C or 75 F) and little wind. The lake was like glass, which was much easier on the swimmers (last year, swimmers had a hard time catching a breath in the meter-high waves). Our swimmer finished ahead of last year's pace, in 13th. Our hill runner did great, finishing in the fastest time, which was tough considering both the competition and the fact that the leg was lengthened by about a kilometer this year.

I was a little nervous about this year's bike ride. On Wednesday, I was doing some hills to train for the event and a combination of new shoes and pedals (the click-in kind, which I've only used once before) and someone in a truck cutting me off on a gravel road up a hill resulted in a nasty crash. I had a lot of gravel-embedded road rash on my right forearm, leg, and back, but the worst part was my shoulder; I landed on my right shoulder and although it didn't hurt much then, by the time I got home it was agony. I got x-rays Thursday morning and fortunately nothing was broken or separated, so it was just soft tissue damage. It was a little better by Saturday morning but still very sore and tight. Fortunately, cycling is all lower body, so I was hoping it wouldn't be too much of a factor. I also have a dark purple bruise the size of a football on my right hip, but there's no pain, so I wasn't worried about it.

This year, I decided to use a road bike rather than a mountain bike and it made a huge difference. It wasn't a very fancy bike -- a $149 no-name brand -- but the lighter frame and much smaller tires really helped. (I did not, however, use the new shoes and pedals.) The other big difference was no head-wind this year. As a result, I felt like I was flying through the race. I passed three other cyclists like they were standing still and felt so strong I didn't even shift down on most of the hills. As they did last year, Peggy, Nick, and friends cheered me on at the start of my leg, drove ahead and cheered me on again, but couldn't beat me to the finish line because of traffic on the narrow, hilly road.

Here's a shot of me just starting off, with my son Nick chasing me:

036

I finished the 9.45 km race in 16:29, an average of over 34 kph, which I figured was pretty good for an old banged-up guy. It turns out that was the second fastest time of the day, which I was extremely happy about, considering the number of 20 year olds in the race. The only humbling thing was the fastest cyclist finished over a minute ahead of me, and he was in his 60s! He's a serious cyclist, though, with an $8,000 carbon fibre bike and a body that looks like he's made of steel, but he's in my sights for next year.

Here's Nick and I after the race:

040

Our swimmer from last year was our runner in the 6 km run this year, and he fared much better in this event, finishing 14th. Our canoeists also did much better this year (considering they swamped the canoe last year, they couldn't have done worse), also finishing 14th for their leg.

Echo Challenge raised over $25,000 for the YMCA this year, up significantly from last year. It felt great to have fun, engage my competitive side, and do something worthwhile for the community. I can't wait for next year!

Friday, June 13, 2008

Southwest Fox Just Got Better

Southwest Fox 2008 Gold sponsor xSQL Software is giving away a free license of xSQL Data Compare Pro (a $399 value -- allows you to compare and synchronize data in two SQL Server databases) to every conference participant. What a great offset of the conference price – you pay $695 for the conference and get back more than half of it ($399) with this offer alone! Even better, you'll receive your free license as soon as you register for Southwest Fox 2008 so you don't even have to wait until October!

Wednesday, June 11, 2008

Reset Your VPC Password

Ever forget the password to a Virtual PC? Virtual NT Password Reset Disk is a bootable floppy image you can mount in a VPC to reset the password.

Using the Microsoft Date and Time Picker Control with Date and Time Values

I've used the Microsoft Date and Time Picker (DTPicker) ActiveX control for years. Yesterday, I ran into an interesting issue. First, some background.

The control has four different data entry modes, set via the Format property: short date, long date, time, and custom. The nice thing about the first three is that they automatically use the date/time settings you set in the Regional Settings applet in the Control Panel, so you don't have to worry about localization issues. Custom is used for custom formats.

Time format is fine if you just want the time, but if you want both date and time displayed, you have to set Format to 3 for custom, then set the CustomFormat property to the desired format. For example, "MM/dd/yyy HH:mm:ss" uses two digits for most values and four digits for year (yes, four even though the format string uses three). However, here's the issue: how do you know what format to use? The user could be using MM/DD/YYYY, DD/MM/YYYY, or any of a variety of formats.

Fortunately, VFP has several functions that return the formats for dates. SET("DATE") returns values like MDY or American for MM/DD/YYYY format, BRITISH or DMY for DD/MM/YYYY format, and so on. SET("MARK") returns the character separating parts of the date (such as "/" or "-").

I have a subclass of the DTPicker control called SFDatePicker (actually, it's a container that contains a DTPicker) that provides additional functionality, including empty date support, data binding, and format control. Format control is handled via a custom lDateTime property; the default of .F. means only the date appears while .T. means date and time. Drop one on a form, set cControlSource to the control source if desired, set lDateTime to .T. if you want date and time, and you're done.

Except someone from Australia, which uses DD/MM/YYYY as its date format, reported that if they specify that Stonefield Query should display a datetime field as both the date and time rather than the default date only, when they filter on that field, the filter dialog displays the date as MM/DD/YYYY.

The following code in the SetCustomFormat method of SFDatePicker, which is called from Init and the Assign method of lDateTime (in case you change this property programmatically), sets CustomFormat as necessary:

with This 
  lcFormat = set('DATE') 
  if lcFormat <> 'SHORT' or .lDateTime 
    .oleDTPicker.Object.Format = 3 
    lcMark = set('MARK') 
    do case 
      case inlist(lcFormat, 'AMERICAN', 'MDY', 'USA', 'SHORT') 
        lcCustomFormat = 'MM' + lcMark + 'dd' + lcMark + 'yyy' 
      case inlist(lcFormat, 'BRITISH', 'DMY', 'FRENCH', ;
       
'GERMAN', 'ITALIAN') 
        lcCustomFormat = 'dd' + lcMark + 'MM' + lcMark + 'yyy' 
      case inlist(lcFormat, 'JAPAN', 'YMD', 'TAIWAN', 'ANSI') 
        lcCustomFormat = 'yyy' + lcMark + 'MM' + lcMark + 'dd' 
    endcase 
    if .lDateTime 
      lcCustomFormat = lcCustomFormat + ' HH:mm:ss' 
    endif .lDateTime 
    .oleDTPicker.CustomFormat = lcCustomFormat 
  endif lcFormat <> 'SHORT' ...
endwith

It looks like this code handles different date formats properly, including the date separator, so what's the problem? It's a subtle one: if you use SET SYSFORMATS ON, which you should so the user's regional settings are respected, SET("DATE") returns "SHORT". This code assumes that short dates are treated as MDY.

Now the problem is how to determine what the user's actual date format is. I figured a Windows API function would take care of it, and did find some possibilities on MSDN, but it looks like these functions can't be called directly from VFP because they require callback functions. So, I took a brute force approach:

if lcFormat = 'SHORT'
  ldDate = date(2008, 1, 3)
  lcDate = dtoc(ldDate)
  lnPos1 = at('8', lcDate)
  lnPos2 = at('1', lcDate)
  lnPos3 = at('3', lcDate)
  do case
    case lnPos1 < lnPos2 and lnPos2 < lnPos3
      lcFormat = 'YMD'
    case lnPos1 > lnPos2 and lnPos2 > lnPos3
      lcFormat = 'DMY'
    case lnPos1 > lnPos3 and lnPos3 > lnPos2
      lcFormat = 'MDY'
  endcase
endif lcFormat = 'SHORT'

This code uses 01/03/2008 (in MDY format) as a date, converts it to a string (which respects the user's regional settings), then figures out what order the month, day, and year parts are in and sets lcFormat accordingly.

Ugly, yes, but it works, so I'll stick with it until a more elegant solution is available.

Update: I figured there was a better way to do this. While I was looking at the code for Carlos Alloatti's cool ctl32_datepicker control, I came across his use of SET('DATE', 1). Looking that up in the VFP help file, I discovered this was exactly the function I needed. I can't believe I either never knew about that or (more likely) forgot about it. So, now the code is a much simpler and cleaner:

if lcFormat = 'SHORT'
  lnDate = set('DATE', 1)
  do case
    case lnDate = 0
      lcFormat = 'MDY'
    case lnDate = 1
      lcFormat = 'DMY'
    otherwise
      lcFormat = 'YMD'
  endcase
endif lcFormat = 'SHORT'

If you're curious about how the other features work, here are the details.

Control source support: as I mentioned above, cControlSource is a custom property containing the name of the control source if desired. There's also a Value property so the control can look like other data-bound controls. Refresh updates Value from the control source:

if not empty(This.cControlSource)
  This.Value = evaluate(This.cControlSource)
endif not empty(This.cControlSource)

The Change event of the DTPicker, which fires when the user changes the date and/or time, raises a custom DateChanged event of the container. DateChanged updates the control source, which could either be a field in a cursor or something else, such as a property of an object:

with This
  lcAlias = juststem(.cControlSource)
  lcField = justext(.cControlSource)
  do case
      case empty(.cControlSource)
      case used(lcAlias)
        replace &lcField with .Value in (lcAlias)
      otherwise
        store .Value to (.cControlSource)
  endcase
endwith

Value_Access gets the value from the DTPicker, changing it to a date if a datetime isn't needed:

luValue = This.oleDTPicker.Object.Value
if not This.lDateTime
  luValue = ttod(luValue)
endif not This.lDateTime
return luValue

Blank date support: if you've worked with the DTPicker control, you know it doesn't like blank dates. So, the code in Value_Assign uses the current datetime in that case. Also, since DTPicker expects to have its Value property set to a datetime value, we have to handle being passed a date:

lparameters tuValue
local luValue
with This
  do case
    case empty(tuValue)
      luValue = datetime()
    case vartype(tuValue) = 'D'
      luValue = dtot(tuValue)
    case vartype(tuValue) = 'T'
      luValue = tuValue
    otherwise
      luValue = .NULL.
  endcase
  if not isnull(luValue)
    try
      .oleDTPicker.Object.Value = luValue
      if not .CalledFromThisClass()
        raiseevent(This, 'DateChanged')
      endif not .CalledFromThisClass()
    catch
      endtry
  endif not isnull(luValue)
endwith

Wednesday, June 04, 2008

Open a Command Window in Vista

LifeHacker today mentioned a posting by HowToGeek that's a nice time-saver if you need to open a Command window in Vista: find the directory you want the Command window to start in using Windows Explorer, then Shift-right-click and choose Open Command Window Here from the shortcut menu. I don't use a Command window often, but when I do, I usually get annoyed at having to type long paths, complete with quotes, in CD commands, so this definitely goes on the "Vista Tips" list (along with another favorite: Ctrl-Shift-Esc to quickly launch the Task Manager).