Monday, January 23, 2006

... or something like that.

The week of Xmas 2005 I got a call from my Fearless Leader at SafeNet.  It seems that for 2006 Microsoft has made it quite abit harder to be a true Microsoft Partner, and as such they are going to lose about 80% of their current certified partners.  This sounds good to me, since this might make partnership mean a little more.  However it turns out we are short an MCAD plus one premier certification on staff in order to qualify.  As the person with the Fanciest Title it falls on me to get 4 certs before January 31st.  I have never cared much for certifications, from what I had seen from the MSFT practice questions in the past they seem to cover a lot of stuff I am not likely to use in the real world. 

So, xmas weekend was out for studying, under pain of death from my family.  The following week was out because I was getting ready for my CF 2 presentation.  By the way I still need to pos the slides/code for that.  The following week I'm not sure what happened.  In order to force myself to study and take the exams I just scheduled 4 tests in close succession on the 18th, 20th, 23rd, and 27th.  A part of me secretly wanted me to fail something to prove that the tests are a meaningful measure of skill.  I did have to read up on the remoting and COM+ stuff for 70-320 but I passed the 1st three easily, I suppose I am an MCAD as of this morning.  On Friday I have to take the Solution Architecture test to prove I am a sufficiently skilled Hand Waver and SafeNet will get to keep its partnership status.  After that I might as well take the BizTalk test to get the illustrious MCSD designation.

To me, the only reason to take these tests is for partnership points, no client has ever asked me about certifications.  Most of the very skilled people I know locally do not pursue certifications.  I am somewhat hopeful that the Microsoft Certified Architect program will be brutal, and therefore mean something when you pass.

Monday, January 23, 2006 11:19:20 AM (Central Standard Time, UTC-06:00)  #    Disclaimer  |  Comments [1]  |  Trackback
 Monday, January 16, 2006

As with everything I post here, the following is my opinion and you are welcome to disagree.

 

IT recruiting is a mess right now in our area.  I feel bad for all the good recruiters and PMs who are trying to find people to staff projects.  I get calls and emails from recruiters regularly and I don’t have anyone to send them.  Of course I’d rather take advantage of the sweet recruiting bonus at SafeNet anyway, but the fact is I just don’t know anyone who is looking.

 

The ITAA is currently lobbying to remove H-1B visa caps because according to them 859,000 tech jobs are going unfilled every year.  The ITAA is obviously lobbying for cheap labor to drive down yours & my salaries, but there is clearly something going on right now.  No, I don’t think we’re going to see salaries at 1999 levels nor should we, but things are picking up right now.  There is no one to do the work.

 

Part of the issue right now is that companies are simply not investing in hiring and retaining junior level developers.  Every project I see requires “One senior and 3 mid level” developers.  People don’t become mid-level developers by going to grad school or by doing a single internship one summer: they do it by building systems, initially as junior developers.  Hopefully with some guidance from someone who has already built many systems. 

 

When I was getting my start, I was lucky enough to have fantastic mentors, people who had the skills and could work with me to make me better.   There was also a company directive stating that it was part of their job to spend that time with me.  If I ever saw one of these guys on the street (my first job was in Madison, a ways from me) I would shake their hand, buy them a drink, and thank them for helping to give me a good start.  In fact, one of the reasons I have tried to pursue more senior level positions in my career is to be able to work with less experienced people: find the ones who “get it”, and spend time with them turning them into the next great developer.

 

I just don’t see companies making this investment today.  The kids with CS degrees are having trouble finding work because they are not yet mid-to-senior-level.  The kids a few classes younger than them are not going into CS majors because they see these kids having trouble finding work.   A myth starts that “American kids don’t like CS” and we get lobbyists at the soapbox talking about how poor our educational system is.  Good paying jobs + Availability of Jobs = Students in that major.

 

Companies refuse to hire inexperienced people and then complain that there’s no one out there who’s looking for a job because most people are (wisely) taking decent care of their people.  They need to hire someone for a particular project, meaning they need someone with X,Y,Z skill set already.  “We have a deadline and there is no time to bring someone up to speed on this.”  Their mid and senior level people, the good ones who get stuff done who might also make good mentors, they are already overworked as they try to balance their own work with helping out their supposedly mid-level “peers” who aren’t getting the work done because they don’t “get it”, but were lucky enough to get in before the bubble burst and have a “mid level” title now.  Everyone is running around as though the skies were falling and no one is investing in the next generation of talent.  If you cannot find the talent, you need to seriously consider home-growing the talent that you need.  People will learn your business while they learn your technology.

 

