developing with openedge gui for .net in developer...
TRANSCRIPT
1
DEVELOPING WITH OPENEDGE GUI FOR .NET IN DEVELOPER STUDIO
PUG AFRICA CONFERENCE 2020
This workshop is aimed at Progress OpenEdge developers that are new to developing applications with
the OE Developer Studio and designing User Interfaces with the OE GUI for .NET.
Presented by: Mark Davies
ProSoft - Software & Business Solutions
2
• Own Laptop – (a pretty decent one)
• Developer Studio 11.7 or higher – already installed • Progress OpenEdge development experience
• Basic understanding of Object Oriented development
WORKSHOP REQUIREMENTS
3
Launch OpenEdge Developer Studio
Specify a location where you will create your workspace – somewhere simple and quick to access.
Like:
D:\Workspace\WorkshopPUG2020
When you open a workspace for the first time, you will get the Welcome page. Select Workbench to open the standard workbench.
4
5
Create new projects.
From the menu, select New and then OpenEdge Project
We will be creating 3 projects in this workspace.
1. PUG2020Client -> This is for our Client-side application 2. PUG2020Common -> Project for common components that we want to share between
the different environments 3. PUG2020Server -> Typically we will want to deploy our server-side code separate from
the client-side code and probably to an AppServer PAS or Classic
6
7
8
9
Default Perspective
10
11
Create the second project PUG2020Common:
12
13
Create the final project PUG2020Server:
14
15
16
We should now have the following:
17
Create a Sports2000 Database
Select the PUG2020Server project and then Open the Data Dictionary from the main toolbar
18
Create a new Dbs folder in your workspace folder (from this example D:\Workspace\WorkshopPUG2020\Dbs).
Click on Files and specify this new folder and also the name for the new DB – call it Sports2000
Once created, DO NOT connect to it.
19
Access the project properties from right-click on the project and select Properties in the context menu:
20
Expand Progress OpenEdge and select Database Connections:
Select the Configure database connections link
21
22
23
24
25
26
Check that the database has started and is connected:
27
Configure the OpenEdge Editors (the way I like it)
28
29
30
31
How to BLOCK select code (where to find the shortcuts):
32
Creating your own Macros:
33
34
Code:
DEFINE VARIABLE ${QueryHandle} AS HANDLE NO-UNDO.
DEFINE VARIABLE ${BufferHandle} AS HANDLE NO-UNDO.
${BufferHandle} = BUFFER ${TableName}:HANDLE.
CREATE QUERY ${QueryHandle}.
${QueryHandle}:SET-BUFFERS(${BufferHandle}).
${QueryHandle}:QUERY-PREPARE(SUBSTITUTE("FOR EACH &1 WHERE &1.Field =
'&2'",${BufferHandle}:NAME,"SomeValue")).
${QueryHandle}:QUERY-OPEN().
${QueryHandle}:GET-FIRST().
DO WHILE ${BufferHandle}:AVAILABLE:
${QueryHandle}:GET-NEXT().
END.
${QueryHandle}:QUERY-CLOSE().
${QueryHandle} = ?.
DELETE OBJECT ${QueryHandle} NO-ERROR.
35
Open the project Properties for PUG2020Client and then expand Progress OpenEdge and select PROPATH.
Add the PUG2020Common workspace (from the Workspace Directory button).
36
Open the sample Form1.cls file in the GUI editor or create a new GUI Form.
The UI is blank or not visible – this is because of the DPI settings – how do we fix it?
Open the Properties of the Developer Studio Icon and click on Change high DPI Settings and turn ON the Override high DPI scaling behavior and set it to Application.
Run Developer Studio again and check that you can open a GUI Form.
37
The following section explains how we will create a dataset and then load data into this dataset
and use it on the GUI client.
• Open the PUG2020Server project and create a new folder called Utilities and then copy
the GenerateIncludes.p program supplied into this folder.
• Open this program in the editor – this will be explained.
• Open the PUG2020Common project and add a folder called Salesrep.
• Create the includes for Salesrep table
• The contents of the two include files will be explained.
o Why do we create include files with the fields and not just LIKE?
• Open the PUG2020Server folder and then create a new folder called SalesRep
• Create a new Procedure in the SalesRep folder called SalesRepDataAccess.p
CREATING DATASET AND LOADING DATA INTO A DATASET
38
First, we will need to include our dataset definition:
Next, we will create a new internal procedure and define two parameters:
39
Next, we will define variables, create a query and a data source, attach it, fill it and clean up.
40
The program should look something like this:
41
Let’s test this in ABS Scratchpad:
We should be seeing this:
42
We can now build a simple Form with a grid and fill it with data.
In the PUG2020Client project, create a new folder for SalesRep.
Right-click on the SalesRep folder and select New -> ABL Form
43
Add a ProBindingSource from the Toolbox under the OpenEdge Controls group.
44
Once added, the ProBindingSource Designer will be launched.
Select the Import Scheme from file and then select the ttSalesrep.i file in the PUG2020Common\SalesRep folder in your project.
Once the file is selected, check the ttSalesrep table in the Tree and click on Add – this will add the table and all fields to the bindingsource.
45
Select OK to complete this process.
46
Next, we want to make sure we name our controls to something that is easy to find and user later in code.
Select the newly created bindingsource control and then go to the Properties window and select the Name property and change it to SalesrepBindingSource:
Assign the following properties on this control:
AutoSort -> True
AutoUpdate -> True
47
Next, add a DataGridView onto the form by dragging it from the Toolbox (under the Microsoft Controls group).
Open the property control box and select the SalesrepBindingSource in the Choose Data Source property:
48
By default, all columns are added to the grid, we only want a few. Select the Edit Columns link, select the first Month Quota field and delete it – do the same for the 11 remaining columns:
49
Add a Button onto the form and name it btnFill and assign the Text property to Fill.
Note on how the Properties can be grouped / sorted – use what is best for you.
Select the Events TAB on the Properties window, make sure the Button is still selected and then locate the Click event and add a trigger for this event and call it OnClickFillButton and press ENTER.
50
When you enter, it will automatically generate the code-behind for this button trigger:
Since we want to make use of our dataset we will need to include the reference to it, add the following line to the definition sections of the SalesRepForm class:
Let’s write the code to retrieve the data into the dataset. Go back to the OnClickFillButton method – use Ctrl+O to get the outline pop-up and locate this method and double-click on it.
51
Add the following lines of code here:
Explanation of code to follow in workshop 😉
Attempt to run this directly from project.
The form runs, but clicking on Fill raises an error.
We need to add the PROPATH to the server-side project first.
Add the PROPATH by going to the Project’s Properties and adding the PUG2020Server project path to this path.
Run it again and select the Fill button – it still fails, because the database is not connected.
We fix this by adding the database connection to the Run Configuration for this one screen.
52
Locate the SalesRepForm on the left tree and then select the Databases TAB on the right:
53
Select the Sports2000 database then click Apply and Run.
When you click on Fill, the data is filled and the grid now has data.
54
The form doesn’t look very pretty yet – sizing doesn’t work and we don’t really want the user to have to click on a button to fill the data. We will do this next:
We will be creating methods by the dozens, let us first create a few macros first.
First macro is to create a PRIVATE method and a second to create a PUBLIC method.
Option the OE Macros and add these with the following code:
Code for Private method mpr
/*------------------------------------------------------------------------------
Purpose:
Notes:
------------------------------------------------------------------------------*/
METHOD PRIVATE ${ReturnDataType} ${MethodName}():
${cursor}
END METHOD.
Code for public method mpu
/*------------------------------------------------------------------------------
Purpose:
Notes:
------------------------------------------------------------------------------*/
METHOD PRIVATE ${ReturnDataType} ${MethodName}():
${cursor}
END METHOD.
1. Move the code to fill the dataset into a new private method called FillData 2. Remove the OnClickFillButton event ON THE BUTTON – do not just delete the code. 3. Remove the button on the screen too
55
4. Subscribe to the Load event on the form (Method name, OnLoadForm) 5. Call the FillData method from this method 6. Rename the Grid to SalesrepGrid 7. Turn off the option to Edit, Add and Delete in the grid 8. Set the anchors for the grid – see what the anchor properties do and how easy sizing
becomes 9. Watch out for sizing to small 10. Other properties to set for the grid:
a. AutoSizeColumnsMode -> Fill (works well for only a few columns) b. BackgroundColor -> White c. SelectionMode -> FullRowSelect
11. Set the following properties on the SalesrepBindingSource: a. AutoSort -> True b. AutoUpdate -> True
12. We now have a functioning form to VIEW salesrep information, but that is only part of what we want to do – we need all the CRUD operations.
13. To support a decent CRUD functioning form we would also need to give the user better fields to update the data and we’ll address that next.
14. To create a better looking Form with separation of information, you can use TABs or separated sections by defining Group Boxes. I find it easier to use to Microsoft Table Layout Panel – it has some functionality that will make splitting the sections of a form dynamic and responsive.
15. Add a TableLayoutPanel with 2 rows, the first a variable size of 100% and the second a fixed size – we’ll adjust that size as we go along the process. Set the Anchor property to be Top, Bottom, Left, Right
56
16. Add a group box to the second row in the layout panel – we need a container-type object in the TableLayoutPanel rows as it only allows a single control.
17. Drop the group box, name it to grbSalesrep and then set set these properties: a. Dock -> Fill b. Text -> Salesrep
18. The name of the label controls should prefix lbl and then the name of the field, eg, lblSalesrep and lblRepName. The following properties need to be set for the label controls:
a. Name -> lbl and the field name b. AutoSize -> True c. Text -> The label for the field
19. The TextBox control is used for standard text entry, drag a TextBox onto the form and set the following properties:
a. Name -> prefix with txt and then the field name 20. Add another GroupBox inside of the Salesrep GroupBox and size it so that it fills the
bottom part of the form with enough space so that we can add 6 rows of information. Set the properties of this group box to:
a. Name -> grbMonthQuota b. Anchor -> Top, Bottom, Left, Right
21. To assist with the layout of the 12 quota fields, we will again make use of the TableLayout control – go ahead and drop another TableLayout control in the Month Quota group box and set the following properties:
a. Name -> tlpMonthQuota b. Dock -> Fill
22. Select the control’s Edit Rows and Columns from the context menu (right-click on the control) and change it to have the following:
23. Rows -> 6 with a fixed size of 24px
57
24. Columns -> 4 with a variable size of 25% for each 25. We will need to add labels for each of the 12 month’s quotas, a label for each of the
months in the year with the first 6 months in column 1 and the last 6 in the 3rd column. The following properties should be set for each of the labels:
a. Name -> lblMonthQuota followed by a number for the month, eg lblMonthQuota1, lblMonthQuota2 etc
b. Text -> The month name – starting with January for the first and then lastly December for the lblMonthQuota12
c. Dock -> Fill d. TextAlign -> MiddleLeft
26. You can copy the labels (before you set the Dock property) by selecting and dragging it into each of the cells. You can then multi-select each of these cells and then change the properties for all the same values – the Name and Text properties will have to be set individually though.
27. Next, we want a control to allow the user to capture a numeric value only – with the standard Microsoft controls we have two options, the MaskedTextBox or the NumericUpDown. In this case, we’ll use the NumericUpDown since the MaskEdit is one of the worst controls I have ever used. Anyway, you will need to add 12 of these controls, in the cells in column 2 and 4 and then set these properties:
a. Name -> txtMonthQuota and the month number, txtMonthQuote1, txtMonthQuota2 b. ThousansSeparator -> True c. TextAlign -> Right d. Maximum -> 99999
28. We also want to make sure that this function is usable with just a keyboard, we want to set the TabIndex for each control – typically you would start from 0 and work your way through all the controls. Remember that a container type control, like a GroupBox’s
58
controls will start a position 0 again as they follow the tab index inside of their own container.
29. The quickest way to set the tab index is to select the Form, then on the Eclipse menu, select Design -> Tab Order. You then click on each control (including labels or other non-tab-stop controls) until you are done, then again click on the Design menu and again on Tab Order to stop recording the new tab order.
30. If we run the form at this stage, we only see the data in the grid still, we still need to BIND the controls with the bindingsource.
31. We need to set the Text property on the DataBindings property category. Do this for each of the text controls on the form. NOTE: For the NumericUpDown control, set the VALUE property.
32. Add buttons to the top of the form for Add, Delete, Update, Save and Cancel. Once done, create a trigger for the Click event. Name each of these events OnClickAdd, OnClickDelete, OnClickUpdate, OnClickSave and OnClickCancel. **This is ugly, but will show you how it all works
33. We also do not want to allow a user to just type into a field at any stage, we want to enable the controls only when they are entering in some update mode like add or update. To make the code neater, we’ll create methods to get this done for us.
a. Create a new PRIVATE VOID method called AssignFieldsEnabledState that takes a single input parameter of type LOGICAL and call it pEnable:
59
34. We will now call this method in the OnLoadForm method to disable the fields initially when the user enters the functions. Pass FALSE as the enable state
60
35. We also want the Buttons to be aware of the state the function is in – we only want to enable the appropriate buttons at the right stages. To do this, create another new PRIVATE VOID method called AssignButtonState that also takes one input parameter of type LOGICAL and call it pEnabledState.
36. We then also want to call this method in the OnLoadForm method to set the default
button state.
61
37. Let us first tackle the Update functionality. What needs to happen when we Update is that we want to enable all the fields, but in addition to this, we need to tell the Dataset that it needs to track what changes the user is making to the data – this is required so that we can send this data back to the database and commit the changes. To do this, open the method OnClickUpdate and add the following code:
38. Next add the following for the OnClickAdd:
62
39. Next, we want a single method for save that we can call from both Delete or Save. Create a new PRIVATE method that RETURNS a LOGICAL:
63
40. Now, add code in the OnClickSave:
64
41. Next is the OnClickDelete:
42. The following is the code for the OnClickCancel:
65
43. We now just need to handle the save in the server-side code. Open the
SalesRepDataAccess.p program in the PUG2020Server project (you can use Ctrl+Shift+R) to find a program in your workspace.
66
67
44. And the following section is the validation called from the SaveChanges procedure:
45. Save it all, make sure it compiles and then run it. 46. You should be able to Add, Update and Delete salesrep data and see it commit the
changes to the database.
68
69
The remainder of the workshop is to assist delegates with completing the maintenance function and share ideas.
CREATE THE MAINTENANCE FUNCTION FOR THE ‘BENEFITS’ TABLE