Support Statement: Dealing with Twips to Pixels

Dealing with Twips to Pixels

By default, VB6 describe the size and layout of elements on a form using a unit of measure called a Twip. WinForms .NET on the other hand uses Pixels. Generally speaking, you can think of a Twip as 1/15 of a Pixel. So, for example, a button that is 300 Twips wide in VB6 should be 20 Pixels wide in WinForms.
It is quite common to find hard coded numeric constants in the calculations for laying layout and sizing. For example, here is a const and a function, taken from our FileExplorer sample. These are used to layout and size controls in response to user actions:

Const mSplitLimit = 2000 ' Minimum width for TreeView and ListView

Sub SizeControls(X As Single)
    ' Set sizes and locations for the movable controls
    On Error GoTo ErrorHandler

    'Set the TreeView's width
    If X < 1500 Then X = 1500
    If X > (Me.Width - 1500) Then X = Me.Width - 1500
    tvTreeView.Width = X
    imgSplitter.Left = X

    ' Set up the ListView
    With lvListView
        .Left = X + 75
        .Width = Me.Width - (tvTreeView.Width + 190)
        lblPath.Left = .Left + 30
        lblPath.Width = .Width - 60
        RichTextBox1.Left = .Left
        RichTextBox1.Width = .Width
    End With
ErrorHandler:
    ' trying to resize during minimize?
End Sub


In the code above, calculations involving numeric literals should be modified so they are expressed as Pixels rather than Twips.

Process for Twips Migration

1) Identify the application and COM API symbols that are involved with control/form sizing and layout. We typically refer to these as Screen Geometry variables. These may be identified in several ways:

  • Expressions with names containing Left, Top, Width, Height, and variations of these (e.g ColumnWidth, ColWidth, RowHeight, etc.)
  • Variables used in Resize event handlers and supporting functions (e.g. SizeControls above
  • Variables known to be used for layout based on your knowledge of the legacy code or COM Controls. (e.g. Constants defining limits and default sizes)

For example, you may search the VB6 code for names in assignments that include numeric literals using the Source Search with a pattern such as this:

(\w+Height|\w+Top|\w+Width|\w+Left|\w+Bottom)\s{0,3}(\s*As.?\s*)?=.?-?\d\d+


The regular expression pattern above will ignore assignments to geometry variables in the Form property bag (e.g. ClientHeight, ClientWidth, etc) as these are already handled as Twips by the tool. 

Fully qualified identifiers may be reported using the Search panel / Symbols report. For example:

FileExplorer.frmExploreLite.mSplitLimit 
FileExplorer.frmExploreLite.SizeControls.X


2) Once you have the fully qualified identifiers of the variables computed using Twips literals, you may prepare FixType statements to tell gmStudio to make a Twips to Pixels adjustment to the literal values used in computing those variables. This is done by changing the type of the variable to TwipsX or TwipsY using a FixType statement. This is typically done in a Compile\Refactor block:

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
C:\gmTestBed\FileExplorer\proj_csh\usr\tran.FileExplorer_mgd.xml
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   41 <Compile Project="%SrcPath%" Resx="%ResxFolder%">
   42 <Refactor>
   43    <!-- Some application variables are used for screen geometry so Twips must be converted to pixels -->
>  44    <FixType identifier="FileExplorer.frmExploreLite.mSplitLimit" type="TwipsX" />
   45    <Fixtype identifier="FileExplorer.frmExploreLite.SizeControls.X" type="TwipsX" status="ByVal" />
   46 </Refactor>
   47 </Compile>

In addition, you will often use type="TwipsX" with properties and method arguments declared in Interface Description Files and RefactorLibrary for COM APIs:

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
C:\gmTestBed\FileExplorer\proj_csh\usr\Mscomctl.ocx.WinForms.Refactor.xml
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~>
71  <Migrate id="IToolbar.ButtonHeight" type="TwipsY" migName="ButtonSize.Height" />
72  <Migrate id="IToolbar.ButtonWidth" type="TwipsX" migName="ButtonSize.Width" />
178 <Migrate id="IPanel.Width" type="TwipsX" />
203 <Migrate id="ITreeView.Indentation" type="TwipsX" />
422 <Migrate id="IColumnHeaders.Add.Width" status="ByVal" type="TwipsX" Optional="1440"/>
431 <Migrate id="IColumnHeader.Width" type="TwipsX"/>
497 <Migrate id="IComboItem.Indentation" type="TwipsX"/>
501 <Migrate id="IImageCombo.Indentation" type="TwipsX"/>
736 <property id="Width" type="TwipsX" value="IColumnHeader.Width"/>

Given these rules, the translation will express the screen geometry as Pixels:

      private const int mSplitLimit = (2000)/15;     //  Minimum width for TreeView and ListView
      ...
      public void SizeControls(int X)
      {
         //  Set sizes and locations for the movable controls
         try
         {
            gmRTL.Core.GlobalException.Clear();

            // Set the TreeView's width
            if (X < 100)
            {
               X = 100;
            }
            if (X > this.Width - 100)
            {
               X = ((int)(this.Width - 100));
            }
            tvTreeView.Width = X;
            imgSplitter.Left = X;

            //  Set up the ListView
            lvListView.Left = ((int)(X + 5));
            lvListView.Width = ((int)(this.Width - (tvTreeView.Width + 13)));
            lblPath.Left = ((int)(lvListView.Left + 2));
            lblPath.Width = ((int)(lvListView.Width - 4));
            RichTextBox1.Left = lvListView.Left;
            RichTextBox1.Width = lvListView.Width;
         }
         catch(Exception exc)
         {
            gmRTL.Core.GlobalException.Initialize(exc);
         }
         //  trying to resize during minimize?
      }