==============================================================================================
EXAMPLES ON CONTROLS
=====================
Objects and Keynames:
=====================
Whenever you create a control like a button or a text field, The .NET creates an object for
that control which contains all its setup data and stores the object into memory. One essential
item which you must specify whenever you create a new object is the name of its reference.
The reference is a variable which stores the location of the object in memory. Although a
reference is an independant variable which is not part of the Object itself, it identifies the
object so, for simplicity we may call objects here by the name of their references.
Objects consume plenty of computer resources. So we can't keep them unless we need them. For
this reason, the .NET includes a program which is called "the Garbage Collector". This program
runs constantly while your program is running. It eliminates any object whenever it finds no
reference pointing to it. This means that if (clp) has been a reference to the first color object
which you have created, then you used it as a reference of a second color object, you expect the
first color's object to be disposed shortly since it becomes unreferenced.
Personal C Sharp makes use of this automatic object disposal feature in dealing with many
objects. For example, after we create a shape object (like a rectangle or a circle) and draw it
on the screen, the object becomes of no use, so we get rid of it by making the same reference
refers to the next object which we create. This means that we use only one reference for all
shape objects. We call that reference the "Present Object Reference" since it always refers to
the object which we are presently working on. To make sure you understand this mechanism, click
on "Reference-Desktop" at the top menu and read the section titled "WHAT IS THE PRESENT OBJECT?".
With some object types like controls, we cannot allow any object of their kind to be lost.
This is because the user may activate any of the controls which you have created at any moment
during the execution of your program. Since objects are hard to work with, PC# prefers to deal
with them internally while letting you refer to controls with easy to use string type variables
called "keynames".
PC# keeps references to all the control objects which you have created into its archives. It also
keeps the keynames which you have requested to identify them with into archives in a way which
makes it possible to cross the keyname of an object to its reference.
If you have created a button using the keyname "bt0" then later you wanted to set its color by
calling method cm("sC"), you need to precede the call with the assignment (cs="bt0") The first
thing the method will do is to cross the keyname to the archived object reference, make (btp)
which is the "Present Button Object Reference" another reference to same object then perform the
color setup operation on (btp) After the operation is done, (btp) stays referencing that
button's object until you call a method to operate on another button.
When we select a keyname or a present object reference for a control we start by obtaining the
two character abbreviation of the object name based on the following rules:
(1) The first char is the first letter in the object's name.
(2) If the name is made of one word, the second char is the next non vowel letter in the
name. If the name is made of more than one word, the second char is the first letter of
the second word.
To name the present object of the control, we simply add the letter 'p' to the 2-char
abbreviation. For a keyname, we add any integer in the range (0:89) to the 2-char abbreviation
in order to identify each control we create. This means that the total length of a keyname can
be either 3 or 4.
Notice that the range (90:99) is reserved for keynames which PC# uses internally.
We hope that you now understand what the present objects and keynames are for. If you could
not find it easy to understand, don't worry. Study the following examples then return back
to it.
=========================================================================================
EXAMPLE 1: Let us start with a simple one. We'll create a button and a text field. If
the user enters "red", "green" or "blue" into the text field then clicks the button,
the button background color changes accordingly. If he enters any other phrase, the
button's background color turns yellow.
=========================================================================================
public class a : pcs {
public override void init() {
base.init();
}
public override void setup() { // All installations are done within this method
cs="tf0";k=50;i=200;o=20;cm("i");
// Install text field "tf0", positioned on the window
// at point (0,50), width=200, height=20 (pixels)
cs="bt0";cis="My Color Changes";k=-50;i=140;o=40;cls="S9y0";cm("i");
// Install button "bt0", positioned at point(0,-50)
// width=140, height=40, black txt in yellow backgrnd
}
public override void update() { // This method is called whenever an event takes place
if ("bt0".Equals(cs)) { // If button "bt0" has generated this event
cs="tf0";cm("gu"); // Get latest update of text field "tf0" in (cus)
os=cus.Substring(0,1);om("u");// Get 1st char of (cus) and covert it to U/C
n="RGB".IndexOf(os); // See if it was R,G,B or none of them
if (n<0) cls="S9y0"; // If none of them select yello background color
else if (n==0) cls="S9r0"; // If was "R" select red background color
else if (n==1) cls="S9g0"; // If was "G" select green background color
else if (n==2) cls="S9b0"; // If was "B" select blue background color
cs="bt0";cm("sC"); // Call method cm() to set the color for "bt0"
cs="tf0";cus="";cm("su"); // Erase tf0's text.
cs="tf0";cm("sx"); // Turn the focus state of "tf0" on.
}
}
}
==============================================================================================
HOW TO WRITE, COMPILE AND RUN THE PROGRAM? See Example 1 in the General Examples list.
==============================================================================================
TUTORIAL:
(1) This program contains two new methods, the "setup()" and the "update() methods. Method
setup is where you request the creation of new controls, how they look and where they should
be. Method update() is called automatically whenever an event concerning one of the controls
you have created takes place.
(2) As you can see, you did not need to set delegates or to work with objects. References to the
Objects of all the controls created are stored into PC# archives. Whenever you like to
operate on a specific control, you supply method cm() with the keyname of the control
assigned to (cs) together with any other required data and select the method's mode which
performs the operation.
(3) INSTALLING CONTROLS: To install a control, you call cm("i") with the control's keyname and
its installation data. The installation data are many, we'll list few of them here and keep
the rest for the coming examples. There are default values for most of the data. Control's
location and size are the only required data.
j,k = Horizontal and vertical locations of control's center relative to form's ceter.
i,o = Control's width and height.
cls = Control's foreground color code followed with its background color code.
fns = Font code for control's text.
Control locations are measured relative to form's center instead of the top left corner of
the form. This simplifies installation significantly. You must have noticed that the
horizontal position (j) has not been specified for either control. That was because we wanted
them to be centered into the Form horizontally, so (j=0) for both. We also wanted them to be
at equal distances from the center vertically, so we choosed (k=50) for "tf0" and (k=-50)
for "bt0".
So, we have nicely centered controls into the form with maximum ease. The best of all is that
you can change the size of the form as you like while keeping controls at the center without
having to modify your code. The user may also maximize the window or change its size by
dragging its edges while controls always maintain their position at the center.
When you move to the chapter of "Drawing", you'll see additional advantages in making all
measurments relative to the form's center. In addition to making drawing an easy and simple
task, it is the convenient and scientific way to draw.
k
|
-ve j | +ve j
+ve k | +ve k
|
--------------+------------- j
|
-ve j | +ve j
-ve k | -ve k
|
(4) When the user clicks on the button "bt0", class (pcs) receives the event, makes the
assignment (cs="bt0") then calls method update()
Generally, method update() is made of several blocks, one for each control which is expected
to generate an event to be handled. In this example, the only control which can generate an
event is "bt0", so there is only one block.
(5) The text field "tf0" also generates an event which method update() can receive if the user
pushes the [Enter] key after typing his / her text. We could have handled this event directly
if we have included a block in method update() which starts with "if ("tf0".Equals(cs)) {".
However, in this example we did not handle that event.
(6) We can always get tf0's latest update any time by calling cm("gu"). The term "getting latest
update" means different things for different controls. For a text field, it means getting the
text it contains and returning its value assigned to the "text update string variable" (cus).
For a Combo Box it means getting the order of selected row and returning its value assigned
to "the update integer variable" (cui)
(7) We can also write into a text field programmatically using method cm("su") We have used
this feature to erase the text field with:
cs="tf0";cus="";cm("su"); // erase tf0's text.
(8) We can also turn a control's focus on programmatically using method cm("sx"). We have done
that to "tf0" at the end of the event handling block in order to save user the time spent in
clicking into the text field each time he/she wants to write into it.
(9) In order to simplify the code, we have been checking the first char only of user's entered
text in order to know which color he / she has selected. Sorry if this didn't please you.
=========================================================================================

EXAMPLE 2: Let us now get further, we are going to use several controls with variety of
sizes, colors, fonts and texts. Here are what we need to do:
(a) Create a Combo Box which contains several items and a label to identify it.
(b) Create a text field with a button by its side. Whenever the user enters a string into
the text field and clicks the button, the string is added to the items the Combo
Box contains. The button label will be "Add". The text field will contain some default
text at startup.
(c) Create another text field, also with a button by its side. Whenever the user
enters a string into that text box and clicks the button, the string will be compared
against all items the Combo Box contains, if a match was found, that item will be
selected. The button's label will be "Select"
(d) Create a third button labled "Which Item is Selected". Whenever the user clicks this
button, The last selected item of the Combo Box will be determined. Its text and order
number will be displayed into an external dialog box.
=========================================================================================
public class a : pcs {
public override void init() {
base.init();
}
public override void setup() {
j=400;k=300;cm("fs"); // Resize Form to (400,300) Pixels
cs="bt0";cis="Add";j=120;k=100;i=90;o=30;cls="r0y0";
fns="trbsui16";id=0;cm("i"); // bt0: location (120,100), dimensions (90,30)
// See Tutorial for (id)
cs="bt1";cis="Select";j=120;k=50;i=90;o=30;cls="r0g7";fns="trbi16";id=2;cm("i");
cs="bt2";cis="Which Item is Selected?";i=340;o=30;cls="b0r7";
fns="trb16";id=3;cm("i");
cs="lb0";cis="Test ComboBox >";j=-110;k=-90;i=120;o=30;os="e";cls="r0s7";
fns="tri11";id=-1;cm("i");
cs="tf0";cus="Banana";j=-60;k=100;i=225;o=30;cls="b0p7";fns="trb16";id=4;cm("i");
cs="tf1";cis="";j=-60;k=50;i=225;o=30;cls="S9b7";fns="tr16";id=5;cm("i");
// Installing a Combo box: cus=top row's text, CUS[]=Items of all other rows in order.
cs="ch0";cus="Select a fruit";j=80;k=-90;i=190;o=40;cls="b0m6";fns="trb16";
CIS=new string[] {"Apples","Oranges","Peaches","Pears"};
id=6;cm("i");
}
public override void update() {
if ("bt0".Equals(cs)) { // If bt0 clicked
cs="tf0";cm("gu"); // Get tf0 update (its text) into (cus)
CIS[0]=cus;cs="ch0";cm("sL"); // Add it to ch0's list.
}
if ("bt1".Equals(cs)) { // if bt1 clicked
cs="tf1";cm("gu"); // Get tf1's update value (its text) into (cus)
string text=cus; // Save (cus) temporarely
cs="ch0";cm("O"); // Make (chp) refers to ch0's object.
int index = chp.FindString(text); // Search it for tf1's text match
cus="";cui=index;cs="ch0";cm("su");// update ch0 (make cui its selected index)
}
if ("bt2".Equals(cs)) { // if bt2 clicked
cs="ch0";cm("gu");string text=cus; // Get ch0 update (cus=selected text, cui=selectd index)
os="Selected Item's Text: " + text + "\n" + "Index: " + cui;cm("d");
//Display results in a dialog
}
}
}
=========================================================================================
HOW TO WRITE, COMPILE AND RUN THE PROGRAM? See Example 1 in the General Examples list.
=========================================================================================
TUTORIAL:
(1) You already know how controls are created within method setup() The only difference
in this example is that more setup items have been given values instead of acceping their
defaults. Those items are:
cis: Control's label. Necessary for buttons, labels and Check Boxes.
os : Label alignment. Can be: os="c":Align at Control's center which is the default.
os="w":Align at left side (west) of the Control.
os="e":Align at right side (east) of the Control.
cus: (Optional) Update string. In case of a text field, if assigned a string value at
installation, the string will appear into the field at startup as a default choice.
id : Tab Index. When you push the tab key repeatedly, controls gain focus in an order
which is determined by the value of (id) for each control. if (id<0) the control
will never gain focus.
Here are all controls and the value assigned to (id) for each:
bt0:0 bt1:2 bt2:3 lb0:-1 tf0:4 tf1:5 ch0:6
So when you hit the tab key repeatedly, the controls will gain focus at the same
order as listed above and lb0 will be skipped.
(2) INSTALLING THE COMBO BOX: The combo box gives you the choice of either selecting one
item from its list or if you could not find an item that you like enter a new item in its
top box to make it your choice just like you do with text fields. Installing a Combo Box
requires the following additional setup items:
CIS[]: A string array which contains all selectable items.
cui : (Optional) Index of selected item. If we like to supply user with a default
selection, we supply this integer value.
cus : (Optional) It can be also used as a default choice for the new item the user may
choose. However, it can also be used as a title for the Combo Box like we did in
this example.
REMARKS:
--------
a) cus, cui are update var's, which means that they are mainly for getting user's entered
data back to the program. We include them in our setup data only when we like to supply
default selection for the user.
b) In the case of a Combo Box, whenever we like to supply default selection, we should
either supply (cus) or cui. This is because the user can either select an item from
list or enter a new item into the top box.
(3) In the first block of method update(); we have obtained the string which (tf0) contains,
assigned it to CIS[] and called the method to add it to the list. Please note that CIS[]
is reset before the method return in modes "i" and "s". This means that all its rows are
assigned empty string "".
(4) In the second block of method update(), we needed to search all ch0's selections to
see if any matchs the string which the user has entered into "tf1". If a match was found,
we needed to know the index of the matching item in order to make it the selected item.
Although C# can do this search for us, PC# does not get involved into this particular
feature, so we must do it on our own. We ask method cm() to supply us with a reference
to ch0's object which is kept in PC#'s archives. PC# responds by making the "present Combo
Box object reference" which you must know that its name is (chp) refers to "ch0".
If you are wondering why the two letter base name of the combo box is "ch" instead
of "cb", it is because it was initially called "Choice" and the name "cb" is reserved for
the "Check Box".
=========================================================================================

