The Case of the Mysteriously Changing Font

Published on Author Michael

A while back I wrote up a simple IP address control for WinForms. This control relied on the underlying IP Address common control from Windows.

The goal was to provide the ability, in WinForms, to enter and validate an IP address without having to write a bunch of code. 

But a mysterious problem occurred when the control was used more than once in the same process.  The font would mysteriously change to bold.  This problem will be discussed later. 

Here is the process I used to create the control and ultimately track down and fix the issue.

Window Classes

When a window is created it must be assigned a window class.  The class determines, amongst other things, some basic properties that all windows of the class inherit.  This includes the default window procedure used to process messages.  Each window class must be registered using RegisterClassEx before it can be used.  The common controls in Windows each use their own window class.  Before the common controls can be used InitCommonControlsEx must be called to registered the common window classes.  Once a window class has been registered an instance of the window class is created by passing the class to the CreateWindowEx function in Windows.  When a common control’s window class is used in the call an instance of one of the common controls is created.

In WinForms the common controls are initialized automatically so no additional effort is needed.  For most of the common controls an existing WinForm control exists to wrap it.  Under the hood each control will pass the appropriate window class to the create call.  The property CreateParamss is responsible for configuring the window before it is created.  To create an instance of a common control the property is overridden to pass the appropriate window class. 

Creating the Control

To create the IP control first we create a new class called IPAddressTextBox and derived it from TextBox.  Because this is a common control CreateParams has to be overridden to use the appropriate window class.  Now when the control is created it will appear and work like the standard IP Address control in Windows.  Here is the basic code.

public class IPAddressTextBox : TextBox
{
   protected override CreateParams CreateParams
   {
      get
      {
         CreateParams parms = basee.CreateParams;

         parms.ClassName = “SysIPAddress32”;;
                                
         return parms;
      }
   }
}

 

Propertiess

The IP Address control does not have any custom styles so there is no need to expose any additional properties.  However the base TextBox class has a couple of properties that do not make sense for the IP control such as maximum text length and if the control supports multiline.  These properties are (fortunately) virtual so they are overridden to do nothing, hidden from the editor and hidden from the designer.  Here is the code used to hide the unneeded properties.

[Browsable(false)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
[EditorBrowsable(EditorBrowsableState.Never)]
public override int MaxLength
{
   get { returnn 15; }
   set { }
}

[Browsable(false)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
[EditorBrowsable(EditorBrowsableState.Never)]
public override bool Multiline
{
   get { returnrn false; }
   set { }
}

[Browsable(false)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
[EditorBrowsable(EditorBrowsableState.Never)]
public override bool ShortcutsEnabled
{
   get { return true; }
   set { }
}

The Browsable attribute controls whether the property shows up in the Properties grid or not.  The DesignerSerializationVisibility attribute controls what and if the property is serialized into the designer file.  The EditorBrowsable attribute controls whether the property is always, sometimes or never shown in the editor (Intellisense).

To make the control easier to use the control’s value will be exposed as an IPAddress value.  This simply requires that the text value be parsed.  Here is the property to expose the IP address.

[Browsable(false)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public IPAddress Value
{
   get 
   {
      IPAddress addr;
      IPAddress.TryParse(basee.Text, out addr);

      return addr ?? IPAddress.None;
   }
                
   set
   {                                
      string addr = ((value != nulll) ? value.ToString() : “”);
      if (basee.Text != addr)
      {
         base.Text = addr;

         OnValueChanged();
      };
   }
}

This basic implementation ignores invalid data.  The setter calls a protected virtual method to raise an event when the value changes. This provides parallel functionality to the TextChanged event that all controls inherit.

Methods

The IP address control has only one custom message that can be exposed.  The IP address supports setting the focus to the individual portions of the address.  A SetFocus method is exposed to allow developers to set the focus to each of the individual fields of the address.

public void Focus ( byte field )
{
   if (IsHandleCreated)
      SafeNativeMethods.SendMessage(Handle, IPM_SETFOCUS, field, 00);
}

The method simply sends the appropriate message to the window using P/Invoke.

A few additional methods are defined to handle some basic input processing and to update the value if the text changes.

Problem In Paradise

The basic control is done.  Compiling and running the control in a sample Winform app shows that it is working correctly.  But there is a problem.  Once the control is destroyed (such as by closing the owning form) then subsequent uses of the control cause the font to be in bold.  It does not matter whether the same instance was used or an entirely new one was created.  Debugging the problem and tweaking with the control did not resolve the problem.  The font was somehow getting corrupted.  A little INet searching revealed that the IP Address control has a “bug” (this came from a message posted by an MS engineer).  The underlying common control will delete the font it is using when the control is destroyed.  While this sounds like a good thing it does introduce a serious problem.  In Winforms controls will use the same standard font by default.  If the default font is passed to the IP control and the control then deletes it then the standard font is corrupted and subsequent use will cause bizarre behavior. 

Fixing the Problem

Fortunately fixing this problem is not that difficult.  What needs to happen is that the underlying IP control needs to get its own font when it is created.  When the control eventually gets destroyed it will then delete the copy rather than the original. 

A control gets its font from the Font property.  To ensure that the control always gets its own copy of the font it needs to be cloned.  This is easily handled by overriding the Font property and simply cloning the font in the setter.

public override Font Font
{
   get { return base.Font; }
   set
   {
      base.Font = (Font)value.Clone();
   }
}

One final change is needed.  When the control is first created it will get the default font so the constructor of the control needs to ensure that a copy of the default font is used instead.

public IPAddressTextBox ( )
{
   this.Font = base.Font;
}

With these changes in place the control now gets its own copy of the font.  When the underlying IP control is destroyed it will delete the copy and the default font remains unaffected. 

Caveats

The attached file contains the control and a simple test program.  The control, as provided, is used in production code but might still contain some issues.  Before using this code in any production code be sure to fully test it.