I will close with this: I do very well at Safenet, so I am not looking for work.  However, the one thing that an employer could do that would show me that they “Get it” would be to offer to pay me a great salary to be a tech lead and mentor of junior developers.  I would certainly send the brightest kids in their direction.  Invest in the next generation of talent, or we are going to have several messes on our hands.

Monday, January 16, 2006 12:49:48 PM (Central Standard Time, UTC-06:00)  #    Disclaimer  |  Comments [3]  |  Trackback
 Thursday, January 12, 2006

Many applications have scaling, or zoom-in, zoom-out features of some kind.  Acrobat reader, Word, Graphics programs, etc.  We have these great Skin and Theme features in ASP.NET 2.0 but not so in Windows forms.  You can enable XP visual styles though, check out this Help article:

ms-help://MS.VSCC.v80/MS.MSDN.v80/MS.VSADD.v10.en/dv_vstechart/html/vbtchUsingWindowsXPVisualStylesWithControlsOnWindowsForms.htm

While trying to determine a good design approach to creating a skin-able windows app I stumbled across some odd behavior which I assumed must be a bug:

  1. Create a windows forms app and throw some controls on it.  Leave everything the default size/font/color since I am going to apply a skin to it anyway.
  2. At runtime, change the font to the "skin" font.
  3. If the design-time font was good ole MS sans-serif 8.25pt and I assigned a font with a different size (Verdana, 12pt) the form would suddenly get tremendously larger.

My forms extend from a BaseForm, so this must be a Visual Ineritence bug, I thought, and the client decided not to spend $$ on the skinable feature just yet anyway.  When a request came in to allow parts of the application to Scale via zoom-in, zoom-out, I came back to this.  My odd resizing forms are exhibiting behavior as designed.

ScaleMode

Windows Forms in .Net 2.0 are designed to automatically scale under one of two events: the Font assigned to the form changes, or the screen resolution changes.  There is an AutoScaleMode property on a Form, with the enumerated values being

  • None
  • Inherit
  • Font
  • DPI

It seems I could find my way through this in order to meet my scaling requirement and also revisit the Skin feature in future.  A Form keeps track of what dimensions it was designed to regardless of other resizing and such.  This is stored in the AutoScaleDimensions property.  From the set_Font method of a Form, the Scale and ultimately ScaleControl methods will be called.  These can be overridden to alter scaling behavior, or just observed to determine how to manipulate scaling.  So, I have an MDI application and the area I want to scale will be contained in a "ScalableForm" like so:

Now, a simple way to give the user the ability to zoom out, I create a simple ScaleData class.

public class ScaleData
{
public ScaleData(float size, string desc)
{
_fontSize = size;
_display = desc;
}
private float _fontSize;

public float FontSize
{
get { return _fontSize; }
set { _fontSize = value; }
}
private string _display;

public string Display
{
get { return _display; }
set { _display = value; }
}
}

... and populate some scale values in the ScalableForm constructor:

