gmplPatternStatement

Pattern Statement Summary

I have added a pattern gmPL statement that can be used as an alternative way to associate surface patterns with components in the class. The intent is to eventually replace the current migPattern,... attributes with the new command.


<property id="FontSize" type="Single" status="GetSet" opcode="PBX.14" />
<pattern id="FontSize.Get" >
   <wpf narg="1" code="%1d.FontSize" />
   <all narg="1" code="%1d.Font.Size" />
</pattern>
<pattern id="FontSize.Set" >
   <wpf narg="2" code="%2d.FontSize = (%1d * 1.333)\c" />
   <all narg="2" code="%2d.Font = MigrationSupport.Utils.FontChangeSize(%2d.Font, %1d)\c" />
</pattern>

The attributes of the Pattern statement are as follows:

Attribute Description
id This string attribute identifies the component to be patterned.

The script errors associated with the Get, Set, or Let statements are as follows:

Error Description
1074 Pattern command missing required id attribute.
1075 The pattern identifier [%1d] is not recognized.


How do I associate patterns with properties in IDF Classes?

Patterns are associated with properties in IDF classes using the GetSet status followed by patterns that specify how the reference is to be authored when its value is being used, get pattern, and when its value is being assigned, set pattern. The important point to remember is that these patterns must account not just for the reference itself, but also for the surrounding content.

Consider the TabHeight property of the ISSTabCtl class of TABCTL32.OCX. Its default entry in its IDF is


<class id="ISSTabCtl" parent="IDispatch" default="_Tab">
   <property id="TabHeight" type="Single" status="InOut"/>
</class>
Using this entry the translations of references to this property in the VB6 codes


Private Sub Check2_Click()
   SSTab1.TabHeight = IIf(Check2.Value = vbChecked, 600, 295)
End Sub
Private Sub Form_Load()
   Check2.Value = IIf(SSTab1.TabHeight = 600, vbChecked, vbUnchecked)
   Text2 = SSTab1.TabHeight
End Sub
become in C#


private void Check2_Click(object sender, RoutedEventArgs e)
{
   SSTab1.TabHeight = (Check2.IsChecked.Value == true ? 600 : 295);
}
private void Form_Load(object sender, RoutedEventArgs e)
{
   Check2.IsChecked = (SSTab1.TabHeight == 600 ? true : false);
   Text2.Text = Convert.ToString(SSTab1.TabHeight);
}
For this example a translation to WPF is being performed in which the host SSTab1 of the property must be written as ((TabItem)SSTab1.Items[0]) and the property itself must be written as ActualHeight. Within the surface pattern logic, then, the representation of the host of a property must be taken as an argument for it. In addition, when a value is being assigned to a property that value precedes the host.property pair and must also be taken as an additional argument for its pattern. Here is a revised specification for the property


<property id="TabHeight" type="TwipsY" status="GetSet"/>
<pattern id="TabHeight.Get" >
   <wpf narg="1" code="((TabItem)%1d.Items[0]).ActualHeight"/>
   <all narg="1" code="%1d.TabHeight"/>
</pattern>
<pattern id="TabHeight.Set" >
   <wpf narg="2" code="((TabItem)%2d.Items[0]).ActualHeight = %1d\c"/>
   <all narg="2" code="%2d.TabHeight = %1d\c"/>
</pattern>
Notice first that GetSet status must be specified for the property. It is this status that triggers the use of the patterns. The patterns themselves are standard. The all patterns generate the same surface forms as the un migrated code. As discussed above, the get patterns have 1 argument which is the representation of the host of the property. The set pattern has two arguments: argument 1 is the representation of the expression being assigned; and argument 2 is the representation of the host of the property. Using these patterns, the translations become


private void Check2_Click(object sender, RoutedEventArgs e)
{
   ((TabItem)SSTab1.Items[0]).ActualHeight = (Check2.IsChecked.Value == true ? 40 : 20);
}
private void Form_Load(object sender, RoutedEventArgs e)
{
   Check2.IsChecked = (((TabItem)SSTab1.Items[0]).ActualHeight == 40 ? true : false);
   Text2.Text = Convert.ToString(((TabItem)SSTab1.Items[0]).ActualHeight);
}

How do I associate patterns with accessors in IDF Classes?

An accessor is a property with both set and get access that has an argument. It occurs within Class statements within description files authored from IDL-- i.e., external COM components. Patterns are associated with accessors in much the same way as they are associated with properties. They are assigned the GetSet status and are then followed by patterns that specify how the reference is to be authored when its value is being used, get pattern, and when its value is being assigned, set pattern. The important point to remember is that these patterns must account not just for the reference and its argument themselves, but also for the surrounding content.

Consider the TabEnabled property of the ISSTabCtl class of TABCTL32.OCX. Its default entry in its IDF is


<class id="ISSTabCtl" parent="IDispatch" default="_Tab">
   <accessor id="TabEnabled" type="Boolean">
      <argument id="sIndex" type="Short" status="ByVal"/>
   </accessor>
</class>
Using this entry the translations of references to this accessor in the VB6 codes


Private Sub Check1_Click()
   SSTab1.TabEnabled(1) = (Check1.Value = vbChecked)
End Sub
Private Sub Form_Load()
    Check1.Value = IIf(SSTab1.TabEnabled(1), vbChecked, vbUnchecked)
End Sub
become in C#