==========================================================================================
EXAMPLE 3: Let us now get further, We need to create two "Radio Button groups, two Check
box groups and two "List Box" sets. One set of "single selection type" and the other of
"multiple selection" type. Additionally we'll interpret the events received from them all
and display what they mean. We are going also to create two "Text Area" controls to use
for display.
==========================================================================================
public class a : pcs {
public override void init() {
base.init();
}
public override void setup() {
cs="rb00";cis="rb00";j=-330;k=150;i=80;o=30;cls="b09s00";cm("i"); // 1st rb group
cs="rb01";cis="rb01";j=-210;k=150;i=80;o=30;cls="b09s00";cm("i");
cs="rb02";cis="rb02";j=-90;k=150;i=80;o=30;cls="b09s00";cm("i");
cs="rb10";cis="rb10";j=-330;k=100;i=80;o=30;cls="b09s00";cm("i"); // 2nd rb group
cs="rb11";cis="rb11";j=-210;k=100;i=80;o=30;cls="b09s00";cm("i");
cs="rb12";cis="rb12";j=-90;k=100;i=80;o=30;cls="b09s00";cm("i");
cs="cb00";cis="cb00";j=-330;k=20;i=80;o=30;cls="b09s00";cm("i"); // 1st cb group
cs="cb01";cis="cb01";j=-210;k=20;i=80;o=30;cls="b09s00";cm("i");
cs="cb02";cis="cb02";j=-90;k=20;i=80;o=30;cls="b09s00";cm("i");
cs="cb10";cis="cb10";j=-330;k=-40;i=80;o=30;cls="b09s00";cm("i"); // 2nd cb group
cs="cb11";cis="cb11";j=-210;k=-40;i=80;o=30;cls="b09s00";cm("i");
cs="cb12";cis="cb12";j=-90;k=-40;i=80;o=30;cls="b09s00";cm("i");
cs="ls0";ib=false;j=120;k=50;i=120;o=230;cls="S9p7";fns="trb16";// Single
CIS=new string[] {"Item 0","Item 1","Item 2"};cm("i"); // Selection List
cs="ls1";ib=true;j=290;k=50;i=120;o=230;cls="S9y7";fns="trb16"; // Multiple
CIS=new string[] {"Item 0","Item 1","Item 2"};cm("i"); // Selection List
cs="pn0";cds="s";cls="s90s90";o=100;cm("i"); // Panel and 2 Text
cs="ta0";ps="pn0";i=380;cds="w";cls="S9s9";fns="crb12";cm("i"); // Areas installed
cs="ta1";ps="pn0";i=380;cds="e";cls="S9s9";fns="crb12";cm("i"); // into the panel.
}
public override void update() {
es=""+(char)13+(char)10; // New Line code to be used later.
if (cs.IndexOf("ls")<0) { // If event is for buttons (Not Lists)
ss=""; // Initialize display string
ss+="Activated Control: "+cs+es; // Add event source to display string
cs="rb0*";cm("gu"); // Get update for rb group 0 (should be cui)
ss+="FIRST RB GROUP: cui="+cui+es; // Add (cui) of rb group 0 to display string
cs="rb1*";cm("gu"); // Repeat for rb group 1
ss+="SECOND RB GROUP: cui="+cui+es;
cs="cb0*";cm("gu"); // Get update for cb group 0 (should be CUS[])
ss+="FIRST CB GROUP: CUS[ ]="; // Scan CUS[] and add all its 3 items to
for (int n=0;n<3;n++) ss+=" "+CUS[n]; // display string
ss=ss+es; // Add new line code to display string.
cs="cb1*";cm("gu"); // Repeat with cb group 1.
ss+="SECOND CB GROUP: CUS[ ]=";
for (int n=0;n<3;n++) ss+=" "+CUS[n];
ss=ss+es;
cus=ss;cs="ta0";cm("su"); // Display the display string on text area ta0.
}
else { // Else if event source is a List Box
ss=""; // Initialize display string
ss+="Activated Control: "+cs+es; // Add event source to display string.
cs="ls0";cm("gu"); // Get update for ls0 & add to display (should
ss+="FIRST LIST: cui="+cui+es; // be cui since single selection list)
cs="ls1";cm("gu"); // Get update for ls0 & add to display (should
ss+="SECOND LIST: CUS[ ]="; // be CUS[] since Multiple selection list)
for (int n=0;n<3;n++) ss+=" "+CUS[n];
// Add CUS[] contents to display string.
ss=ss+es; // Add new line code to display string.
cus=ss;cs="ta1";cm("su"); // Display the display string on text area ta1.
}
}
}
=========================================================================================
HOW TO WRITE, COMPILE AND RUN THE PROGRAM? See Example 1 in the General Examples list.
=========================================================================================
TUTORIAL:
=========
The setup() method should be easy to follow. There are only few new things which need
explanation.
(1) GROUP NAMING: Radio buttons and Check Box groups are installed individually and their
events can be handled also individually, except that installing them as groups makes
programming easier, more convenient and less subject to errors. We can have upto 9 groups
in one program (numbered 0:8). Each group can be made of upto 10 buttons (numbered 0:9)
So, "rb36" is the 7th Radio Button of the 4th group and "cb00" is the first Check Box of
the first group. These group naming rules must be observed when you select keynames for
Radio Button groups, Check Box groups and (as you'll see later) Menu's.
Group number 10 is reserved for PC# internal use. In general, keynames must be in the range
"xx0" to "xx89". They can be 3 or 4 char's long except for grouped items where keynames must
be 4 char's long.
(2) You may have noticed that the color codes for all buttons are made of 6 char's. This
was because we wanted their background color to match the form's color and the easiest
way to accomplish this is making their background color transparent (opacity digit=0)
So we used the 3-char code for both background and foreground. Foreground colors should
be fully opaque (opacity digit=9)
(3) "List Box" controls are multi-item controls, so their installation should be very
similar to the installation of the "Combo Box" of the previous Example. One small
difference is that they can be made to accept either single selection like Combo Boxes
and Radio Button groups or multiple selections like Check Box groups. We use the boolean
variable (ib) to determine of which kind they are.
(4) The text areas have not been installed directly into the form. They have been
installed into a panel and the panel has been installed into the form. You may have noticed
that we have not supplied full location or size data for either Text Area or for the
Panel, we have used layout technique to guarantee keeping them at the bottom of the form.
This will be discussed in details in the next Example.
(5) RECEIVED EVENTS: As you already know, when the user clicks on a control or an item
of a control, method update() is called and supplied with the keyname (cs) of the
control which has generated the event.
Normally, we use (cs) to get the control's latest update by calling cm("gu"). We have done
that with the lists and we coud have done that with the buttons individually, except that
we have a better choice for the Radio Buttons and Check Box groups, we can get their group
update at once by using Wild Card names. For example we could get the combined update for
Radio Button group number 1 with:
cs="rb1*";cm("gu");
(6) Since Radio Button groups and (ls0) are single selection items, their update item is the
index of their selected item in (cui). For Check Box groups and (ls1), their update is
array CUS[] with each row containing either "1" or "0" indicating that the item of the
same order as the row is "selected" or "not selected" respectively.
There is nothing else which needs to be explained, you must be able to read the program
code and fully understand it.
=========================================================================================

EXAMPLE 4: Let us now discuss some layout considerations. We will use the same form which
has been generated in Example 3 for the discussion.
TUTORIAL:
The layout tools available are:
==============================
a) SCROLLABLE PANELS: You can make a control or a group of controls occupy less space in
the form by installing a scrllable panel of the necessary size then installing the
control(s) into the panel.
b) SCROLLABLE FORM: You can make the entire form scrollable, so it can handle its contents
regardless to its size.
c) MARGINS: When the scrollable form is reduced in size, some of the controls it contains
may end with their borders touching the form's borders. You can avoid that by specifying
a horizontal and vertical margins.
d) ANCHORE: We can allow C#'s layout manager to move each control slightly in one or more
directions whenever it finds that necessary.
e) DOCKING: We can force a control to be placed at any of the forms 4 sides and keeps
its place when the form is resized.
How to do it:
=============
a) To place controls on panels, see the setup() method of Example 3, where the Panel pn0
was created and the two Text Areas ta0,ta1 have been installed into it. To make the
panel scrollable, just add (ib=true;) to pn0's parameters.
b) To make the Form scrollable add at the top of method setup: ib=true;cm("fa");
c) SETTING MARGINS: To clear 20 pixels at both left and right borders and 30 pixels at
both top and bottom borders of the form, add this statement also at the top of
method setup: j=20;k=30;cm("fm");
d) SETTING THE ANCHOR: To set the Anchor for any control to "South East", include this
with the rest of its parameters in method setup: cas="se";
d) SETTING THE DOCKING: To set the Docking for any control to "East", include this
with the rest of its parameters in method setup: cds="e";
If you like to dock to more than one direction, we suggest installing the control(s)
into panels. This technique has been used in Example 3, when the Panel was docked
to the south while the 2 Text Areas docked to east and west.
Now try:
=======
(1) Modify Example 3 program by adding at the beginning of method setup:
j=600;k=350;ib=true;cm("fs"); // Resize Form's ClientSize to (600,350)
Notice that some areas at the right side and at the bottom of the form have been
clipped off.
(2) Now add (ib=true;) to pn0's parameters.
Notice the scroll bar which now appears at the bottom of the panel. It allows us to see
both Text Areas in full, while changing nothing to all other controls in the form.
(3) Now add this second statement at the top:
ib=true;cm("fa"); // Set AutoScroll property to true.
Notice that the form has 2 scroll bars now which enables us to see all controls. Look at
the right side, you will see that "ls1" ends exactly at the right border of the form.
(4) Now add this third statement:
j=20;k=30;cm("fam"); // Set horiz & vertical margins of 20 & 30 pixels
Notice that "ls1" is not touching form's border anymore.
=========================================================================================