public ScalableForm()
{
InitializeComponent();
ScaleSizes = new List<ScaleData>();
ScaleSizes.Add(new ScaleData(this.Font.Size / 2f, "50%"));
ScaleSizes.Add(new ScaleData(this.Font.Size, "100%"));//This will be the design-time size
ScaleSizes.Add(new ScaleData(this.Font.Size * 2f, "200%"));

...

There is certainly no need to pre-calculate a limited set of zoom values but it removes the need for calculations later to get back to 100% .  Now I provide a menu item that will scale the currently active child Form and the code is very simple:

private void toolStripMenuItem2_Click(object sender, EventArgs e)
{
  _scale.Font = new Font(_scale.Font.FontFamily,_scale.ScaleSizes[0].FontSize);
}

So telling it to zoom to 50% looks like:

A couple of other things bear mentioning.  Note that "a bunch of text in a label" appears the same size in both cases , although the Location of the Label was scaled down.  By default the ScaleChildren called from ContainerControl will not scale controls set to be automatically sized, so in this case I would need to tell my label not to auto size itself.  Also, the documentation for scaling claims you can have a control on this form not ever scale itself by extending a control class and providing the following:

public class UnscalableComboBox : ComboBox
{
   protected override void SetBoundsCore(int x, int y, int width, int height, BoundsSpecified specified)
   {
      base.SetBoundsCore(x, y, 121, 21, BoundsSpecified.None);
   }
}

However, neither this, not setting Locked=true, not setting MaximumSize and MinimumSize would stop a control from being scaled when I changed the font.  For now I put the scaling function in the menu (rather than a combo box on this form somewhere) but will continue to investigate this feature as I work on this project.

 

Thursday, January 12, 2006 10:51:44 AM (Central Standard Time, UTC-06:00)  #    Disclaimer  |  Comments [0]  |  Trackback
 Wednesday, January 11, 2006

My presentation at the user group last night went ok.  A big part of my example was to show both multi-threading and mobile data synchronization with a mobile device talking wirelessly to my laptop.  The Wifi connection in the room was weak sauce though and I didn't have the foresight to bring an access point as a backup plan despite owning 2 or 3.  Seeing a progress bar tick off across the screen often makes things click for people, so that's too bad.  I have not presented in this building before either and wasn't quite prepared for how difficult it was to see what was on the screens.  Oh well, live and learn, I will definately scope out the Venue more before I do this again.

As soon as I get a moment I'll make the code and slides available here as well as from the Wineta site.

Wednesday, January 11, 2006 10:05:04 AM (Central Standard Time, UTC-06:00)  #    Disclaimer  |  Comments [0]  |  Trackback
 Tuesday, January 03, 2006

I posted some 2006 goals/resolutions on my personal site, but as I sit here on my 3rd cup of coffee I realize I forgot the most important one for 2006: get into work earlier.  Get up when the alarm goes off.  Stephen Covey calls this "Mind over Mattress" and quite rightly names it one of the most fundamental things you can to do Get Stuff Done.

I have never been a morning person.  This goal has been a failed resolution in years past.  Usually having kids forces people to shift to an earlier schedule but my almost 3 year old seems to have inherited mommy & daddy's desire to sleep in a little bit.  She did wake me up at 7:20 today to let her watch Spongebob, though.  I thought moving further out of the city would help too, but I still get up just early enough to get into work at a semi-reasonable hour.

This year will be different because I really have no choice.  I have enough to do in the month of January alone that it will not happen without some productivity changes on my part.  I'm such a slacker right now ;)  As soon as the Mrs. goes back to nursing clinicals later this month I have no choice but to get up stupid early at least three days a week to help with the daycare shuffle.  Let's hear it for Mind over Matress in 2006.

Tuesday, January 03, 2006 9:26:49 AM (Central Standard Time, UTC-06:00)  #    Disclaimer  |  Comments [3]  |  Trackback
 Sunday, January 01, 2006

Despite various studies indicating the negative effects on employee productivity and especially morale my current client has purchased "Websense" to do detailed internet site blocking and surf-volume reporting.  I wonder how much money it cost them to reduce their employee productivity and job satisfaction in this manner?  Why does this bother people?  I suspect a top reason might be that most websites are not quite as ubiquitous as a phonecall.  Many firms have reviewed phone records for years and too much tele time might get you a stern talking to.  Your manager does not necessarily know what you called your wife 17 times a day about, just that you did.  Visiting websites, though, might give various keys as to your political beliefs, religious affiliations, and various other things someone could hold against you.  One would think that in the Litigious States of America this is knowledge employers would not want.  Now someone can claim they were let go because their manager snooped their internet habits and found out that they were gay, had HIV, was an atheist, voted Democrat, etc.

At any rate I've had to cut everything but MSDN and Fox News out of my daily diet in an effort to stay off of the top daily surfers list.  I typically have other work to do at home and the holidays have not given me more free time.  I finally set down and read/skimmed several hundred articles worth of waiting content.  To my fellow Wisconsin bloggers: I am caught up now.

I will be having a Nerd Lunch late this week to preview my INETA presentation next week. 

Sunday, January 01, 2006 9:41:19 PM (Central Standard Time, UTC-06:00)  #    Disclaimer  |  Comments [2]  |  Trackback
 Tuesday, December 27, 2005
I have always had a small amount of spam on this site.  I checked my email on christmas day to discover I had 6000 referral spam instances (I had the blog engine set up to email me) waiting for me.  I will be temporarily turning off all the trackback, pingback, and referral features until I get time to upgrade the engine later this week.
Tuesday, December 27, 2005 10:16:16 AM (Central Standard Time, UTC-06:00)  #    Disclaimer  |  Comments [0]  |  Trackback
 Tuesday, December 20, 2005

As promised, I have some ideas regarding Async code for beginners.  Part one of three.

Compact Framework

The first thing to learn when doing asynchronous programming on the Compact Framework is that it is not as forgiving as the full .NET framework.  Threading things that might be a problem on a Pentium IV will be a problem on a 400mhz XScale.  The first problem you may run into, since its not well documented is Thread priorities.  You may want to give your "worker" threads more CPU time than the UI thread.  While the thread priority method exists on the thread class in the CF it is unofficially unsupported in CF 1.0.  I have tested this on at least 3 different devices with both CE.NET 4.2 and Windows Mobile and any attempt to set a thread priority locks up execution and debugging.

Your UI is running on a thread that processes events and updates UI controls.  In fact the compact framework starts quite a few threads you have no control over, for exampleTCP listeners and such when you execte web services.  Any attempt to access a GUI control's properties from outside this UI thread could cause unexpected behavior, including the program locking up and no longer responding.  Often you can get away with reading a control's properties without locking up, but setting a Label Text property is much more likely to cause a deadlock.  The way you access a control's properties from your worker threads, then, is using Control.Invoke.  The method description for Control.Invoke says:

"Executes the specified delegate on the thread that owns the control's underlying window handle."

That should be fairly straight forward.  There is a Message Pump running.  Every time something happens in GUI code that changes some visual aspect of a control it occurs as the Message Pump dispatches messages.  Code you call directly from the UI thread automatically gets executed this way,  Control.Invoke gives you a way to execute some code from within this message pump without exposing the internals to you.  On Compact Framework 1.0 only the version of Control.Invoke that takes a no-arguments Delegate is supported.  In CF 2.0, Control.Invoke (Delegate, object[]) is also supported, which eliminates the need for the following tip:

Since you cannot pass arguments to Control.Invoke in CF 1.0 you may need a way to communicate with your UI controls from a worker thread.  For example, suppose you have a custom sync process running on a worker thread and you want to update a Label each time a table is processed with the name of the next table name.  I would argue that the worker thread having any knowledge of the UI controls is bad, so you need a better means of communicating.  The Mediator/Director pattern from the GOF design patterns book provides a solution.  Consider the following:

On the Worker thread class:

OnPullStart(new PullStartEventArgs("Next Table Name"));

...

public event PullStartEventHandler PullStart;
        protected void OnPullStart(PullStartEventArgs e)
{
  if (this.PullStart != null)
  {
    PullStart(this, e);
  }
}

On the Director we have a method or property we can talk to which keeps the UI control decoupled from other classes:

...
public string SyncMessage
{
get{ return _syncMessage;}
set
{
_syncMessage = value;
_someControl.Invoke( new EventHandhler(_someControl.SyncMessageChanged));
}
}
...

On the GUI control owning the Label, which also kicked off the worker thread, you supply an EventHandler:

public void SyncMessageChanged(object sender, EventArgs e)
{
  _someLabel.Text = Director.Instance.SyncMessage;
}

Note that in some cases you may wish to synchronize access to the value from the Director.  Every application is different.  This is a somewhat simplified example.  In my real applications I typically have a "Context" (Possibly State pattern) object of some kind which keeps State for me, and the Director just fires messages.  There are different techniques to use, but I will offer some tips:

  1. Whenver dealing with updating the UI, always be concious of "What thread is this method being called from?"
  2. In light of #1, be sure ALL code that touches GUI components has come from an invoke method.  Sometimes even Querying a Control's property from outside the UI thread can cause a lockup.
  3. Use Events on a worker thread to signal a significant event that a client class may be interested in.
  4. Do not pass instances of your controls to worker threads so that the worker can call Invoke, use a technique that decouples the worker thread from any classes that might subscribe to the worker class's events.

Modal Mixing

The very excellent OpenNETCF libraries for the .Net compact framework provides some extended functionality for processing windows messages.  Much of this is in the ApplicationEx class, allowing you to run your GUI's main form using ApplicationEx.Run( new MyForm() );.  I have also experienced different GUI lockup situations where different ShowModal() and MessageBox calls are mixed.  If you need to show a modal Form be sure to use the new ApplicationEx.ShowDialog(Form, bool dispose) rather than Form.ShowDialog(); this will ensure the form is blocking the correct message pump.

The next threading article will be a short one discussing a couple of easy performance tricks you can use Threads for in ASP.NET.  In the article after that I will show some other threading techniques.

Tuesday, December 20, 2005 11:19:48 AM (Central Standard Time, UTC-06:00)  #    Disclaimer  |  Comments [0]  |  Trackback