private void Check1_Click(object sender, RoutedEventArgs e)
{
   SSTab1.set_TabEnabled(1,Check1.IsChecked.Value == true);
}
private void Form_Load(object sender, RoutedEventArgs e)
{
   Check1.IsChecked = (SSTab1.TabEnabled(1) ? true : false);
}
The default translation of accessors does not assume that the accessor in the set context has the same reference form as the get context because direct assignments to functions like this


myMethod(myValue) = newValue;
are not typically well formed in the target languages. Consequently the set is converted to a method with the prefix set_ whose first argument is the original accessor argument and whose second argument is the value being assigned. The same strategy must followed when associating patterns with accessors.

For this example a translation to WPF is being performed in which the host SSTab1 of the accessor must be written as ((TabItem)SSTab1...) and the accessor itself must be written as Items[argument].IsEnabled. Notice that the migrated form converts the argument into a subscript which can now appear in a direct assignment. Within the surface pattern logic, then, the representation of the host of an accessor must be taken as an argument for it as well as the representation of the argument. In addition, when a value is being assigned to an accessor that value precedes the host.accessor(argument) pair and must also be taken as an additional argument for its pattern. Here is a revised specification for the accessor


<accessor id="TabEnabled" type="Boolean" status="GetSet">
   <argument id="sIndex" type="Integer" status="ByVal"/>
</accessor>
<pattern id="TabEnabled.Get" >
   <wpf narg="2" code="((TabItem)%1d.Items[%2d]).IsEnabled"/>
   <all narg="2" code="%1d.TabEnabled(%2d)"/>
</pattern>
<pattern id="TabEnabled.Set" >
   <wpf narg="3" code="((TabItem)%2d.Items[%3d]).IsEnabled = %1d\c" />
   <all narg="3" code="%2d.set_TabEnabled(%3d,%1d)\c" />
</pattern>
Notice first that GetSet status must be specified for the accessor. It is this status that triggers the use of the patterns. The patterns themselves are standard. The all patterns generate the same surface forms as the un migrated code. As discussed above for the get patterns argument 1 is the representation of the host of the accessor and argument 2 is the representation of the accessor argument. The set pattern has three arguments: argument 1 is the representation of the expression being assigned; argument 2 is the representation of the host of the accessor; and argument 3 is the representation of the accessor argument. Note that the all patterns produce the same surface form as the default. Using these patterns with


<Select SubSystem="wpf"/>
produces the following translations


private void Check1_Click(object sender, RoutedEventArgs e)
{
   ((TabItem)SSTab1.Items[1]).IsEnabled = Check1.IsChecked.Value == true;
}
private void Form_Load(object sender, RoutedEventArgs e)
{
   Check1.IsChecked = (((TabItem)SSTab1.Items[1]).IsEnabled ? true : false);
}

Why do VB6 Decimal values with TWIPS types get truncated?

The migration to WPF uses decimal values as opposed to the TWIPS types used for WinForms. This is causing problems when decimal values appear in the property specification. For instance, I made the PictureBox Scale properties become -1.25, -1.25, 1.25, 1.25 to show that decimals and negatives are allowed in VB6. I then translated to WPF and got ScaleLeft="1" ScaleTop="1" ScaleWidth="1" ScaleHeight="1". The result should be ScaleLeft="-1.25" ScaleTop="-1.25" ScaleWidth="1.25" ScaleHeight="1.25".

WPF assumes double types where as WinForms assumes TWIPS types, which are integer types. If you read a decimal value into an integer type it will be truncated. WPF has to use a special vbcontrols.xml file that declares these properties as double. Below are the changes to be made for this example.


Comparing files \PROMULA\METALANG\vbcontrols.xml and \JIRA\PRM352\PROJ\LNG\VBCONTROLS.XML
\PROMULA\METALANG\vbcontrols.xml
   </pattern>
   <property id="ScaleLeft" type="TwipsX" status="GetSet" opcode="PBX.23" />
   <pattern id="ScaleLeft.Get" >
\JIRA\PRM352\PROJ\LNG\VBCONTROLS.XML
   </pattern>
   <property id="ScaleLeft" type="Double" status="GetSet" opcode="PBX.23" />
   <pattern id="ScaleLeft.Get" >
  ...
\PROMULA\METALANG\vbcontrols.xml
   </pattern>
   <property id="ScaleTop" type="TwipsY" status="GetSet" opcode="PBX.24" />
   <pattern id="ScaleTop.Get" >
\JIRA\PRM352\PROJ\LNG\VBCONTROLS.XML
   </pattern>
   <property id="ScaleTop" type="Double" status="GetSet" opcode="PBX.24" />
   <pattern id="ScaleTop.Get" >
 ...

Compiling a language file "wpfLang.vbi" with these changes and using a translation script that starts as follows


<gmBasic languageFile="C:\Jira\PRM352\proj\lng\wpfLang.vbi" >
the following correct output is obtained.


<my:GraphicsPanel Name="ClientRectangle" Background="#FFF0F0F0" Margin="0">
 <my:GraphicsPanel Name="Picture1" Canvas.Left="104" Canvas.Top="72" Height="105" Width="233" ScaleLeft="-1.25"
    ScaleTop="-1.25" ScaleWidth="1.25" ScaleHeight="1.25" ScaleMode="MigrationSupport.WPF.ScaleMode.User" Margin="3" />
<Button Name="Command1" Canvas.Left="88" Canvas.Top="40" Height="33" Width="57" TabIndex="1" Content="Command1" />


Table of Contents