EXAMPLE 5: It is now the time for the new menu's. We are going to create 3 menu groups.
The first one will be of type "MainMenu" and will be installed at the top of the form.
The Second one will be of type "ContextMenu" and will be installed at a specific location
within the form. The third one will be also of type "ContextMenu" and will be attached to
a button. Whenever you Right-Click the button, the menu appears.
public class a : pcs {
public override void init() {
bli=1;
base.init();
}
public override void setup() {
cs="mn03";cis="Display a Picture"; // Main menu. Start with creating sub-menu
CIS=new string[] {"Draw as is at Form's Center","Make it fill the Form"};
cm("i"); // which contains items only (leaf menu)
cs="mn02";cis="Display a Shape"; // Then create menu's which contain items
CIS=new string[] {"Square","Circle","Hexagon","Text"};
cm("i"); // and "already created" sub-menu's if any
cs="mn01";cis="Display a Dialog"; // Same rule applies.
CIS=new string[] {"Open File Dialog","Save File Dialog","Font Dialog",
"Color Dialog"};
cm("i");
cs="mn00";cis="main Menu";ib=true; // Finally create the root menu. (ib=true)
CIS=new string[] {"mn01","mn02","mn03","Change Form's color"};
cm("i"); // indicates "Main Menu" type.
cs="mn10";cis="Context-1";ib=false; // Create context menu group "mn1". Only
CIS=new string[] {"Item 0","Item 1","Item 2","Item 3"};
cm("i"); // root. It has no sub-menu's.
cs="mn20";cis="Context-2";ib=false; // Create context menu group "mn2". Also
CIS=new string[] {"Red","Green","Blue"};
cm("i"); // made of one root menu only.
cs="lb0";cis="My Menu Sets My Color";j=00;k=100;i=200;o=50;cls="S9y0";
cms="mn20";cm("i"); // Create label and attach "mn20" to it
}
public override void update() {
// Var's Used: g=menu group number m=sub-menu number n=Item number
// Example: If cs="mi031": g=0, m=3, n=1
if (cs.IndexOf("mi")!=0) return; // Neglect all but Menu item events.
os=cs.Substring(2,1); // Get Group number from (cs)
om("ti");g=o; // Convert it to type (int)
os=cs.Substring(3,1); // Get Menu number from (cs)
om("ti");m=o; // Convert it to type (int)
os=cs.Substring(4,1); // Get Item number from (cs)
om("ti");n=o; // Convert it to type (int)
cs=""; // Erase (cs)
cls="s9";gm("sps");lf=460;of=30;gm("crf"); // Cover previous mesg with white paint
cls="S9";gm("sps"); // Return drawing pen's color to black
fns="trb16";os="Received events concerning: menu group "+g+", menu "+m+",item "+n;
gm("ctf"); // Display msg describing selection.
bli=g+2;um("b");return; // Jump to where selection is executed.
}
public override void run() {
// -------------------------------------- Startup ---------------------------------------
if (blp==1) {
cms="mn00";cm("fmm"); // Install main menu into form
cls="s9";gm("sps");lf=400;of=30;gm("crf");// Draw white rect at center to write on
}
// ------------------- Execution of Main Menu Group (mn0) Selections ---------------------
else if (blp==2) { // menu group 0
if (m==3 && n==0) { // If "Draw pix at center" selected
fls="images\\pix.jpg";gm("cid"); // call gm() at mode "Create Image & draw"
}
else if (m==3 && n==1) { // If "Fill Form with pix" requested:
lf=this.Width;of=this.Height; // Create bitmap object based on the image file
fls="images\\pix.jpg";gm("blf"); // with size equal to Form's.
gm("br"); // Render Bitmap.
}
else if (m==2 && n==0) { // If "Draw Square" selected
lf=of=300;gm("crd"); // call gm(), md "create rect & draw"
}
else if (m==2 && n==1) { // If "Draw Circle" selected
lf=of=300;gm("ced"); // call gm(), md "create ellipse & draw"
}
else if (m==2 && n==2) { // If "Draw Hexagon" selected
lf=6;of=300;gm("c=d"); // Use "Create Equal sided Shape & draw"
}
else if (m==2 && n==3) { // If "Draw Text" selected
fns="trb64";os="Hello World!";gm("ctf");// Use mode "Create text & fill"
}
else if (m==1 && n==0) { // If "Display Open File Dialog" selected
cm("dfo");os="You have selected the file: "+os;cm("d");
} // Call cm(), mode "Dialogs-file-open"
// Then display file selected.
else if (m==1 && n==1) { // If "Display Save File Dialog" selected
cm("dfs");os="You have selected the file: "+os;cm("d");
} // Call cm(), mode "Dialogs-file-save"
else if (m==1 && n==2) { // If "Display Font Dialog" selected
cm("dtf");os="You have selected the font: "+fnp.Name;cm("d");
} // Call cm(), mode "Dialogs-text-font"
// Then display name of selected font
else if (m==1 && n==3) { // If "Display Color Dialog" selected
cm("dtc");os="You have selected the color: "+clp.Name;cm("d");
} // Call cm(), mode "Dialogs-text-color"
// Then display HEX code of selected color
else if (m==1 && n==4) { // If "Display Print Dialog" selected
cm("dp"); // Display the dialog
}
else if (m==0 && n==3) { // If "Change form's color" selected
cls="b5";gm("ec"); // Color background light blue.
}
}
// -------------- Execution of Form's Context Menu Group (mn1) Selections ---------------
else if (blp==3) { // It does not include items to execute
}
// --------------- Execution of lb0's Context Menu Group (mn2) Selections ---------------
else if (blp==4) {
if (n==0) cls="S9r0"; // If item 0, select red backgrnd color
else if (n==1) cls="S9g0"; // If item 1, select green backgrnd color
else if (n==2) cls="S9b0"; // If item 2, select blue backgrnd color
cs="lb0";cm("sC"); // Then Call method cm() to set lb0 color
}
// --------------------- Things to do at the end of each Selection -----------------------
cms="mn10";j=-40;k=-80;cm("fmc"); // After executing every selection,
} // reinstall Context Menu
}
=========================================================================================
HOW TO WRITE, COMPILE AND RUN THE PROGRAM? See Example 1 in the General Examples list.
=========================================================================================
TUTORIAL:
(1) MENU NAMING: Naming menu groups is similar to naming Radio Button and Check Box groups.
We are allowed upto 9 menu groups (groups 0:8 ) and each group can contain upto 10
sub-menu's (menu's 0:9) Menu group 9 is reserved for PC# internal use.
The menu group is a menu tree in which the root menu contains a combination of menu items
and sub-menu's. Each sub-menu also may contain items and other sub-menu's. For example
menu group number 3 may be made of a root menu "mn30", which may contain sub-menu "mn31"
and sub-menu "mn31" may contain sub-menu "mn32" and sub-menu "mn33" and so on. Number of
all menu's in the tree must not exceed 10 and their names should be "mn30":"mn39".
(2) MENU SETUP: Menu setup is made to be as simple as possible. It is made to be similar to
the setup of any other multi-item control with the following few additional rules:
a) If a menu contains a sub-menu among its items, the text for the item representing the
sub-menu will be the sub-menu's key name. For example "mn31" may contain two items
"Apples" and "mn32".
b) You can't include a sub-menu as an item of another menu unless that sub-menu has
been created in advance. So, concerning the example mentioned in (a), creation of
sub-menu "mn32" must preceed the creation of "mn31".
c) Unlike all other controls, there are only 3 setup items for menu's which are their names
assigned to (cis), their items assigned to CIS[] and the boolean value (ib) which will be described next.
d) There are two types of menu's, "Main" and "Context". Your program can include only
one main menu which is installed at the top of the form. However, your program can include
several context menu's which you can make appear at any location into the form or attach
to any control. Cotext menu's which are attached to controls, appears when the control is
right clicked. Context menu's disappear once selection is made while the main menu stays
at it's place all the time. To indicate that a menu is intended to be the main menu include
(ib=true) among the setup parameters of its root.
(3) RECEIVING EVENTS FROM MENU'S: The only events which interest us are the ones generated
when the user clicks on an item. As with all other controls, each menu item has its own
unique key name (cs) Immediately after the item is clicked on, method update is called and
supplied with (cs) for the item.
Key names for menu items are in the form "migmt". "mi" means "menu item", "g" is a digit
(0:8) representing the menu group the item belongs to, "m" is a digit (0:9) representing
the menu it belongs to and "t" is the order number of the item within its menu.
For example when item number 3 of menu "mn01" is clicked on, method update will be called
and supplied with (cs=mi013)
(4) Now look at method setup() of the example, every thing should be easy to understand.
Notice that "mn00" included (ib=true) since it is the root of the menu group which we
like to use as "Main menu". Also, notice that in the setup of "lb0", we included
(mns=mn20) which attaches context menu "mn20" to that control.
(5) Method update() seperates group number, menu number and item number from the key name
received, displays them at the center of the form graphically. This is the first time
this type of text display has been done. Notice how making measurments relative to form's
center has simplified things. We did not need to state where text should be displayed,
because if we did, it should have been (j=0;k=0;) which is the default, thanks to the
GUV's rules.
(6) Before displaying the new message, we had to erase old ones, so we drew and filled a
white rectangle at the center to cover up old messages. Again see how easy it is to make
two different shapes, a string and a rectangle line-up perfectly without having to wory
about misalignment whenever the form is resized.
(7) Method run() is divided into blocks as normal, the first block is the start-up block
where main menu is installed into form. Each of the other blocks executes the selections
received from items of each menu group. At the end of method run(), the form's context
menu is reinstalled. This is necessary since context menu's last only until the user
pushes any key then they disappear.
=========================================================================================

EXAMPLE 6: Mostly, every thing necessary concerning "Controls" has been covered in the
preceeding examples. The only two items which I believe have been missed are the
installation of "Tooltips" and "Background Images". Tooltips are the little yellow text
strips which appear when you place mouse cursor on a control for few seconds without
clicking. We are going to create a button with background image and a tooltip attached.
There will be no tutorial following this example.
public class a : pcs {
public override void init() {
base.init();
}
public override void setup() {
cs="bt0";cis="Look at my Tooltip";i=250;o=100;fns="trb20";
ims="images\\pix.jpg";cts="WARNING: Do not click this button!";
cm("i"); // Create button at center using the image in file "pix.jpg"
} // as its background image and attach a tooltip to it.
public override void update() {
if (cs.Equals("bt0")) { // If "bt0" clicked
ib=true;cm("fv"); // Make form invisible.
}
}
}
=========================================================================================
HOW TO WRITE, COMPILE AND RUN THE PROGRAM? See Example 1 in the General Examples list.
=========================================================================================

