Personal C Sharp                                                         By  famsoft.org
HomeHow To StartExamples-DesktopExamples-WebPC# MethodsReference-DesktopReference-Web


============================================================================================== 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 of the "Drawing" chapter. 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. } } ===============================================================================================


=============================================================================================== USING THE WINDOWS PRESENTATION FOUNDATION (WPF) =============================================== We have now two seperate classes. Class (pcs) is for version 2. You can use it with either version 2 or version 3. Additionally, we have class (pcs3) which can be accessed with version 3 only. The two classes are identical in everything except Controls and Drawing. All version 3 additions will be in class (pcs3) only. Changes made to Controls: ========================= The (WPF) controls are more similar to the "Web Controls" than to the "Desktop" Controls. For this reason, class (pcs3) is using almost identical software to the one used in class (pasp) for this purpose. Class (pc3) does not do the layout discretely, it does it in the same manner as class (pasp) does. ------------------------------------------------------------------- If you have not studied the "Web Examples" yet, you must study at least the first 4 examples of the "WPDI" before you get further. ------------------------------------------------------------------- Class (pasp) inserts each group of controls into a table, then inserts the tables into bigger tables, and this continues until it ends with the root table "tb0" containing the whole page. Class (pcs3) uses the same idea except that it replaces tables with panel called "Grids" which is very similar to tables. The root grid "gr0" contains everything in the page. Some of the good ideas used in class (pasp) like having a "Master page" and combining controls with graphics are going to be used also in class (pcs3) Although we have done the best we can to absorb the new changes internally into class (pcs3), so you can use the same PC# methods as if nothing has been changed, there are still some few changes which you need to be taking care of. As a sample, this is how the first example of this section should be written using (pcs3) and the WPF: ---------------------------------------------------------------------------------------------- public class a : pcs3 { public override void init() { base.init(); } public override void setup() { // All installations are done within this method //---------------------------------- Page Contents ----------------------------------- cns=""; // gr0 has no container since it's the root cs="gr0";cls="S9g8";cm("i"); // gr0 installation parameters //----------------------------------- tb0 Contents ----------------------------------- cns="gr0"; // tb1 is divided into 4 col,3 rows cs="tf0";j=k=0;oyd=0.5;ds="c";lf=200;cls="b0y0";fns="trbi20";cm("i"); cs="bt0";cis="My Color Changes";j=0;k=1;oyd=0.5;ds="c";lf=160;of=30;fns="trb16"; cls="S9y0";cm("i"); } 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) if (cus.Length<1) return; 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. } } } ---------------------------------------------------------------------------------------------- As you can see, the code is mostly the same. You are still using the same methods, the same architecture, the same color and font codes. So, as far as you are concerned almost nothing has changed. The only difference, you may have noticed is using grids instead of tables. Using grids is similar only. Some rules are different since they require more complicated setup. We have done our best to simplify their setup and ended with dividing controls into 3 categories each require different actions. Let us list all setup parameters before we get further: j,k : Index of first cell which the control occupies horizontally and vertically. This has been explained before so we are not going to say more about these two parameters here. i,o : Column and row span. These two items have also been discussed thoroughly before. lf,of : Width and height of the control. oxd,oyd: They are new. When you like to install two controls into one grid panel horizontally, and the first contol is twice as wide as the second one, you need to make the assignments (oxd=0.67;oyd=0.33;) This means: oxd = (CellWidth / ContainerWidth) oxd = (CellHeight / ContainerHeight) When we have been installing web controls into tables, the space allocation job has been done automatically. Unfortunately, Grid panels are less helpful with this feature. ds : Direction string. Can be (c/n/s/e/w/ne/nw/se/sw/fh/fv) It tells how the control will be aligned inside its cell. (c) means center, Next 8 directional symbols are obvious. (fh) and (fv) mean that the control should fill its cell horizontally / vertically. Default values: --------------- When you keep a parameter unassigned, its value will be zero or "" which mean the following: j,k : Means that the control starts at the first cell (horizontally, vertically) i,o : Means that the control ocupies one cell (horizontally, vertically) oxd,oyd : Means that the control occupies the full width or the full height of its container. ds : Means mount the control at the center of its cell. Let us now discuss each control category requirements: (1) Grids: ---------- As you can see in the code above, the root table is installed first. Then you may need to install more grids into the root grid and this can continue forming a tree of grids. The root grid require no parameters other than the color if you like it to be identifiable. This is because,it is the only control in its container. Branch grids require j, k, i, o and either (lf,of) or (oxd,oyd) You may also supply both if you don't like the grid to occupy its entire cell. (2) Image Controls: ------------------- The simplist way to mount an Image Control is to divide the space of its container by installing additional grids into it first, then install the image control into one of the new grids. Since it will be the only control in its container, it will require no parameters other than (lf,of) if you like to change the size of its image. (3) All other controls: ----------------------- Supplying j, k, i, o, oxd and oyd is necessary for all other controls (unless you are accepting defaults) Assigning values to (lf, of) is optional. Creating a master page: ======================= As you have noticed with web controls, master pages offer a great help. So, let us have one which contains an icon, a title and a settable number to be displayed. The master page will extend class (pcs3) and each example will extend the master page. You can create a hierarchy of master pages and each may contain fields and methods which can be accessed by your application class. --------------------------------------------------------------------------------------------- public class ms1:pcs3 { public override void setup() { base.setup(); j=560;k=360;cm("fs"); //---------------------------------- Page Contents ----------------------------------- cns=""; // gr0 Container="" since it's // mounted directly on the page. cs="gr0";cls="S9s9";cm("i"); // gr0 installation parameters //----------------------------------- gr0 Contents ----------------------------------- cns="gr0"; // Container=gr0 for next tables cs="gr01";cis="g1";j=0;k=0;lf=410;of=60;ds="w";cls="S9g7";fns="tr12";cm("i"); cs="gr02";j=1;k=0;lf=150;of=60;ds="e";cls="S9g7";fns="crb10";cm("i"); cs="gr03";cis="g2";j=0;k=1;i=2;lf=560;of=300;ds="c";cls="S9y7";fns="crb10";cm("i"); //----------------------------------- gr01 Contents ---------------------------------- cns="gr01"; // gr1 is divided into 2 rows cs="lb11";j=0;k=0;oyd=0.6;ds="c";cis="Windows Presentation Foundation Examples"; fns="trb16";cls="r0g7";cm("i"); cs="lb12";j=0;k=1;oyd=0.4;ds="c";cis="Example Number 1";fns="trb12";cls="b0g7";cm("i"); //----------------------------------- gr02 Contents ---------------------------------- cns="gr02"; // gr2 contains one image only. cs="im01";ds="e";ims="images\\icon.bmp";cm("i"); } } ---------------------------------------------------------------------------------------------- The master page makes a good exercise on mounting controls. Here are some remarks: (1) We needed to create two grids one for common data at the top and one to be used by the child pages. Since we wanted to mount an image, we had to create a third grid for it. (2) For the grids we must supply either (lf,of) or (oxd,oyd) In this case, we supplied (lf,of) (3) (i,o) have been left unassigned for all except the image since it occupies 2 cells vertically (4) For (lb11,lb12) we, (oxd) has not been assigned since they occupy their entire cell horizy. ---------------------------------------------------------------------------------------------- Accessing class (pcs) graphics: ------------------------------- Version 2 has been great for graphics and we have spent plenty of time creating software using the ".NET" version 2 and both classes (pcs) and (pasp), so we must make use of them here. We have developed an easy way to access version 2 graphics which requires the following: (1) You install an Image Control which will receive (bio) with your drawing on. (2) You create a nested class which extends class (pcs). (3) You start the nested class by calling gm("wo") meaning "Open a new WPF operation". (4) You write the same code into the nested class just as you have been. (5) You end the nested class with calling gm("wc") meaning "Close the WPF operation. This will modify the resulting (bio) with all the graphics on its surface to meet WPF requirements. (5) From method run() of the top class you execute the nested class then call cm("su") supplying it with the image control's keyname. Method cm() at this mode updates an image control by changing its image. The method expects the new image file name to be assigned to (ims) This time we assign "b" to (ims) which means to the method that the image is (bio) --------------------------------------------------------------------------------------------- Example 1: Create a master page then extend it with a class which runs two examples of the version 2 kind. One is example 1 of "Handling controls" and the other is example 8 of "Drawing". --------------------------------------------------------------------------------------------- //assembly ms1.exe; // Referenced class. public class a : ms1 { // using pcs3 // Class a extends master class. public override void init() { base.init(); } public override void setup() { base.setup(); //----------------------------------- gr3 Contents ----------------------------------- cns="gr3"; // gr3 is divided into 4 col,3 rows cs="gr4";cis="g1";j=0;k=0;oxd=0.5;ds="w";cls="S9y7";fns="tr12";jd=5;cm("i"); cs="gr5";j=1;k=0;oxd=0.5;ds="e";cls="S9y7";fns="crb10";jd=5;cm("i"); //----------------------------------- gr4 Contents ----------------------------------- cns="gr4"; cs="tf0";j=k=0;oyd=0.5;ds="c";lf=200;cls="b0y0";fns="trbi20";cm("i"); cs="bt0";cis="My Color Changes";j=0;k=1;oyd=0.5;ds="c";lf=160;of=30;fns="trb16";cls="S9y0";cm("i"); //----------------------------------- gr5 Contents ----------------------------------- cns="gr5"; cs="im1";ds="c";ims="";lf=200;of=200;cm("i"); } // This is the control which will receive (bio) and scale it //-------------- Method update is entirely used by color change section ---------------- 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) if (cus.Length<1) return; 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. } } //------------------- Method run is entirely used by the Drawing section --------------- public override void run() { N no=new N(); // Inistantiate nested class. no.run(); // Run nested class instance. cs="im1";ims="b";cm("su"); // Update Image control with (bio) image. } //--------------------------------- The nested class ----------------------------------- public class N : pcs { // Class (N) extends (pcs) public override void run() { gm("bwo"); // Open a new WPF operation j=k=220;cm("fs"); // Reduce size to fit object. cls="y7";gm("ec"); // Make background color matche page's color. //********************** No change upto next asterisk line ************************* lf=of=170;gm("ce"); // Create the circular gold plate cls="o5y5";gm("spl"); // Prepare linear gradient brush for it gm("grf"); // then render-fill the gold plate. lf=6;of=50;gm("c="); // Create hexagon shape object at center. cls="r0";ks="r";gm("grs"); // Draw the object using sp effects-refl at jf=45;cls="b0";ks="r";gm("grs"); // center in red, then repeat 6 times using jf=-45;cls="b0";ks="r";gm("grs"); // different colors and different locations jf=22;kf=40;cls="g0";ks="r";gm("grs"); jf=-22;kf=40;cls="m0";ks="r";gm("grs"); jf=22;kf=-40;cls="m0";ks="r";gm("grs"); jf=-22;kf=-40;cls="g0";ks="r";gm("grs"); lf=of=25;gm("ce"); // Create a circle at center (pearl) for (int x=0;x<20;x++) { // Draw it 20 times using sp effects-reflection jf=80;kf=18*x;kb=true; // at locations around the plate. Polar coord's cls="p0";ks="r";gm("grs"); // are used for specifying locations } //*********************************************************************************** gm("bwc"); // Close WPF operation } } } ==============================================================================================