========================================================================================
HANDLING MULTI-FORMS
====================
(This section requires version 1.52 or higher)
So far, in all the examples we have seen, we have had one set of controls which we install
on the form at startup and never change. This time we are going to see how we can
write a class in which there are more than one set of controls which are to be mounted on
the form alternatively.
Here is what you need to do in order to handle multi-forms:
(1) Set the multi-form flag:
----------------------------
This is done in method init(). Make the assignment (ib=true) then Call dm("fm").
What this does is changing the way controls are mounted into the form. By default,
controls are mounted directly on the form's surface. When we set the multi-form flag,
a new panel (pno) is created and made to be always equal in size to the form. The
controls are mounted on this panel then the panel is mounted on the form.
The use of (pno) simplifies the switching from one set of controls to another. The
switching is done by disposing the old (pno) and creating a new one for the new set.
(2) Divide methods "setup()" and "update()" into blocks:
--------------------------------------------------------
Your program needs to be divided into blocks. You know what this means, except that this
division used to apply to method run() only. Now you need to divide methods setup() and
update() to match method run()'s division. This means that block 1 of method setup()
should contain the controls which are required for block 1 of method run(). Also block 1
of method update() should handle events for those controls.
(3) Call method cm("in") to prepare for a new installation before switching controls:
-------------------------------------------------------------------------------------
All you need to do in order to load a new set of controls, is to call setup() from the
block of method run() which matchs the block where the wanted controls are. However,
before you do that you must call cm("in) which disposes present set of controls and cleans
up archives in order to prepare for the new control installations.
Remember the definition of PC#'s Block Jump:
--------------------------------------------
The block jump simplifies the handling of multi-forms, so we are going to be using this
feature in the coming examples. Now method run() is not the only method which is divided
into blocks, methods setup() and update() are also divided so we need to clarify a point.
You need to know that the destination of the jump is always in method run() regardless to
which method has initiated the jump. This means that if you execute a jump to block 2 from
block 1 of method update(), the destination is going to be block 2 of method run(). This is
how this jump is defined.
CHAINED FORMS:
==============
We use this type when we like forms to follow each other in a specific order. Each form
should contain a "Next" button which takes you to the next form when clicked. A form may
also contain a "Back" button alone or in addition to the "Next" button so you can return
back to the previous form.
========================================================================================
Example 7: In this example, We are going to chain the two forms used in examples 1 and 2.
We'll add a "Next" button to the first form and a "Back" button to the second one. The
execution will start by example 1 and the user will be able to switch forms as many times
as he/she wants by clicking the two buttons.
========================================================================================
public class a : pcs {
public override void init() {
bli=1; // Start by executing the form of example 1.
ib=true;dm("fm"); // Set the multi-form flag.
base.init();
}
public override void setup() {
//----------------------------------- Example 1 -------------------------------------
if(blp==1) {
j=300;k=350;cm("fs"); // Resize Form to (300,350) Pixels
//---------------------- Setup data for example 1, No Change ----------------------
cs="tf0";k=50;i=200;o=20;cm("i");
cs="bt0";cis="My Color Changes";k=-50;i=140;o=40;cls="S9y0";cm("i");
//---------------------------------------------------------------------------------
cs="bt1";cis="Next";k=-125;i=140;o=30;cls="r0g7";cm("i");// "Next" Button
}
//----------------------------------- Example 2 -------------------------------------
if(blp==2) {
j=400;k=375;cm("fs"); // Resize Form to (400,350) Pixels
//---------------------- Setup data for example 2, No Change ----------------------
cs="bt0";cis="Add";j=120;k=100;i=90;o=30;cls="r0y0";fns="trbsui16";id=0;cm("i");
cs="bt1";cis="Select";j=120;k=50;i=90;o=30;cls="r0g7";fns="trbi16";id=2;cm("i");
cs="bt2";cis="Which Item is Selected?";i=340;o=30;cls="b0r7";fns="trb16";id=3;cm("i");
cs="lb0";cis="Test ComboBox >";j=-110;k=-90;i=120;o=30;os="e";cls="r0s7";
fns="tri11";id=-1;cm("i");
cs="tf0";cus="Banana";j=-60;k=100;i=225;o=30;cls="b0p7";fns="trb16";id=4;cm("i");
cs="tf1";cis="";j=-60;k=50;i=225;o=30;cls="S9b7";fns="tr16";id=5;cm("i");
cs="ch0";cus="Select a fruit";j=80;k=-90;i=190;o=40;cls="b0m6";fns="trb16";
CIS=new string[] {"Apples","Oranges","Peaches","Pears"};id=6;cm("i");
//---------------------------------------------------------------------------------
cs="bt3";cis="Back";k=-155;i=140;o=30;cls="r0g7";cm("i");// "Back" Button
}
}
public override void update() {
//----------------------------------- Example 1 -------------------------------------
if(blp==1) {
//---------------------- Update code for example 1, No Change ---------------------
if ("bt0".Equals(cs)) {
cs="tf0";cm("gu");
os=cus.Substring(0,1);om("u");
n="RGB".IndexOf(os);
if (n<0) cls="S9y0";
else if (n==0) cls="S9r0";
else if (n==1) cls="S9g0";
else if (n==2) cls="S9b0";
cs="bt0";cm("sC");
cs="tf0";cus="";cm("su");
cs="tf0";cm("sx");
}
//---------------------------------------------------------------------------------
if ("bt1".Equals(cs)) { // If "Next" Button Clicked:
bli=2;um("b");return; // Jump to block 2 (of method run())
}
}
//----------------------------------- Example 2 -------------------------------------
if (blp==2) {
//---------------------- Setup data for example 2, No Change ----------------------
if ("bt0".Equals(cs)) {
cs="tf0";cm("gu");
CIS[0]=cus;cs="ch0";cm("sL");
}
if ("bt1".Equals(cs)) {
cs="tf1";cm("gu");
string text=cus;
cs="ch0";cm("O");
int index = chp.FindString(text);
cus="";cui=index;cs="ch0";cm("su");
}
if ("bt2".Equals(cs)) {
cs="ch0";cm("gu");string text=cus;
os="Selected Item's Text: " + text + "\n" + "Index: " + cui;cm("d");
}
//---------------------------------------------------------------------------------
if ("bt3".Equals(cs)) { // If "Back" Button Clicked:
bli=1;um("b");return; // Jump to block 1 (of method run())
}
}
}
public override void run() {
//----------------------------------- Example 1 -------------------------------------
if(blp==1) {
cm("in");setup(); // Initialize for new form, execute Ex 1 setup
}
//----------------------------------- Example 2 -------------------------------------
if(blp==2) {
cm("in");setup(); // Initialize for new form, execute Ex 2 setup
}
}
}
========================================================================================

SELECTABLE FORMS:
=================
We use this type when we like to allow the user to select the form. The "Next" and "Back"
buttons are replaced with a "Return" button for this type. When the user clicks the
"Return" button, he gets back the main form where he makes his choice.
========================================================================================
Example 8: This example starts with a Text Screen where the user is allowed to select
either one of the same two forms. Each of the two forms contains a "Return" button. When
the "Return" button is clicked, execution returns back to the Text Screen menu.
========================================================================================
public class a : pcs {
public override void init() {
bli=0; // Start execution at the Text Screen form
ib=true;dm("fm"); // Set the multi-form flag
base.init();
}
public override void setup() {
//----------------------------------- Example 1 -------------------------------------
if(blp==1) {
j=300;k=375;cm("fs"); // Resize Form to (300,375) Pixels
//---------------------- Setup data for example 1, No Change ----------------------
cs="tf0";k=50;i=200;o=20;cm("i");
cs="bt0";cis="My Color Changes";k=-50;i=140;o=40;cls="S9y0";cm("i");
//---------------------------------------------------------------------------------
cs="bt1";cis="Return";k=-125;i=140;o=30;cls="r0g7";cm("i");// "Return" Button
}
//----------------------------------- Example 2 -------------------------------------
if(blp==2) {
j=400;k=375;cm("fs"); // Resize Form to (400,375) Pixels
//---------------------- Setup data for example 2, No Change ----------------------
cs="bt0";cis="Add";j=120;k=100;i=90;o=30;cls="r0y0";fns="trbsui16";id=0;cm("i");
cs="bt1";cis="Select";j=120;k=50;i=90;o=30;cls="r0g7";fns="trbi16";id=2;cm("i");
cs="bt2";cis="Which Item is Selected?";i=340;o=30;cls="b0r7";fns="trb16";id=3;cm("i");
cs="lb0";cis="Test ComboBox >";j=-110;k=-90;i=120;o=30;os="e";cls="r0s7";
fns="tri11";id=-1;cm("i");
cs="tf0";cus="Banana";j=-60;k=100;i=225;o=30;cls="b0p7";fns="trb16";id=4;cm("i");
cs="tf1";cis="";j=-60;k=50;i=225;o=30;cls="S9b7";fns="tr16";id=5;cm("i");
cs="ch0";cus="Select a fruit";j=80;k=-90;i=190;o=40;cls="b0m6";fns="trb16";
CIS=new string[] {"Apples","Oranges","Peaches","Pears"};id=6;cm("i");
//---------------------------------------------------------------------------------
cs="bt3";cis="Return";k=-135;i=140;o=30;cls="r0g7";cm("i");// "Return" Button
}
}
public override void update() {
//----------------------------------- Example 1 -------------------------------------
if(blp==1) {
//---------------------- Update code for example 1, No Change ---------------------
if ("bt0".Equals(cs)) {
cs="tf0";cm("gu");
os=cus.Substring(0,1);om("u");
n="RGB".IndexOf(os);
if (n<0) cls="S9y0";
else if (n==0) cls="S9r0";
else if (n==1) cls="S9g0";
else if (n==2) cls="S9b0";
cs="bt0";cm("sC");
cs="tf0";cus="";cm("su");
cs="tf0";cm("sx");
}
//---------------------------------------------------------------------------------
if ("bt1".Equals(cs)) { // If "Return" button Clicked:
bli=0;um("b");return; // Jump to block 0 (of method run())
}
}
//----------------------------------- Example 2 -------------------------------------
if (blp==2) {
//---------------------- Setup data for example 2, No Change ----------------------
if ("bt0".Equals(cs)) {
cs="tf0";cm("gu");
CIS[0]=cus;cs="ch0";cm("sL");
}
if ("bt1".Equals(cs)) {
cs="tf1";cm("gu");
string text=cus;
cs="ch0";cm("O");
int index = chp.FindString(text);
cus="";cui=index;cs="ch0";cm("su");
}
if ("bt2".Equals(cs)) {
cs="ch0";cm("gu");string text=cus;
os="Selected Item's Text: " + text + "\n" + "Index: " + cui;cm("d");
}
//---------------------------------------------------------------------------------
if ("bt3".Equals(cs)) { // If "Return" Button Clicked:
bli=0;um("b");return; // Jump to block 0 (of method run())
}
}
}
public override void run() {
//-------------------------------- Branching form -----------------------------------
if(blp==0) { // Text Screen Form
cm("fsd");
cm("in");cm("it"); // Initialize for new form, install Text Screen
tia=toa="t"; // Make Text Screen the text in & out device
cls="r0";fns="trb14"; // Form Selection menu
os=" Select A Form";tm();
os="";tm();cls="S9";fns="trb10";
os=" (1) First Form.";tm();
os=" (2) Second Form.";tm();
os="";tm();cls="b0";
os="Selection :";bli=3;tm("i"); // Get user selection and jump to block 3.
return;
}
//----------------------------------- Example 1 -------------------------------------
if(blp==1) {
tia=toa="s"; // Change text in & out to sys screen (Console)
cm("in");setup(); // Initialize for new form, execute Ex 1 setup
}
//----------------------------------- Example 2 -------------------------------------
if(blp==2) {
tia=toa="s"; // Change text in & out to sys screen (Console)
cm("in");setup(); // Initialize for new form, execute Ex 2 setup
}
//--------------------------- Executing Menu Selections -----------------------------
if (blp==3) {
// Var's used: f=index of item selected.
f="12".IndexOf(os); // Get index of item selected.
if (f<0){ // If unexpected char entered
bli=0;um("b");return; // return to block 0.
}
else if (f==0) {bli=1;um("b");return;} // Jump to block 1 if 1st item selected
else if (f==1) {bli=2;um("b");return;} // Jump to block 2 if 2nd item selected
bli=0;um("b");return; // Then return back to block 0
}
}
}
========================================================================================
A BETTER WAY TO HANDLE MULTIPLE FORMS:
======================================
The preceeding two examples have shown one way to handle multiple forms. This way is simple,
it requires one class only, but its drawback is that it is not modular. The modular design
is always the best. You like to be able to run Example1 and example2 classes alone whenever
you need so. In the meantime you like to run them through other classes as part of a chain
or selectable objects.
We are going to rewrite Example 8 in a manner which can achieve this goal.
We are going to keep the classes of example 1 and example 2 as is. The only exception is
that we'll add the missing form resizing to example 1's class since it was not done. So
add this line at the top of method setup() of Example 1 class:
j=300;k=200;cm("fs"); // Resize Form to (300,200) Pixels
For Example 2, the Form resizing call should be kept at:
j=400;k=300;cm("fs"); // Resize Form to (400,300) Pixels
We also need to compile both classes under different names, so do the following:
(1) Change the class declaration statement for the two classes into:
public class Example1 : pcs
public class Example2 : pcs
(2) Save Example1 class into a file named "Example1.cs" and save Example2 class into a file
named "Example2.cs"
(3) Compile (and check the operation of) the two classes with:
pcpr Example1 [ENTER] and pcpr Example2 [ENTER]
Make sure both classes run correctly as independant programs before proceeding next.
Writing the branching class:
----------------------------
The branching class starts with creating an instance of each of the two classes and as you
may know, this requires a commented line to be placed above the class declaration statement.
The line starts with the word "//assembly" followed with a space then with the path names of
the files where each of the instantiated classes are defined, seperated with commas.
When you executed either of the two classes individually, you did not have to do anything
to get all the controls to appear into the form. That was because PC# automatically calls
method setup() at startup which installs all the controls into the form.
Probably, this means to you that we should call e1.setup() where (e1) is the reference of
Example1's instance to get the controls of Example1 to show up. There is still one problem.
Whatever shows up does not show up on our form. It shows up on Example1 instance's form.
The solution is in using the panel (pno) which we have discussed before. We'll get all
controls of Example1's instance to be mounted on (pno) then we'll mount (e1.pno) on our
form. In order to achieve this, we must turn the "multi-form" flag of Example1's instance
on. This is done by:
e1.ib=true;e1.dm("fm");
Now we can call Example1's setup method by [e1.setup();], then mount its (pno) on the
branching class's form by:
pno.Controls.Add(e1.pno);
The last thing you need to know, is that Example1's form does not include the "Return"
button. We are going to be adding that button into our form after we load (pno). This is
why we need to recize our form to a higher size than Example1 form's size. The button
will be added below (e1.pno).
The events for all controls which have been installed within (e1) are handled by method
e1.update() So we, don't get involved in their handling. This does not apply to the
"Return" button which has been installed within the branching class and its events must
be handled within it.
Now let us see the actual code of the branching class:
========================================================================================
//assembly Example1.exe,Example2.exe
// Names of files where refrenced objects are located
public class a : pcs {
Example1 e1=new Example1(); // Creating an instance of Example1's class
Example2 e2=new Example2(); // Creating an instance of Example2's class
public override void init() {
bli=0; // Start execution at the Text Screen form
ib=true;dm("fm"); // Set the multi-form flag
base.init();
}
public override void update() {
//----------------------------------- Example 1 -------------------------------------
if(blp==1) {
if ("bt1".Equals(cs)) { // If "Return" button Clicked:
bli=0;um("b");return; // Jump to block 0 (of method run())
}
}
//----------------------------------- Example 2 -------------------------------------
if (blp==2) {
if ("bt3".Equals(cs)) { // If "Return" Button Clicked:
bli=0;um("b");return; // Jump to block 0 (of method run())
}
}
}
public override void run() {
//-------------------------------- Branching form -----------------------------------
if(blp==0) { // Text Screen Form
e1.ib=true;e1.dm("fm"); // Turn multi-form flag on for (e1)
e2.ib=true;e2.dm("fm"); // Repeat for (e2)
cm("fsd"); // Resize Form to PC# default.
cm("in");cm("it"); // Initialize for new form, install Text Screen
tia=toa="t"; // Make Text Screen the text in & out device
cls="r0";fns="trb14"; // Form Selection menu
os=" Select A Form";tm();
os="";tm();cls="S9";fns="trb10";
os=" (1) First Form.";tm();
os=" (2) Second Form.";tm();
os="";tm();cls="b0";
os="Selection :";bli=3;tm("i"); // Get user selection and jump to block 3.
return;
}
//----------------------------------- Example 1 -------------------------------------
if(blp==1) {
tia=toa="s"; // Change text in & out to sys screen (Console)
cm("in");e1.cm("in"); // Initialize this form & e1's form
j=300;k=350;cm("fs"); // Resize form to a higher size than e1's.
e1.setup(); // Execute Example1's setup()
pno.Controls.Add(e1.pno); // Add (e1.pno) to this class's (pno)
cs="bt1";cis="Return";k=-125;i=140;o=30;cls="r0g7";cm("i");
// Install "Return" Button below (e1.pno)
}
//----------------------------------- Example 2 -------------------------------------
if(blp==2) {
tia=toa="s"; // Change text in & out to sys screen (Console)
cm("in");e2.cm("in"); // Initialize this form & e2's form
j=400;k=350;cm("fs"); // Resize form to a higher size than e2's.
e2.setup(); // Execute Example2's setup()
pno.Controls.Add(e2.pno); // Add (e2.pno) to this class's (pno)
cs="bt3";cis="Return";k=-125;i=140;o=30;cls="r0g7";cm("i");
// Install "Return" Button below (e2.pno)
}
//--------------------------- Executing Menu Selections -----------------------------
if (blp==3) {
// Var's used: f=index of item selected.
f="12".IndexOf(os); // Get index of item selected.
if (f<0){ // If unexpected char entered
bli=0;um("b");return; // return to block 0.
}
else if (f==0) {bli=1;um("b");return;} // Jump to block 1 if 1st item selected
else if (f==1) {bli=2;um("b");return;} // Jump to block 2 if 2nd item selected
bli=0;um("b");return; // Then return back to block 0
}
}
}

DIALOG BOXES
============
The ".NET" library contains several useful dialog boxes and PC# has simplified their
use cosiderably. Additionally, PC# has developed software which allows you to create
your own custom dialog classes with maximum ease and simplicity. Here is a list of
the dialog boxes available:
THE MESSAGE BOX:
================
It displays text and can contain different buttons. The button clicked by the user
determines what the user wants. You call this dialog with cm("d") and the following
parameters:
(1) os=Text to display. This is the only non-optional parameter.
(2) js=Dialog's title. Optional.
(3) oc=Dialog's icon. Optional. Can be 'x', '?', '!', 'i' or none.
(4) ks=Keys available. Optional. Can be one of the following:
"o" means provide OK key only. This is the default.
"oc" means provide OK and Cancel keys.
"ari" means provide Abort, Retry and Ignore keys.
"ync" means provide Yes, No and Cancel keys.
"yn" means provide Yes and No keys only.
"ri" means provide Retry and Ignore keys only.
Your program also receives an output from this dialog which indicates the key selected
by the user. It comes assigned to (os) and can be one of the following characters:
"o" means "OK", "c" means "Cancel", "a" means "Abort", "r" means "Retry",
"i" means "Ignore", "y" means "Yes" and "n" means "No" button has been selected.
FILE OPEN AND SAVE DIALOG BOXES:
================================
You call these dialog boxes with cm("dfo") and cm("dfs") respectively. When the user
clicks the "Open" button of the "Open file" dialog, or the "Save" button of the "Save
file" dialog, PC# assigns the selected file name to (os).
TEXT COLOR AND FONT SELECTION DIALOG BOXES:
===========================================
You call these dialog boxes with cm("dtc") and cm("dtf") respectively. When the user
clicks the "OK" button, PC# assigns the selected color or font object to (clp) or (fnp)
respectively.
PRINT DIALOG BOXES:
===================
The use of these dialog boxes will be explained in the "Printing" section.
CUSTOM DIALOG BOXES: (Requires version 1.52 or later)
====================
In addition to the dialog boxes which Microsoft supplies us with, we can create our own
dialog boxes which contain any number of controls and / or drawings. A custom dialog box
class is the same as any other class which you write with the following few additional
features:
(1) It receives a string which contains all the parameters the calling class likes to
pass to it. This is done very simply by calling cm("dcr") to receive the custom
dialog input string. The string comes assigned to (os).
(2) It outputs a string which contains all the data the user has supplied. This is also
done very simply by assigning the string to (os) then calling cm("dco").
(3) It calls sm("e") to exit after its job has been completed so that the dialog box
disappears and execution returns back at the calling class.
The calling class uses method cm("dcs") to launch the dialog box and send the input
string which it likes to pass to it. This mode requires 4 parameters:
(1) The input string assigned to (os).
(2) The dialog class name assigned to (ks).
(3) The path name of the directory where the dialog box class is located assigned to (js).
If it was the current directory, keep (js) unassigned since it is the default.
(4) The requested block number to continue at after the job has been done and the
dialog's output string has been received. The output string comes assigned to (os).
The Input and output strings:
-----------------------------
Since you are the one who writes both the dialog classes and the classes which use them,
you can set your own specs for those strings. They don't have to be simple text strings,
they may contain several pieces of data combined together in any form as you see necessary.
========================================================================================
Example 9: In this example we are going to create an "input" dialog box which is very much
like the "Text Screen". It contains a "Rich Text Box" to display instructions to the user
and a text field to receive user's response. We are also going to create a class which
calls the dialog box supplying the instructions then displays the user's output text
which it receives.
========================================================================================
The dialog class:
-----------------
public class input : pcs {
public override void init() {
base.init();
}
public override void setup() {
j=300;k=200;cm("fs"); // Resize Form to (300,200) Pixels
cs="rt0";k=50;i=290;o=120;jf=30;kf=10;cm("i");
cs="tf0";j=-40;k=-50;i=190;o=40;cls="b0y0";cm("i");
cs="bt0";cis="OK";j=100;k=-44;i=60;o=30;cls="S9g7";cm("i");
}
public override void update() {
if ("bt0".Equals(cs)) { // If OK button clicked:
cs="tf0";cm("gu"); // Get user's text in (cus)
os=cus;cm("dco"); // Output text to calling class
sm("e"); // Exit.
}
}
public override void run() {
cm("dcr"); // Receive input string
cs="rt0";cus="\n"+os;cm("su"); // Display it
cs="tf0";cm("sx"); // Set focus for text field
}
}
--------------------------------------------------------------------------------------
The class which uses the dialog box:
------------------------------------
public class a:pcs {
public override void init() {
toa="t"; // Use text screen for text output
bli=0; // Start at block 0.
base.init();
}
public override void run() {
if (blp==0) { // Startup block
bli=1;ks="input"; // continuation block, dialog class name
os="Enter your name:";cm("dcs"); // input string. Launch dialog, send data
os="Text Sent: Enter Your Name:";tm(); // Display text sent locally.
}
if (blp==1) { // Continuation block. os=dialog out. data
os="Text Received: "+os;tm(); // Display text received locally.
}
}
}
========================================================================================
Both classes need to be compiled into executable files, using:
pcp input [Enter] and pcp a [Enter]
Then you can test the operation by running "a.exe".
========================================================================================

Improving the Input dialog box:
===============================
In the next example, we'll add three new features to the input box which has been created
in example 9. The three new features are:
(1) Making it a general Text Box which can be set to be either an input box which
contains a Rich Text Box, a Text Field and an [OK] button or a display only box which
contains a Rich Text Box and an [OK] button only.
(2) Adding the abilities of displaying text in different colors and fonts, displaying
images and also displaying plain text or RTF file contents into the Rich Text Box.
(3) Automatic size adjustment of the dialog box based on the size of its contents.
Generalizing the dialog box:
----------------------------
The dialog box class will be renamed to "TextDialog". The executable file will be
named "TextDialog.exe". Whenever it is called without parameters, it will set itself to
be a display only box and Whenever it is called with the parameter "i" it will set itself
to be an input box.
Adding the new abilities:
-------------------------
We know that method tm() allows us to display text in different colors and fonts when the
text screen is used. The text screen display object is a Rich Text Box which is referenced
with (rta). The dialog box also contains a Rich Text Box object which we know that after
executing:
cs="rt0";cm("O");
it becomes the present Rich Text Box object and (rtp) becomes its reference. Now,
if we make the assignment (rta=rtp;), whatever is intended to be displayed on the text
screen will be displayed on the dialog box. So we can use all the modes included into
method tm() to display text, images and file contents on the dialog box. However, this
must be preceded with the assignment (toa="t";), so that method tm() knows that the text
screen is the text output device before all.
Note that when (toa="t";) is included into method init(), PC# creates the text screen
form. Here, we are going to make this assignment within method run() so PC# will not
have the chance to create that form and will assume that the dialog's display object is
the one.
What is left now is how the calling class (class a) will be able to inform the dialog
class (class TextDialog) with what it wants to do. As mentioned before, you can do that
any way you want, however this is how we are going to do it in the coming example:
We are going to include into the input string codes representing the statements which
the dialog class will be using to populate the box. The codes will be seperated with
semi-colons. Here is a list of the statements and the the codes representing them:
Operation Description Operation Statements Code Used
======================== ======================== ===========
Display a line of text os="The Text";tm(); l=The Text;
Display a string of text os="The Text";tm("d"); d=The Text;
Set Color cls="ColorCode"; c=ColorCode;
Set Font fns="FontCode"; f=FontCode;
Display an image ims="ImageFile";tm("dg"); g=ImageFile;
Display text file content fls="TextFile";tm("flt"); t=TextFile;
Display RTF file content fls="RtfFile";tm("flr"); r=RtfFile;
Set Rich Text Box Width w=Width;
Set Rich Text Box Height h=Height;
In order to simplify the "display line of text" operation, the "l=" is optional for that
operation. This allows us to simply use (os="text";) when we like to display the text
without specifying color or font. In other words, it makes this dialog box as simple to
use in this case as the previous one.
Automatic size adjustment:
--------------------------
Automatic size adjustment of the dialog box is done after the text is displayed into it.
At the start, the dialog box is made to occupy the full screen. Then we readjust its size
to make it large enough to take whatever is in it and no more. We use the "PreferredSize"
property supplied by the ".NET" for this purpose.
Sometimes, we don't get the exact size which we want. So, we made it possible to specify
the width and height wanted for the Rich Text Box as you can see in the table above.
There is also a minimum allowed size for the dialog box which guarantees its ability to
house the text field and button and also a maximum allowed size which guarantees that it
does not exceed full screen size.
========================================================================================
Example 10: Display Personal C Sharp icon followed with the same menu which has been used
in example 4 of the General section into a dialog box then display the user's selection
received into a text screen.
========================================================================================
The class which uses the dialog box:
------------------------------------
public class a:pcs {
public override void init() {
toa="t"; // Use text screen for text output
bli=0; // Start at block 0.
base.init();
}
public override void run() {
if (blp==0) { // Startup block
bli=1;ks="TextDialog i"; // continuation block, dialog file name, par
os="g=images\\icon.bmp;l=;"; // Make the input string, add the icon
os+="c=r0;f=trb14;"; // Add line feed then color and font codes
os+="l= MENU;l=;"; // Add the rest of the menu codes as
os+="c=S9;f=trb10;"; // described above.
os+="l= (R) Red.;";
os+="l= (B) Blue.;";
os+="l= (G) Green.;";
os+="l= (E) Exit.;l=;";
os+="c=b0;l=Selection :;";
cm("dcs"); // Launch dialog, send data
}
if (blp==1) { // Continuation block. os=dialog out. data
os="Text Received: "+os;tm(); // Display text received locally.
}
}
}
The dialog class:
-----------------
public class TextDialog : pcs {
char dlc;int width,height,wmax,hmax;
public override void init() {
base.init();
}
public override void setup() {
// Variables used: wmax,hmax = Full screen width and height.
// width,height = rt0 width, height in pixels as suggested by calling program
// dlc = Desired Dialog box type. dlc=t means Text Box, dlc=i means Input Box
dlc='t'; // Text display only is the default dialog type
dm("pc"); // Get parameter count
if (o>1) { // If there are parameters:
i=1;dm("p"); // Get first one.
if(os.IndexOf("i")==0) dlc='i';// if 1st par starts with "i", dialog typ=input
}
ib=true;cm("fv"); // Make form temporarely invisible
cm("fsf"); // Resize Form to full screen
cm("fws");wmax=(int)of; // Assign full screen width to wmax
cm("fhs");hmax=(int)of; // Assign full screen height to hmax
cm("ir"); // Reset all par's usable by cm("i")
cs="rt0";j=0;k=30;i=wmax;o=hmax-60;
// rt0 fills the screen leaving only a 60 pixels strip
cm("i"); // at bottom where tf0, bt0 are installed
if(dlc=='i') { // If input dialog: Install text field & button
cs="tf0";j=-40;k=40-hmax/2;i=190;o=40;cls="b0y0";cm("i");
cs="bt0";cis="OK";j=100;k=46-hmax/2;i=60;o=30;cls="S9g7";cm("i");
}
else { // If display only: Install button only
cs="bt0";cis="OK";j=0;k=46-hmax/2;i=60;o=30;cls="S9g7";cm("i");
}
}
public override void update() { // This method is called whenever an event takes place
if ("bt0".Equals(cs)) { // If OK button clicked:
if (dlc=='i') { // Next two lines apply to input dialog only:
cs="tf0";cm("gu"); // Get user's text in (cus)
os=cus;cm("dco"); // Output text to calling class
}
sm("e"); // Exit. Applies to both types
}
}
public override void run() {
cm("dcr");txs=os;display(); // Get input string, assign to (txs) and execute it
// Several line feeds are automatically added at end of text causing large gap at
// bottom. We like to eliminate the last two.
os=rtp.Rtf; // Assign RTF string Of "rt0" to (os)
Char[] C=os.ToCharArray(); // Use char array to scan (os)
for (j=os.Length-1;j>-1;j--) { // Scan (os) backward
zs=os.Substring(j); // Reach the start of last 2 RTF line feeds then exit
if (zs.IndexOf((char)92+"par"+(char)13+(char)10+(char)92+"par"+(char)13+(char)10)>-1) break;
}
if (j>-1) rtp.Rtf=rtp.Rtf.Substring(0,j);
// Eliminate the 2 line feeds from the text
j=rtp.PreferredSize.Width;k=rtp.PreferredSize.Height;// Preferred width & height
if (width>0) j=width;if(height>0) k=height; // Override with specified values
if (j<260) j=260;if (k<60) k=60; // Apply minimum values
if (j>wmax) j=wmax;if(k>(hmax-60)) k=hmax-60; // Apply maximum values
x=j;y=k; // (x,y)=adjusted width & height
Size sz=new Size(j,k); // Make (sz) the adjusted size object
rtp.MaximumSize=sz; // and assign it to the MaximumSize
j=x;k=y+35;ib=true;cm("fs"); // Resize Clientsize accordingly.
cs="rt0";j=0;k=35/2;i=x;o=y;cm("sB"); // Reset (rt0)'s bounds.
if(dlc=='i') { // If input dialog:
cs="tf0";j=-35;k=10-(y+35)/2;i=190;o=40;cm("sB");
cs="bt0";cis="OK";j=100;k=16-(y+35)/2;i=60;o=30;cm("sB");
} // Set Bounds of tf0 and bt0 to fit resized form.
else { // If display only dialog:
cs="bt0";cis="OK";j=0;k=16-(y+35)/2;i=60;o=30;cm("sB");
} // Set Bounds of bt0 only to fit resized form.
ib=false;cm("fv"); // Make form visible
if(dlc=='i') {cs="tf0";cm("sx");} // If input dialog, Set focus for text field
}
void display() {// This method reads the input string (in txs) and executes its commands.
toa="t"; // Set text output device to text screen
cs="rt0";cm("O"); // Make (rtp) point to (rt0)
rta=rtp; // Make rt0 the text screen display object
if (txs.Substring(txs.Length-1,1)!=";") txs+=";";
// If string is not terminated with a ";", do it
while(txs.Length>0) { // Repeat until all commands extracted and executed:
js="";ks=";";tm("s"); // Get string starting at (txs)'s start and ending at
// first semi-colon. This means get first command.
x=os.IndexOf("="); // Get postion of "=".
if(x<0) {os="l="+os;x=1;} // If unavailable make it "Display text line" command
xs=os.Substring(0,x); // Assign string at left of "=" to (xs)
ys=os.Substring(x+1); // Assign string at right of "=" to (ys)
os=xs;om("l");xs=os; // If (xs) contains uppercase letter(s), convert to l/c
if (xs.IndexOf("c")>-1) {
os=ys;om("c");cls=os; // If xs="c", execute color command
}
else if (xs.IndexOf("f")>-1) {
os=ys;om("c");fns=os; // If xs="f", execute font command
}
else if (xs.IndexOf("l")>-1) {
os=ys;tm(); // If xs="l", execute display line command
}
else if (xs.IndexOf("d")>-1) {
os=ys;tm("d"); // If xs="d", execute display word command
}
else if (xs.IndexOf("g")>-1) {
os=ys;om("c");ims=os;tm("dg"); // If xs="g", execute display image command
}
else if (xs.IndexOf("t")>-1) {
os=ys;om("c");fls=os;tm("flt"); // If xs="t", Load plain text file
}
else if (xs.IndexOf("r")>-1) {
os=ys;om("c");fls=os;tm("flr"); // If xs="r", Load rtf file
}
else if (xs.IndexOf("w")>-1) {
os=ys;om("c");om("ti");width=o; // If xs="w", Specify Rich Text Box width
}
else if (xs.IndexOf("h")>-1) {
os=ys;om("c");om("ti");height=o;// If xs="h", Specify Rich Text Box height
}
if (txs.IndexOf(";")==0) txs=txs.Substring(1);
} // Remove the terminating semi-colon and repeat.
}
}

========================================================================================
WHAT CAN CUSTOM DIALOGS DO?
===========================
The answer is anything you want. They are equal in abilities to the main classes which
use them. They extend (pcs) and can access any Personal C Sharp method. They can create
any control, handle their own events, perform any graphical operation, create their own
threads, form a chain with other dialogs or even make their own dialog calls.
Using dialogs in your application can be very useful. Microsoft operating system and
network setups are mostly done by dialogs. Actually your application can be no more
than a main menu and a series of dialogs which perform all required operations.
PERSONAL C SHARP TEXT DIALOG
============================
The Text Dialog created in the previous example has been found to be a valuable tool which
can be used for several purposes within any application. Therefore we have decided to
create an optimized version of it to include into Personal C Sharp software packages.
The new version can set itself to be one of three types of dialogs:
(1) A simple text display dialog which contains the rich Text Box and the [OK] button
only.
(2) A text display dialog which includes a [SAVE] button for saving the displayed text
into file and a [PRINT] button for printing the text in addition to the Rich Text
box and the [OK] button used in type (1).
(3) The input dialog which you have created in the previous example.
Additionally the Text Dialog is now accessable through method cm().
To call the dialog and set it for type (1), call cm("dt");
To call the dialog and set it for type (2), call cm("dts");
To call the dialog and set it for type (3), call cm("dti");
A common parameter which the method expects at those three modes is the string (os) loaded
with any combination of the 9 commands which have been used in the previous example.
Type (1) and (2) require no additional parameters. Type (3) requires the block number
to continue execution at. Remember to follow type (3) call with a (return;).
Personal C Sharp dialog is included into PC# utilities class (pcsut) which you have downloaded
with Personal C Sharp package.
Repeating Example 10 using Personal C Sharp dialog:
===================================================
It becomes real easy. Class "TextDialog" is unnecessary in this case and class "a" stays
the same except for replacing the call cm("dcs") with cm("dti")
Here is the code:
-----------------
public class a:pcs {
public override void init() {
toa="t"; // Use text screen for text output
bli=0; // Start at block 0.
base.init();
}
public override void run() {
if (blp==0) { // Startup block
bli=1;ks="TextDialog i"; // continuation block, dialog file name, par
os="g=images\\icon.bmp;l=;"; // Make the input string, add the icon
os+="c=r0;f=trb14;"; // Add line feed then color and font codes
os+="l= MENU;l=;"; // Add the rest of the menu codes as
os+="c=S9;f=trb10;"; // described above.
os+="l= (R) Red.;";
os+="l= (B) Blue.;";
os+="l= (G) Green.;";
os+="l= (E) Exit.;l=;";
os+="c=b0;l=Selection :;";
cm("dti"); // Launch dialog, send data
}
if (blp==1) { // Continuation block. os=dialog out. data
os="Text Received: "+os;tm(); // Display text received locally.
}
}
}
-------------------------------------------------------------------------------------------
The new dialog is very handy in displaying "Help" text. If you use type (2) for this
purpose, your users will be able to print the help text or save it for future use.
You can create the help text using "WordPad" and save it into an "RTF" file. Then, you
instruct the dialog object to load the file contents and display them by:
os="r=FileName;";cm("dts");
Our product "TextArt" uses this dialog for displaying help. Here is a sample:

===============================================================================================
Scrollbars
==========
For Versions 1.80 and higher
The ".NET" allows you to set the form to "autoscroll". This means that whenever form contents
exceed its capacity, scrollbars are automatically installed in order to allow you to see the
full content of the page. However, the ".NET" can do this job only when all you have in the page
are controls. This is because the ".NET" has created the controls and archived them, so it can
manage their proper displacement whenever the scrollbars are activated.
For graphics, PC# takes care of this problem. You request the installation of horizontal bars,
vertical bars or both with methods cm("fbh") and cm("fbv") and PC# takes care of installing the
bars and managing the necessary displacement of graphics whenever the bars are activated.
As explained before, the default graphical device which is the bitmap (bio) always follows the
Form in size. Whenever you call method cm("fs") to resize the Form, (bio) is automatically
resized to match the form in size. (bio) is public but we don't expect you to violate this rule
by recreating it with a different size unless there is an urgent need to do so.
Whenever you need to draw beyond the Form size or display an image which is larger than the form,
the rule must be violated since (bio) must be large enough to take your drawings or your image.
The methods which install the scrollbars for you, also recreate (bio) at the size which your
job requires. Method cm("fbh") expects you to supply it with the wanted new width assigned to
(lf) so it creates a new (bio) with the width you have requested while maintaining the current
height. Method cm("fbv") does a similar job vertically. It expects you to supply your wanted new
height to (of) in order to recreate (bio) again at the new height.
===============================================================================================
Example 11a: Resize the form to a small size and install 4 label controls which are at distances
from each others which exceed the Form's size horizontally and vertically. Do what is necessary
to install Scrollbars as needed.
Example 11b: Resize the form to a small size and display an image which exceeds the form size
in both horizontal and vertical directions. Do what is necessary to install Scrollbars so the
full image can be seen.
===============================================================================================
Example 11a:
============
public class a : pcs {
public override void setup() {
j=k=200;ib=true;cm("fs"); // Resize ClientSize of Form to (200,200) Pixels
ib=true;cm("fa"); // Turn AutoScroll feature on.
// Install 4 lables at 4 extreme positions which require the creation of Scrollbars.
cs="lb0";cis="Top Left";j=-70;k=70;os="c";i=60;o=60;cls="r0y0";cm("i");
cs="lb1";cis="Top Right";j=500;k=70;os="c";i=60;o=60;cls="r0y0";cm("i");
cs="lb2";cis="Bottom Left";j=-70;k=-500;os="c";i=60;o=60;cls="r0y0";cm("i");
cs="lb3";cis="Bottom Right";j=500;k=-500;os="c";i=60;o=60;cls="r0y0";cm("i");
}
}
-----------------------------------------------------------------------------------------------
Example 11b:
============
public class a:pcs {
public override void run() {
j=k=220;ib=true;cm("fs"); // Resize ClientSize of form to (220,220) pixels
lf=300;cm("fbh"); // Request horiz scrollbars and new (bio) width of 300 pxls
of=400;cm("fbv"); // Request vert scrollbars and new (bio) height of 400 pxls
// Display an image which matches new (bio) size and exceeds Form in size.
lf=300;of=400;fls="images\\flower.jpg";gm("blf");gm("br");
}
}
===============================================================================================

===============================================================================================
IMPORTANT REMARK:
=================
The PC# managed scrollbars require that no part of (bio) should be left transparent. As you know,
(bio) is transparent when created, so its background color is the background color of the Form
underneeth it. Normally, we don't care to paint it, but if you are going to install scrollbars,
you must paint it or cover it in full with an image as we did in the previous example. The
easiest way to paint (bio)'s background with white paint is by using this code:
cls="s9";gm("ec");
How to make a scroll independant control?
-----------------------------------------
The scroll bars of example 11a and the ones of example 11b are irrelated to each others. The
first one scrolls the Form with all the controls it contains while the other one scrolls (bio)
with all the drawings it contains. So what could happen if we have standard controls like the
ones in example 11a and PC# managed scrollbars like the ones of example 11b?
The answer is that the scrollbars will move all the images and drawings but will not move the
controls. This can have very important applications. When you get to "Filing", you'll see that
this type of controls have been used in "Form Documents" managing software.
Drawing your own controls:
==========================
Everyone knows that we can draw a button, a textfield and any other control and handle their
events just like we do with the ".NET" managed controls. The question is why would anyone need
to do so. One reason is customizing your control to fit the style of your website or your
business. When we draw our controls by ourselves, we have unlimited capabilities which is not the
case when we use standard controls.
This seems to be easy to do, but can we make the controls which we draw scroll independant too?
Yes, method gm() contains tools which can make this almost work free. However, we need to show
you how to adjust your drawing each time the scrollbars change positions. At the end we'll show
you how to do it the easy way.
The "MouseClick" event can be used to handle the button's click and the "KeyPressed" event can
be used to collect user's typed text and display it into the text field. It is important to
know here that the mouse pointer coordinates which you receive at all mouse events are scrollbar
independant (assuming that we'll be using PC# managed scrollbars) So you don't need to adjust
them for scrollbar position changes. If the reason was not clear to you, it's because the
scrollbar positions are relative to the form which is stationary. What we are scrolling is (bio)
alone.
The Scrollbar events:
---------------------
Although, the scrollbar events are handled internally to adjust the locations of your drawings
each time a scrollbar is activated, you can also control their operation if you choose to.
Before the events are handled, method update is called to allow you to do whatever you like to do
before your drawings are adjusted for the new locations. You may also prevent the adjustment of
your drawings by returning (dnb=true)
Whenever the horizontal bar is activated, method update() is called with (cs="hs") and current
postion of the bar assigned to (oxf) Flag (dnb) is reset before the call.
Whenever the vertical bar is activated, method update() is called with (cs="vs") and current
postion of the bar assigned to (oyf) Flag (dnb) is also reset before the call.
If you handle the events in method update and make the assignment (dnb=true), no further action
will be taken. If you keep (dnb=false), your drawings will be adjusted for the new scrollbar
positions as usual.
In example 11b, those two events have not been handled. They are going to be handled in the
next example in order to keep the controls always at the center of the form regardless to the
scrollbar positions.
===============================================================================================
Example 12: Repeat example 11b and additionally install a text field and a button at the center
of the visible part of the form. When the user types something it shows into the text field and
when he clicks the button, the text he has typed is displayed into a dialog box.
The two controls must keep their positions regardless to scrollbar positions and also their
events must not be affected by activating the scrollbars.
===============================================================================================
// REMARK: this code is not final. It shows you how to handle scrollbar events, but it will not
// handle some events like maximizing the Form. The more useable code will come next.
public class a:pcs {
float xf,yf,x1f,y1f;
// Var's used: xf,yf = Current Scrollbar positions.
// x1f,y1f= Previous Scrollbar positions.
public override void setup() {
cs="mc0";cm("i"); // Receive mouse-clicking event.
cs="ky0";cm("i"); // Rexeive key-pressing event.
}
public override void update() {
//--------------------------- Scrollbars activation events ------------------------------
if (cs=="hs") { // If Horizontal Scollbar activated:
um("c"); // See Remark.
xf=oxf; // Get horizontal scrollbar position.
Draw(); // Rredraw controls
x1f=xf; // then assign position to (x1f)
}
else if (cs=="vs") { // If Vertical Scollbar activated:
um("c"); // See Remark.
yf=oyf; // Get horizontal scrollbar position.
Draw(); // Redraw control
y1f=yf; // then assign position to (y1f)
}
//---------------------------------- Key Press event ------------------------------------
else if (cs=="ky0") { // If keyboard key was pressed:
if (ob) return; // Return if special function key.
if(xs.Length<20) xs+=oc; // Add the pressed char to (xs), limit (xs) to 20 char's
um("c"); // Reset GUV's. See Remarks.
lf=200;of=25;gm("cr"); // Redraw text field to erase previous text
jf=-84+xf;kf=125+yf;id=10;cls="s9S2";ad=45;ks="d";gm("grs");
cls="b0";gm("sps");fns="trb16";
od=0;ad=0;os=xs;jf=-80+xf;kf=122+yf;gm("ctf");
} // Then draw current text in blue.
//--------------------------------- Mouse Click event ------------------------------------
else if (cs=="mc0") { // If mouse was clicked:
um("c"); // Reset GUV's ** Will be necessary in the future
// If clicked at submit button, display keyed-in string then erase it.
if (oxf>(40-30) && oxf<(40+30) && oyf>(125-12) && oyf<(125+12)) {
os="Received "+xs;cm("d"); // Display message in a dialog box.
xs=""; // Reset (xs) to reuse it.
}
}
}
public override void run() {
j=k=350;cm("fs"); // Resize form to (350,350) pixels
lf=450;cm("fbh"); // Request horiz scrollbars and new (bio) width of 450 pxls
of=600;cm("fbv"); // Request vert scrollbars and new (bio) height of 600 pxls
xf=yf=0;Draw(); // Draw image and controls with scrollbars at orig positions
}
void Draw() {
// Draws the image then it draws the controls at locations which are adjusted for scrollbars'
// positions. IN: xf,yf=Scrollbar positions.
um("c"); // Reset GUV's. See Remarks.
lf=450;of=600;fls="images\\flower.jpg";gm("blf");gm("br");
// Draw button using 3D effects-depth, adjust for scrollbar positions.
lf=60;of=25;gm("cr");
jf=46+xf;kf=125+yf;id=10;cls="r0S9";ad=45;ks="d";gm("grs");
// Draw button's label in white cententerd at buttons center which is shifted by small
// amounts horizontally and vertically from creation position due to 3D-depth effect.
cls="s9";gm("sps");fns="trb14";
jf=50+xf;kf=122+yf;os="Submit";gm("ctf");
// Draw text field using 3D effects=depth, adjust for scrollbar positions.
lf=200;of=25;gm("cr");
jf=-84+xf;kf=125+yf;id=10;cls="s9S2";ad=45;ks="d";gm("grs");
}
}
===============================================================================================
REMARKS:
========
(1) ** IMPORTANT **
As a general rule (jf,kf) are two of the GUV's which must be reset internally after the
completion of any internal operation. They must not be used for output. For a while, (jf,kf)
have been used to send mouse and scrollbar coordinates to method update() in violation to
this rule. Now we have replaced them with the new ouput variables (oxf,oyf) However, for
backward compatibility, we have kept assigning the same values to (jf,kf) This obligates you
to reset them using um("c") immediately after being received. In the near future (jf,kf)
assignments will be eliminated.
(2) Whenever you create a graphical object then call method gm("grs") to apply 3D effects to it,
make sure to create the object at the center, then supply method gm("grs") with the
coordinates of the point at which you like the object's center to move to. Do not create the
object at that point to start with.
(3) At the start, the distance between (bio)'s center and Form's center is 50 horizontally and
125 vertically. See sketch if that was not clear to you.
- ------------------------------------ -
| | | | |
| | | | |
| |---- 175 --|-50-| | | |
| | | | |
| | Form's center | |300
350| + | - | |
| | | | | |
| | |125 | |
| | | | | |
| | + | - | -
| | bio's center | |
- ------------------------- |
|------ 225 -----| |
| |
| |
| |
| |
| |
| |
| |
------------------------------------
(4) Centering the 3D Text Field and button within the form is also good to discuss. If you center
their front rectangles and forget about the rest of their bodies, they'll look fine
vertically since they are too thin regarding this dimension relative to the form, but they'll
not appear to be centered right horizontally. To be centered right means that we neither
center the front rectangle nor the rear one, we center a plane at the middle of the two.
We know that the difference in the X-direction between the front and rear rectangles is:
Depth/cos 45 = 10 / 1.42 = 7 approximately. So we choosed to shift the button-textfield
combination from From's center by 4 pixels which is about half of this amount. So instead of
positioning the combination's center at 50 pixels from (bio)'s center, we have positioned it
at 54 pixels. So:
For the Button: jf=100-54=46 and For the Textfield: jf=-30-54=-84
|----------- 200 -----------|-- 60 --|
-------------------------------------
|\ |\ |\
| \ | \ | \
| -------------------------|--------|--
- \ | \--|-----\ |
.\ | \ |Submit\ |
7 . \| \| \|
- . ---------------+--+-------------+-----
|7 |---- 100 -----|30|---70----|30 |
| | |Button's center
Textfield| |Combination
center| |center
|
|
|-- 54--|
|
+ (bio) center
(5) Drawing your own web controls is now also possible. See Example 10 of the WPDII.
Displaying (bip) independantly from (bio):
------------------------------------------
This feature is available in versions 1.801 and later. One advantage of it is that it makes it
possible for you to draw something and keep it from scrolling with the rest of your drawings when
the scrollbars are activated.
The idea behind it, is that it allows drawing the present Bitmap object (bip) on the top of (bio)
without allowing the two to blend together. This means that (bio) will never contain whatever
is drawn on (bip) This also means that we can make the drawing of (bip) relative to the Form
instead of being relative to (bio) which leads to the advantage of drawing controls which do not
scroll with the rest of the graphics.
Normally, (bio) is redrawn on the Form each time anything is drawn on its surface unless you
call gm("dn") to stop this automatic (bio) display feature and see the phrase "Please Wait" on
display. In this case you can draw (bio) manually whenever you like by calling gm("d") You can
resume the automatic display feature by calling gm("bn") for the second time.
The stoppage of the automatic display feature does not affect the display of (bip) using the
new modes. Whenever you call gm("bd") for example, (bip) is guaranteed to appear immediately even
if (bio) was not on auto display mode.
You need to know here that not all displays of (bio) are under our control. Whenever something
happens to the window where your class is running, like if it was minimized then maximized back
or if it was covered by another window then put back on the top, the operating system requires
repairing the display and this is when (bio) is redrawn even if it was not on auto display mode.
(bip) display modes available:
------------------------------
bd USE: Display (bip) relative to (bio) (without blending with it)
IN : jf,kf: Desired (bip)'s center location relative to (bio)'s center.
bdf USE: Display (bip) relative to Form.
IN : jf,kf: Desired (bip)'s center location relative to Form's center.
bdd USE: Display (bip) docked to one edge of the Form. (ds) assignment sets where to dock
It can be assigned (n/s/e/w/ne/nw/se/sw/c) Default is c (Center)
IN : jf,kf: gap to leave between (bip)'s edge and the Form's edge it docks with.
Examples: ds="w";jf=20; Sets it 20 pixels away from left side. ds="e";jf=-20; sets
it 20 pixels away from right side.
bdn USE: Stop displaying (bip)
When you call gm("bd"), (bip) will be displayed instantly, additionally it will always be
refreshed with (bio) It will also scroll with (bio) making you feel that it's part of it. The
coordinates (jf,kf) you supply for this mode are relative to (bio)'s center just as you expect.
When you call gm("bdf"), everything will be relative to the Form not (bio) You supply the
coordinates (jf,kf) relative to the center of the Form. Everything (bip) contains will appear to
be stationary and will not scroll with (bio)
Mode "bdd" is the same as mode "bdf" except that it allows you to dock your drawings to the
center of one edge of the Form, to one corner of the form or to the center of the Form. This is
determined by the direction code which you assign to (ds) The parameters (jf,kf) are not
coordinates in this mode, they represent the margin gap which you like to keep when your drawing
docks with one dege of the Form.
How to stop drawing (bip):
--------------------------
There are many uses for (bip) Although sometimes, you may need to draw something on it and
like to constantly refresh it each time (bio) is refreshed, most of the time whatever (bip)
contains are temporary drawings which you don't like others to see. For this reason, it's
important to be able to stop the drawing of (bip) The automatic drawing of (bip) can be stopped
by two means:
(1) Calling gm("bdn")
(2) PC# will stop the automatic drawing of (bip) whenever method gm() is called to create a new
(bip) or to modify the current one. The logic behind this is that the new or modified (bip)
will be the one to appear from this point up if we keep things the same even if you have
never intended to show it. If you do, you should call method gm() again at one of those modes.
Receiving Mouse Events:
-----------------------
In the previous example, we have used the following conditional statement to see if the point
which has been clicked on was within the borders of the submit button.
if (oxf>(40-30) && oxf<(40+30) && oyf>(125-12) && oyf<(125+12)) {.....}
This is based on the discrete position of the center point of the button. We have assumed that
the button is going to be always at the same location which is (40,125) This makes some sense
since the drawing is always at the center of the Form regardless to scrollbar positions and that
mouse events are relative to the Form too.
However, this will not keep the user happy for long. Once the user resizes the Form, the button
will no longer be at its expected position and clicking it may do nothing! Additionally, the new
methods have added extra complexity since they allow you to dock (bip) which contains the button
to any side or corner of the Form, so you can't easily know where the button is.
For these reasons we have added mode "bdc" which reads the clicked on coordinates and modifies
them to make them relative to (bip)'s center no mater where the button is or which size the Form
is at. So all you need to know is how far the button is from the center of (bip) which is a
constant. Actually the button and textfield combo are going to be centered into (bip) so by
looking at the sketch above, the button's center is always at (100,0) from (bip)'s center.
At the mouse click event, method update() receives the coordinates of the clicked on point
assigned to (oxf,oyf) When you call method gm("bdc") the method returns (oxf,oyf) modified so
that their values become relative to (bip)'s center.
REMARK: If you have used method gm("bd") to display (bip), use method gm("brc") to adjust mouse
coordimates, since (bip) in this case scrolls with (bio) just like any object which is directly
drawn on its surface.
Rewriting the code of the previous example:
-------------------------------------------
Using these new features, the previous example can be considerably simplified. You don't need to
receive the scrollbar coordinates since, by using method gm("bdf") or gm("bdd"), your drawings
display becomes scroll independant. Additionally, handling mouse events has been simplified.
---------------------------------------------------------------------------------------------
public class a:pcs {
Bitmap bi0,bi1;
// Var's used: bi0,bi1 = Backups for Form and drawing (bip)'s
// xs = String keyed-in by user into text field.
public override void setup() {
cs="mc0";cm("i"); // Receive mouse-clicking event.
cs="ky0";cm("i"); // Rexeive key-pressing event.
}
public override void update() {
//---------------------------------- Key Press event ------------------------------------
if (cs=="ky0") { // If keyboard key was pressed:
if (ob) return; // Return if special function key.
if(xs.Length<20) xs+=oc; // Add the pressed char to (xs), limit (xs) to 20 char's
bip=(Bitmap)bi1.Clone(); // Make a copy of empty textfield and button drawing
gm("sdb"); // Make it graphical output device.
cls="b0";gm("sps");fns="trb16";
os=xs;jf=-26;kf=-3;gm("ctf"); // Draw text at center of textfield.
gm("sdd"); // Switch back to default graphical output device.
ds="c";gm("bdd"); // Draw bitmap at Form's center
}
//--------------------------------- Mouse Click event ------------------------------------
else if (cs=="mc0") { // If mouse was clicked:
um("c"); // See Remark.
gm("bdc"); // Modify received (oxf,oyf) to be relative to (bip)'s center
// If clicked at submit button, display keyed-in string then erase it.
if (oxf>(100-30) && oxf<(100+30) && oyf>(0-15) && oyf<(0+15)) {
os="Received "+xs;cm("d"); // Display message in a dialog box.
xs=""; // Reset (xs) to reuse it.
bip=bi1;ds="c";gm("bdd"); // Draw bitmap at Form's center
}
}
}
public override void run() {
j=k=350;cm("fs"); // Resize form to (350,350) pixels
lf=450;cm("fbh"); // Request horiz scrollbars and new (bio) width of 450 pxls
of=600;cm("fbv"); // Request vert scrollbars and new (bio) height of 600 pxls
Draw(); // Draw image and controls with scrollbars at orig positions
}
void Draw() {
// Draws the image then it creates a bitmap and draws the button and text field into it.
um("c"); // Reset GUV's. See Remarks.
lf=450;of=600;fls="images\\flower.jpg";gm("blf");gm("br");bi0=bip;
lf=280;of=40;gm("bn");gm("sdb");// Create a bitmap with enough size for the drawings.
// and make it the graphical output device.
// Draw button using 3D effects-depth.
lf=60;of=25;gm("cr");
jf=100;kf=0;id=10;cls="r0S9";ad=45;ks="d";gm("grs");
// Draw button's label in white cententerd at buttons center which is shifted by small
// amounts horizontally and vertically from creation position due to 3D-depth effect.
cls="s9";gm("sps");fns="trb14";
jf=104;kf=-3;os="Submit";gm("ctf");
// Draw text field using 3D effects=depth.
lf=200;of=25;gm("cr");
jf=-30;kf=0;id=10;cls="s9S2";ad=45;ks="d";gm("grs");
gm("sdd"); // Switch graphical output device back to default
ds="c";kf=0;gm("bdd"); // Display bitmap at center of Form
bi1=bip; // vertical scrollbar width. Save drawing
}
}
---------------------------------------------------------------------------------------------
Compile the code and run it. Move scrollbars, then resize the Form and check button click in
each case. Try also docking (bip) to different sides and corners of the Form and check how it
works. Remember that gm("bdd") call where your modifications should be is available at 3
locations within the code.
==============================================================================================

===============================================================================================
KEYBOARD EVENTS
===============
In the previous example, reading keyboard characters and using them has been very simple. All we
needed to do, was installing control (ky0) and receiving its incoming events in method update()
The keyboard characters which have been coming assigned to (oc) have been all we need. This has
been simple for two reasons:
(1) We did not expect to receive events which are generated by pressing special use keys like
Delete, Insert, Function Keys or arrow keys.
(2) We used our own drawn controls. We did not use the standard controls which share the key
events with us.
Now, we are going further, we are going to see how to receive special key events and how to
receive key events in a class wich contains installed standard ".NET" controls.
Handling the Keyboard Event:
============================
When method update() is called after a key has been pressed, it receives two parameters, (oc)
and (ob) The type char variable (oc) can be assigned either an ordinary ASCII character or a
code which represents a special function key.
The boolean variable (ob) is a flag which tells which character type (oc) contains. If (ob=false)
the character in (oc) is a standard ASCII char. If (ob=true) the character in (oc) is a special
function key code.
Standard ASCII characters:
--------------------------
We mean by standard ASCII character any alphanumeric char. It can be lower cased or upper cased.
It can also contain non-printable char's with ASCII codes in the range (0:31) like the [TAB],
[BACKSPACE] and [ESCAPE] whose ASCII codes (in order) are 9,8 and 27.
Additionally, when the [Control] key is pressed in addition to any alphabetic char, (oc) receives
characters with ASCII code in the range (1:26)
Special function key codes:
---------------------------
These codes are assigned to (oc) whenever one of the special function keys is pressed. They are
as follows:
---------------------------------------------------------------------------------------------
Arrow Keys : l = Left Arrow r = Right Arrow u = Up Arrow d: Down Arrow.
Insert/Delete: i = Insert key e = Delete key
Lock Keys : c = Caps Lock n = Numeric Lock s = Scroll Lock
Page Up/Down : U = Page Up D = Page Down
Restart/Exit : B = Break H = Home
Other Keys : a = Alt h = help p = Print Screen
Function Keys: 1 = F1 2 = F2 3 = F3 4 = F4 5 = F5
6 = F6 7 = F7 8 = F8 9 = F9 0 = F10
----------------------------------------------------------------------------------------------
The difficulty caused by installed .NET controls:
=================================================
Whatever we have mentioned above is true and will work just as discussed if there were no ".NET
controls" installed into the Form. If standard .NET controls like buttons and text boxes have
been available, they would have competed with us in consuming keyboard events.
When you press the [TAB] key, it means to those controls that you like to move the focus from the
control which is currently at focus to the next control. If the focus was at a text box, any char
you type is meant to be entered into that text box. The arrow keys also mean different things to
different controls. Pressing [ENTER] while the focus is at a button means "click that button".
For this reason, we have to request series of actions to allow method update to be the only user
of the keyboard events whenever we see that necessary. Method cm() contain three modes which
regulate the keyboard event sharing. They are as follows:
MODE ACTION
==== ======
fk0 Level 0: Do not handle Kyboard events. Leave keyboard events for Controls.
fk1 Level 1: Handle keyboard events. Prevent controls from reading keyboard.
fk2 Level 2: Same as level 1. Additionally all controls are disabled in order to guarantee
that they will not consume keyboard events. This level is not recommended.
Taking event handling of some keys away from controls is sometimes a hard job to do. The ".NET"
controls do not act the same regarding some keys. This is why we have added level 2. However,
disabling every control listed in PC# archives may cause other problems. so we don't recommend
using level 2 unless level 1 has failed and you have no other alternative. A better solution when
level 1 fails is to disable the control or controls which are causing the problem alone.
================================================================================================
Example 17: Create a Form with a standard ".NET" text field and a button. Additionally draw a
graphical text field into it and install (ky0) Start with keyboard event handling left for
controls. When user clicks the button, the event handling switches to method update() When any
key is pressed then, a message is displayed into the graphical text field which identifies the
key. When [ESCAPE] button is pressed, keyboard handling returns back to the standard controls.
================================================================================================
public class a:pcs {
public override void setup() {
j=300;k=150;cm("fs"); // Resize form to (300,150) pixels
cs="ky0";cm("i"); // Receive keyboard events
cs="pn0";o=25;i=300;cds="s";cls="s9s9";cm("i");
// Install a panel and dock it to Form's bottom.
cs="tf0";ps="pn0";o=25;i=220;cds="f";fns="trb12";cls="r0c4";cm("i");
cs="bt0";ps="pn0";cis="Switch";o=25;i=80;cds="w";fns="trb12";cls="s9r0";cm("i");
} // Install a text field and button into the panel.
public override void update() {
//---------------------------------- Key Press event ------------------------------------
if (cs=="ky0") { // If keyboard key was pressed:
char o1c=oc;bool o1b=ob; // Copy received values to persistent var's.
cls="s9";gm("sps"); // Prepare white brush and
kf=12;lf=250;of=50;gm("crf"); // paint over previous message to erase it.
cls="b0";gm("sps");fns="trb14"; // then prepare a blue pen and a new font
if (o1b) { // If key pressed was a special function key:
os="Received special function key code: "+o1c;
kf=12;gm("ctf");gm("grf"); // Double strike its code and exit.
return;
}
else if ((int)o1c==27) { // If key pressed was the [ESCAPE] key:
cm("fk0"); // Set keyboard event handling level at zero
cs="tf0";cus="";cm("su"); // Erase any text left into standard text field
cs="tf0";cm("sx"); // and set focus at text field.
}
else if ((int)o1c<32) { // If ASCII code of key pressed was under (32, decimal)
os="Received character of ASCII code: "+(int)o1c;
kf=12;gm("ctf");gm("grf"); // Double strike code and exit
return;
}
else { // Else if was printable character:
os="Received the printable character: "+o1c;
kf=12;gm("ctf");gm("grf"); // Double strike message identifying character.
return;
}
}
else if (cs=="bt0") { // If "Switch" button clicked:
cm("fk1"); // Set keyboard event handling at level 1.
cls="y0";gm("sps"); // Prepare yello brush and
kf=12;lf=250;of=50;gm("crf"); // highlight graphical text field.
}
}
public override void run() {
cls="g7";gm("ec"); // Paint (bio)'s background.
cls="r0";i=2;gm("sps"); // Prepare 2 pixel wide red pen.
kf=12;lf=254;of=54;gm("crf"); // Draw red frame
cls="s9";gm("sps"); // Switch to white color.
kf=12;lf=250;of=50;gm("crf"); // Draw graphical text field.
cm("fk0"); // Set keyboard event handling at level zero.
cs="tf0";cm("sx"); // Set focus at standard text field.
}
}
===============================================================================================