Anvil of Time https://anvil-of-time.com Nature Forges Everything on the Anvil of Time Thu, 06 May 2021 16:50:35 +0000 en-US hourly 1 100 Days of PowerBuilder – Tutorial – Day 9 – Window Methods https://anvil-of-time.com/powerbuilder/100-days-of-powerbuilder-tutorial-day-9-window-methods/ https://anvil-of-time.com/powerbuilder/100-days-of-powerbuilder-tutorial-day-9-window-methods/#comments Thu, 06 May 2021 16:50:35 +0000 https://anvil-of-time.com/?p=2311 This is part of my project ‘100 Days of PowerBuilder’ which is a series of discussions focused on basic PowerBuilder development.

Note: This article is written with examples created in PowerBuilder 2019 R3. Most steps/examples will be identical with any version going back to 9.

Before we go much further we need to think about common functionality within our application.  One advantage of Object Oriented Programming is the ability to reuse code which not only makes development faster but also aids in understanding and maintaining the application ‘down the road’.  So lets think about some common methods/processes for windows.

Previously we created the ‘we_postopen’ event which is posted by the window open event  To enhance our common window process flow we are expanding on this.

So now to modify the existing window.  Add the we_preopen, we_close, we_validate, we_savechange, we_save, we_cancelchanges, and we_cancel events to w_main.

Change the code in the Open event from this

THIS.PostEvent("we_postopen")

to this

THIS.EVENT TRIGGER we_preopen()

THIS.EVENT POST we_postopen()

I prefer the second syntax for triggering/posting events.

Now in the we_close event add the following

THIS.EVENT TRIGGER we_validate()

THIS.EVENT TRIGGER we_savechange()

THIS.EVENT TRIGGER we_cancel()

Close(THIS)

Hmm, something needs changing here.  We need to change the we_validate event to return a status so we can proceed to either the we_savechange or we_cancel methods.  So open the we_validate event and change the return value from (none) to “integer”

and add the following.

//validate
// do some sort of checking to see if values have changed and prompt the user
integer li_rc
li_rc = MessageBox('Save before closing','Save changes?',Question!, YesNoCancel!)
RETURN li_rc

Now go to the we_close event script and change it to:

// close 
integer li_rc
li_rc = THIS.EVENT TRIGGER we_validate()
CHOOSE CASE li_rc
	CASE 1 //yes
		THIS.EVENT TRIGGER we_savechange()
	CASE 2 // no 
                // reset things if needed
		THIS.EVENT TRIGGER we_cancelchanges()
	CASE 3 // cancel
		RETURN //prevents window from closing
END CHOOSE

Close(THIS)

So now we first trigger the we_validate event and return the Messagebox choice value. Based on that value we either save the changes, discard any changes, or cancel the action altogether.

Next remove the code from our earlier exercise from the Closequery event.  Now for demonstration purposes put MessageBox code in both the we_savechange and we_cancelchange events.  Something like this:

Messagebox('Cancel Change','we_cancelchanges')  //change wording for the we_savechange event

Now go to the ‘Exit’ button on the window and change the code to

PARENT.EVENT TRIGGER we_close()

Now when you run the program and click on the ‘Exit’ button you are prompted to save the changes. Run three times and choose each of the options (Yes, No, Cancel). You will see the two messageboxes for either Yes or No and the Messagebox close but the main window remain open for Cancel.

However, now we have to account for the user closing the window via the titlebar or ‘Alt-F4’. Go back to the Closequery event and insert following code:

// closequery
integer li_rc
li_rc = THIS.EVENT TRIGGER we_validate()
CHOOSE CASE li_rc
	CASE 1 //yes
		THIS.EVENT TRIGGER we_savechange()
	CASE 2 // no
		THIS.EVENT TRIGGER we_cancelchanges()
	CASE 3 // cancel
		RETURN 1 //prevents window from closing
END CHOOSE
RETURN 0

Notice this code isn’t much different from we_close.

Now change the code in we_save to the following;

// save the window then exit. 
// 
//insert save checking logic 

THIS.EVENT TRIGGER we_savechange()
CLOSE(THIS)

So you may wonder why don’t I use functions instead of events for some of this and my answer is that you could.  However, in the next session I will be going over some basic window inheritance and the flexibility of events will be demonstrated.

]]>
https://anvil-of-time.com/powerbuilder/100-days-of-powerbuilder-tutorial-day-9-window-methods/feed/ 1
PowerBuilder Tables Script https://anvil-of-time.com/powerbuilder/powerbuilder-tables-script/ https://anvil-of-time.com/powerbuilder/powerbuilder-tables-script/#respond Fri, 23 Apr 2021 13:39:15 +0000 https://anvil-of-time.com/?p=2307 Here is a script to generate the PowerBuilder ‘cat’ tables for SQL Server (pbcatcol, pbcatedt, pbcatfmt, pbcattbl, pbcatvld).  Depending upon the permissions of the user who is connecting, these may not be created automatically from PB.  Modify to reflect the database user as appropriate (dbo in this script).  Supposedly this is available from the installation files but damn if I could find it.

If you are migrating from one db to another you may need to generate your own scripts for the data depending upon whether or not you added to the tables.

/****** Object:  Table [dbo].[pbcatcol]    Script Date: 4/23/2021 9:28:38 AM ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[pbcatcol](
	[pbc_tnam] [varchar](128) NOT NULL,
	[pbc_tid] [int] NULL,
	[pbc_ownr] [varchar](128) NOT NULL,
	[pbc_cnam] [varchar](128) NOT NULL,
	[pbc_cid] [smallint] NULL,
	[pbc_labl] [varchar](254) NULL,
	[pbc_lpos] [smallint] NULL,
	[pbc_hdr] [varchar](254) NULL,
	[pbc_hpos] [smallint] NULL,
	[pbc_jtfy] [smallint] NULL,
	[pbc_mask] [varchar](31) NULL,
	[pbc_case] [smallint] NULL,
	[pbc_hght] [smallint] NULL,
	[pbc_wdth] [smallint] NULL,
	[pbc_ptrn] [varchar](31) NULL,
	[pbc_bmap] [varchar](1) NULL,
	[pbc_init] [varchar](254) NULL,
	[pbc_cmnt] [varchar](254) NULL,
	[pbc_edit] [varchar](31) NULL,
	[pbc_tag] [varchar](254) NULL
) ON [PRIMARY]
GO
/****** Object:  Table [dbo].[pbcatedt]    Script Date: 4/23/2021 9:28:38 AM ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[pbcatedt](
	[pbe_name] [varchar](30) NOT NULL,
	[pbe_edit] [varchar](254) NULL,
	[pbe_type] [smallint] NULL,
	[pbe_cntr] [int] NULL,
	[pbe_seqn] [smallint] NOT NULL,
	[pbe_flag] [int] NULL,
	[pbe_work] [varchar](32) NULL
) ON [PRIMARY]
GO
/****** Object:  Table [dbo].[pbcatfmt]    Script Date: 4/23/2021 9:28:38 AM ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[pbcatfmt](
	[pbf_name] [varchar](30) NOT NULL,
	[pbf_frmt] [varchar](254) NULL,
	[pbf_type] [smallint] NULL,
	[pbf_cntr] [int] NULL
) ON [PRIMARY]
GO
/****** Object:  Table [dbo].[pbcattbl]    Script Date: 4/23/2021 9:28:38 AM ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[pbcattbl](
	[pbt_tnam] [varchar](128) NOT NULL,
	[pbt_tid] [int] NULL,
	[pbt_ownr] [varchar](128) NOT NULL,
	[pbd_fhgt] [smallint] NULL,
	[pbd_fwgt] [smallint] NULL,
	[pbd_fitl] [varchar](1) NULL,
	[pbd_funl] [varchar](1) NULL,
	[pbd_fchr] [smallint] NULL,
	[pbd_fptc] [smallint] NULL,
	[pbd_ffce] [varchar](18) NULL,
	[pbh_fhgt] [smallint] NULL,
	[pbh_fwgt] [smallint] NULL,
	[pbh_fitl] [varchar](1) NULL,
	[pbh_funl] [varchar](1) NULL,
	[pbh_fchr] [smallint] NULL,
	[pbh_fptc] [smallint] NULL,
	[pbh_ffce] [varchar](18) NULL,
	[pbl_fhgt] [smallint] NULL,
	[pbl_fwgt] [smallint] NULL,
	[pbl_fitl] [varchar](1) NULL,
	[pbl_funl] [varchar](1) NULL,
	[pbl_fchr] [smallint] NULL,
	[pbl_fptc] [smallint] NULL,
	[pbl_ffce] [varchar](18) NULL,
	[pbt_cmnt] [varchar](254) NULL
) ON [PRIMARY]
GO
/****** Object:  Table [dbo].[pbcatvld]    Script Date: 4/23/2021 9:28:38 AM ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[pbcatvld](
	[pbv_name] [varchar](30) NOT NULL,
	[pbv_vald] [varchar](254) NULL,
	[pbv_type] [smallint] NULL,
	[pbv_cntr] [int] NULL,
	[pbv_msg] [varchar](254) NULL
) ON [PRIMARY]
GO
INSERT [dbo].[pbcatedt] ([pbe_name], [pbe_edit], [pbe_type], [pbe_cntr], [pbe_seqn], [pbe_flag], [pbe_work]) VALUES (N'###,###.00', N'###,###.00', 90, 1, 1, 32, N'10')
GO
INSERT [dbo].[pbcatedt] ([pbe_name], [pbe_edit], [pbe_type], [pbe_cntr], [pbe_seqn], [pbe_flag], [pbe_work]) VALUES (N'#####', N'#####', 90, 1, 1, 32, N'10')
GO
INSERT [dbo].[pbcatedt] ([pbe_name], [pbe_edit], [pbe_type], [pbe_cntr], [pbe_seqn], [pbe_flag], [pbe_work]) VALUES (N'###-##-####', N'###-##-####', 90, 1, 1, 32, N'00')
GO
INSERT [dbo].[pbcatedt] ([pbe_name], [pbe_edit], [pbe_type], [pbe_cntr], [pbe_seqn], [pbe_flag], [pbe_work]) VALUES (N'MM/DD/YY', N'MM/DD/YY', 90, 1, 1, 32, N'20')
GO
INSERT [dbo].[pbcatedt] ([pbe_name], [pbe_edit], [pbe_type], [pbe_cntr], [pbe_seqn], [pbe_flag], [pbe_work]) VALUES (N'DD/MM/YY', N'DD/MM/YY', 90, 1, 1, 32, N'20')
GO
INSERT [dbo].[pbcatedt] ([pbe_name], [pbe_edit], [pbe_type], [pbe_cntr], [pbe_seqn], [pbe_flag], [pbe_work]) VALUES (N'MM/DD/YYYY', N'MM/DD/YYYY', 90, 1, 1, 32, N'20')
GO
INSERT [dbo].[pbcatedt] ([pbe_name], [pbe_edit], [pbe_type], [pbe_cntr], [pbe_seqn], [pbe_flag], [pbe_work]) VALUES (N'DD/MM/YYYY', N'DD/MM/YYYY', 90, 1, 1, 32, N'20')
GO
INSERT [dbo].[pbcatedt] ([pbe_name], [pbe_edit], [pbe_type], [pbe_cntr], [pbe_seqn], [pbe_flag], [pbe_work]) VALUES (N'DD/MMM/YY', N'DD/MMM/YY', 90, 1, 1, 32, N'20')
GO
INSERT [dbo].[pbcatedt] ([pbe_name], [pbe_edit], [pbe_type], [pbe_cntr], [pbe_seqn], [pbe_flag], [pbe_work]) VALUES (N'JJJ/YY', N'JJJ/YY', 90, 1, 1, 32, N'20')
GO
INSERT [dbo].[pbcatedt] ([pbe_name], [pbe_edit], [pbe_type], [pbe_cntr], [pbe_seqn], [pbe_flag], [pbe_work]) VALUES (N'JJJ/YYYY', N'JJJ/YYYY', 90, 1, 1, 32, N'20')
GO
INSERT [dbo].[pbcatedt] ([pbe_name], [pbe_edit], [pbe_type], [pbe_cntr], [pbe_seqn], [pbe_flag], [pbe_work]) VALUES (N'HH:MM:SS', N'HH:MM:SS', 90, 1, 1, 32, N'30')
GO
INSERT [dbo].[pbcatedt] ([pbe_name], [pbe_edit], [pbe_type], [pbe_cntr], [pbe_seqn], [pbe_flag], [pbe_work]) VALUES (N'HH:MM:SS:FFF', N'HH:MM:SS:FFF', 90, 1, 1, 32, N'30')
GO
INSERT [dbo].[pbcatedt] ([pbe_name], [pbe_edit], [pbe_type], [pbe_cntr], [pbe_seqn], [pbe_flag], [pbe_work]) VALUES (N'HH:MM:SS:FFFFFF', N'HH:MM:SS:FFFFFF', 90, 1, 1, 32, N'30')
GO
INSERT [dbo].[pbcatedt] ([pbe_name], [pbe_edit], [pbe_type], [pbe_cntr], [pbe_seqn], [pbe_flag], [pbe_work]) VALUES (N'MM/DD/YY HH:MM:SS', N'MM/DD/YY HH:MM:SS', 90, 1, 1, 32, N'40')
GO
INSERT [dbo].[pbcatedt] ([pbe_name], [pbe_edit], [pbe_type], [pbe_cntr], [pbe_seqn], [pbe_flag], [pbe_work]) VALUES (N'DD/MM/YY HH:MM:SS', N'DD/MM/YY HH:MM:SS', 90, 1, 1, 32, N'40')
GO
INSERT [dbo].[pbcatedt] ([pbe_name], [pbe_edit], [pbe_type], [pbe_cntr], [pbe_seqn], [pbe_flag], [pbe_work]) VALUES (N'MM/DD/YYYY HH:MM:SS', N'MM/DD/YYYY HH:MM:SS', 90, 1, 1, 32, N'40')
GO
INSERT [dbo].[pbcatedt] ([pbe_name], [pbe_edit], [pbe_type], [pbe_cntr], [pbe_seqn], [pbe_flag], [pbe_work]) VALUES (N'DD/MM/YYYY HH:MM:SS', N'DD/MM/YYYY HH:MM:SS', 90, 1, 1, 32, N'40')
GO
INSERT [dbo].[pbcatedt] ([pbe_name], [pbe_edit], [pbe_type], [pbe_cntr], [pbe_seqn], [pbe_flag], [pbe_work]) VALUES (N'DD/MMM/YY HH:MM:SS', N'DD/MMM/YY HH:MM:SS', 90, 1, 1, 32, N'40')
GO
INSERT [dbo].[pbcatedt] ([pbe_name], [pbe_edit], [pbe_type], [pbe_cntr], [pbe_seqn], [pbe_flag], [pbe_work]) VALUES (N'JJJ/YY HH:MM:SS', N'JJJ/YY HH:MM:SS', 90, 1, 1, 32, N'40')
GO
INSERT [dbo].[pbcatedt] ([pbe_name], [pbe_edit], [pbe_type], [pbe_cntr], [pbe_seqn], [pbe_flag], [pbe_work]) VALUES (N'JJJ/YYYY HH:MM:SS', N'JJJ/YYYY HH:MM:SS', 90, 1, 1, 32, N'40')
GO
INSERT [dbo].[pbcatedt] ([pbe_name], [pbe_edit], [pbe_type], [pbe_cntr], [pbe_seqn], [pbe_flag], [pbe_work]) VALUES (N'DD/MM/YY HH:MM:SS:FFFFFF', N'DD/MM/YY HH:MM:SS:FFFFFF', 90, 1, 1, 32, N'40')
GO
INSERT [dbo].[pbcatfmt] ([pbf_name], [pbf_frmt], [pbf_type], [pbf_cntr]) VALUES (N'[General]', N'[General]', 81, 0)
GO
INSERT [dbo].[pbcatfmt] ([pbf_name], [pbf_frmt], [pbf_type], [pbf_cntr]) VALUES (N'0', N'0', 81, 0)
GO
INSERT [dbo].[pbcatfmt] ([pbf_name], [pbf_frmt], [pbf_type], [pbf_cntr]) VALUES (N'0.00', N'0.00', 81, 0)
GO
INSERT [dbo].[pbcatfmt] ([pbf_name], [pbf_frmt], [pbf_type], [pbf_cntr]) VALUES (N'#,##0', N'#,##0', 81, 0)
GO
INSERT [dbo].[pbcatfmt] ([pbf_name], [pbf_frmt], [pbf_type], [pbf_cntr]) VALUES (N'#,##0.00', N'#,##0.00', 81, 0)
GO
INSERT [dbo].[pbcatfmt] ([pbf_name], [pbf_frmt], [pbf_type], [pbf_cntr]) VALUES (N'$#,##0;($#,##0)', N'$#,##0;($#,##0)', 81, 0)
GO
INSERT [dbo].[pbcatfmt] ([pbf_name], [pbf_frmt], [pbf_type], [pbf_cntr]) VALUES (N'$#,##0;[RED]($#,##0)', N'$#,##0;[RED]($#,##0)', 81, 0)
GO
INSERT [dbo].[pbcatfmt] ([pbf_name], [pbf_frmt], [pbf_type], [pbf_cntr]) VALUES (N'$#,##0.00;($#,##0.00)', N'$#,##0.00;($#,##0.00)', 81, 0)
GO
INSERT [dbo].[pbcatfmt] ([pbf_name], [pbf_frmt], [pbf_type], [pbf_cntr]) VALUES (N'$#,##0.00;[RED]($#,##0.00)', N'$#,##0.00;[RED]($#,##0.00)', 81, 0)
GO
INSERT [dbo].[pbcatfmt] ([pbf_name], [pbf_frmt], [pbf_type], [pbf_cntr]) VALUES (N'0%', N'0%', 81, 0)
GO
INSERT [dbo].[pbcatfmt] ([pbf_name], [pbf_frmt], [pbf_type], [pbf_cntr]) VALUES (N'0.00%', N'0.00%', 81, 0)
GO
INSERT [dbo].[pbcatfmt] ([pbf_name], [pbf_frmt], [pbf_type], [pbf_cntr]) VALUES (N'0.00E+00', N'0.00E+00', 81, 0)
GO
INSERT [dbo].[pbcatfmt] ([pbf_name], [pbf_frmt], [pbf_type], [pbf_cntr]) VALUES (N'm/d/yy', N'm/d/yy', 84, 0)
GO
INSERT [dbo].[pbcatfmt] ([pbf_name], [pbf_frmt], [pbf_type], [pbf_cntr]) VALUES (N'd-mmm-yy', N'd-mmm-yy', 84, 0)
GO
INSERT [dbo].[pbcatfmt] ([pbf_name], [pbf_frmt], [pbf_type], [pbf_cntr]) VALUES (N'd-mmm', N'd-mmm', 84, 0)
GO
INSERT [dbo].[pbcatfmt] ([pbf_name], [pbf_frmt], [pbf_type], [pbf_cntr]) VALUES (N'mmm-yy', N'mmm-yy', 84, 0)
GO
INSERT [dbo].[pbcatfmt] ([pbf_name], [pbf_frmt], [pbf_type], [pbf_cntr]) VALUES (N'h:mm AM/PM', N'h:mm AM/PM', 84, 0)
GO
INSERT [dbo].[pbcatfmt] ([pbf_name], [pbf_frmt], [pbf_type], [pbf_cntr]) VALUES (N'h:mm:ss AM/PM', N'h:mm:ss AM/PM', 84, 0)
GO
INSERT [dbo].[pbcatfmt] ([pbf_name], [pbf_frmt], [pbf_type], [pbf_cntr]) VALUES (N'h:mm:ss', N'h:mm:ss', 84, 0)
GO
INSERT [dbo].[pbcatfmt] ([pbf_name], [pbf_frmt], [pbf_type], [pbf_cntr]) VALUES (N'm/d/yy h:mm', N'm/d/yy h:mm', 84, 0)
GO
SET ANSI_PADDING ON
GO
/****** Object:  Index [pbcatc_x]    Script Date: 4/23/2021 9:28:38 AM ******/
CREATE UNIQUE NONCLUSTERED INDEX [pbcatc_x] ON [dbo].[pbcatcol]
(
	[pbc_tnam] ASC,
	[pbc_ownr] ASC,
	[pbc_cnam] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
GO
SET ANSI_PADDING ON
GO
/****** Object:  Index [pbcate_x]    Script Date: 4/23/2021 9:28:38 AM ******/
CREATE UNIQUE NONCLUSTERED INDEX [pbcate_x] ON [dbo].[pbcatedt]
(
	[pbe_name] ASC,
	[pbe_seqn] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
GO
SET ANSI_PADDING ON
GO
/****** Object:  Index [pbcatf_x]    Script Date: 4/23/2021 9:28:38 AM ******/
CREATE UNIQUE NONCLUSTERED INDEX [pbcatf_x] ON [dbo].[pbcatfmt]
(
	[pbf_name] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
GO
SET ANSI_PADDING ON
GO
/****** Object:  Index [pbcatt_x]    Script Date: 4/23/2021 9:28:38 AM ******/
CREATE UNIQUE NONCLUSTERED INDEX [pbcatt_x] ON [dbo].[pbcattbl]
(
	[pbt_tnam] ASC,
	[pbt_ownr] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
GO
SET ANSI_PADDING ON
GO
/****** Object:  Index [pbcatv_x]    Script Date: 4/23/2021 9:28:38 AM ******/
CREATE UNIQUE NONCLUSTERED INDEX [pbcatv_x] ON [dbo].[pbcatvld]
(
	[pbv_name] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
GO
]]>
https://anvil-of-time.com/powerbuilder/powerbuilder-tables-script/feed/ 0
100 Days of PowerBuilder – Tutorial – Day 8 – Adding a Menu https://anvil-of-time.com/powerbuilder/100-days-of-powerbuilder-tutorial-day-8-adding-a-menu/ https://anvil-of-time.com/powerbuilder/100-days-of-powerbuilder-tutorial-day-8-adding-a-menu/#comments Wed, 07 Apr 2021 17:46:38 +0000 https://anvil-of-time.com/?p=2285 This is part of my project ‘100 Days of PowerBuilder’ which is a series of discussions focused on basic PowerBuilder development.

Note: This article is written with examples created in PowerBuilder 2019 R3. Most steps/examples will be identical with any version going back to 9.

Let’s take a look at menus.  We will create a simple menu for our main application window created earlier.  Choose File – New – PB Object – Menu then click OK.

Now you have your newly created menu object called ‘untitled0’.  Notice the Menu Style on the Appearance tab of the Properties is ‘traditionalmenu!’; change this to contemporarymenu! to open up the various other options.

 

Now save the menu to the ‘firstapp’ pbl as m_main.

Right click on the ‘m_main’ menu item and choose Insert Submenu Item; give it the name ‘File’.

Then save the menu.  Now associate the menu with the main window of the application by opening the w_main object and clicking on the elipsis button next to the MenuName property.  

Now save the window and run the application.  You should see the menu:

Now granted the menu doesn’t do anything at all now but at least we know we have correctly associated it with the window.  Go back and open the menu painter.  Right click on the ‘File’ menu item and choose Insert Submenu Item.  Give it the name ‘Save’.   Once the control loses focus notice the visual example and the Name property are changed to reflect the menu item text.

Now associate a menu icon with this option by clicking on the elipsis button next to MenuImage.

Then choosing PB menu or toolbar icons

Search for ‘Save’ and then select the Save1! image, click OK, then save the menu. Open the w_main window again and add an event called ‘we_save’ and insert the following:

// save the window then exit.
//
//insert save checking logic
close(THIS)

Save the window and go back the the menu. In the clicked event of the Save menu option place the following:

parentwindow.triggerevent "we_save"

Now run the application.  From the main menu click on ‘File’ and then ‘Save’.  You should be prompted for closing the window (same functionality as from the earlier window example). Now reopen the w_main window, go to the event list and delete the ‘we_save’ event and then save the window.

Now run the application and choose Save from the File menu.  Nothing should happen now.  This shows how the triggerevent method on menus can be used to create generic functionality.  Non existing events can be triggered in PowerScript without the application crashing as would be the case of a function call.

Using Menu Methods

I consider it best practice to use menus primarily to trigger events on their parent windows.  Although menus can have their own methods to do whatever processing is desired,  I don’t believe there are any compelling reasons to architect an application this way  

]]>
https://anvil-of-time.com/powerbuilder/100-days-of-powerbuilder-tutorial-day-8-adding-a-menu/feed/ 1
100 Days of PowerBuilder – Tutorial – Day 7 – Opening a Window https://anvil-of-time.com/powerbuilder/100-days-of-powerbuilder-tutorial-day-7-opening-a-window/ https://anvil-of-time.com/powerbuilder/100-days-of-powerbuilder-tutorial-day-7-opening-a-window/#comments Tue, 30 Mar 2021 21:13:00 +0000 https://anvil-of-time.com/?p=2250 This is part of my project ‘100 Days of PowerBuilder’ which is a series of discussions focused on basic PowerBuilder development.

Note: This article is written with examples created in PowerBuilder 2019 R3. Most steps/examples will be identical with any version going back to 9.

Although we have already opened the main window for our application earlier, there are some things to consider when opening windows. One of the chief concerns is the ‘responsiveness’ of the window. By this I mean what does the user see when the window opens. Does it sit there with a ‘busy’ type icon spinning away and then open? Let’s conduct an experiment.

Open the w_main form for our application and place a Multi-Line Edit control on it.

Expand it so you can see a fair amount of text and make sure the Horizontal Scrollbar property is checked. Now some code in the Open event of the window.

long ll_i, ll_et
string ls
time lt_1, lt_2< httpclient lnv_http
lnv_http = CREATE httpclient lt_1 = Now() // start time
// connect to the web service and send the request
ll_i = lnv_http.sendrequest('Get',"http://worldtimeapi.org/api/timezone/america/new_york")
IF ll_i <> 1 THEN
messagebox('http','request failed')
RETURN
END IF
// get the JSON response from the web service
lnv_http.getresponsebody(ls)
// end time
lt_2 = Now() ll_et = SecondsAfter(lt_1, lt_2)
mle_1.text = 'Start: ' + string(lt_1) + '~r~nEnd: ' + string(lt_2) + &
'~r~nElapsed: ' + String(ll_et) + '~r~n~r~nResponse: ' + ls IF IsValid(lnv_http) THEN DESTROY lnv_http

So what this code does is connect to a web service to get the current time in New York City and then puts the response JSON text into the MLE control.  Prior to connecting to the web service it gets the time and also gets the time after the call so we can see how long the ’round trip’ took.  This information is also put into the MLE text property.

Now when you run this you will get various response times based on your connection speed and latency of the web service (amongst other various things).  Regardless of the speed the window did not open until the code in the open event concluded which makes the application appear unresponsive.

To ‘improve’ the responsiveness of the application we are going to create a separate event on the window; this is known as a ‘user event’.  Click on the dropdown arrow next to the event name ‘Open’, scroll up and choose ‘(new event)’.

Now give it the name ‘ue_postopen’ (notice the top dropdown is updated when you tab off the name field.  In keeping with PB conventions the event name is prefaced with “ue_” to indicate it is a user event – you could also name it “we_” since it is a window event.  In any case none of the other parameters/etc. are changed.

Now cut the code from the ‘Open’ event and paste it here.

In the now empty ‘Open’ event place this line:

EVENT POST ue_postopen()

Or alternatively:

THIS.PostEvent("ue_postopen")

Now when you ‘post’ an event or function call it is placed onto the call stack and then executed once the current method is completed (or after other events and functions if they are already on the call stack since it is a ‘first in, first out’ type of arrangement).

When you run the application now you will see the window opens immediately and then some time passes before the MLE text is updated.  In my testing it took anywhere from 2 to 30 seconds (the default timeout).  Depending upon the situation you may have additional information messages show in the application screen so that the user understands that something is going on behind the scenes.

This type of post open functionality is normally part of most window ancestors and it used whenever any potentially long process is performed as part of the initialization of the object.  These include database calls (populating datawindows/datastores), retrieving network resources (files, images, etc.) or really anything which may take more than a second or so.

]]>
https://anvil-of-time.com/powerbuilder/100-days-of-powerbuilder-tutorial-day-7-opening-a-window/feed/ 1
100 Days of PowerBuilder – Tutorial – Day 6 – Adding Controls to a Form https://anvil-of-time.com/powerbuilder/100-days-of-powerbuilder-tutorial-day-6-adding-controls-to-a-form/ https://anvil-of-time.com/powerbuilder/100-days-of-powerbuilder-tutorial-day-6-adding-controls-to-a-form/#respond Wed, 24 Mar 2021 23:36:00 +0000 https://anvil-of-time.com/?p=2214 This is part of my project ‘100 Days of PowerBuilder’ which is a series of discussions focused on basic PowerBuilder development.

Note: This article is written with examples created in PowerBuilder 2019 R3. Most steps/examples will be identical with any version going back to 9.

Lets add some additional controls to our main form and check out some of the functionality of the PowerBuilder IDE. Once you have opened the main window object, choose Insert – Control from the menu.

Now I’m sure there is some sort of organizational structure behind how the entries here are listed but I haven’t taken the time to figure it out.  You can also click on the down arrow icon next to the control icon on the toolbar.

The icons within the red rectangle are the controls you can choose from.  Like many places in the IDE, the toolbar icon will take on the image from the last control chosen.  Each of the controls is assigned a default name when it is placed on the form.  The name consists of a defalt prefix and a sequence number.  You can control the prefix from within the Options dialog (menu option ‘Design – Options’).  When opening this dialog you are brought to the ‘General’ tab.

Here you can control the alignment grid (which appears on the window control) and whether controls default to a ‘3D’ appearance.

The prefixes are found on the next two tabs.

You can change these as you wish but each control must have a unique name when placed on a form within the IDE.

The Script tab defines some script editor behavior (auto indent, dashes) and controls what type of messages appear when the script is compiled (when saved or when you leave the particular script). The Event and Function List Order controls how these lists appear in the editor.

These tabs control the fonts within the script editor and when you choose to print out a script or control. Obviously some are more appropriate than others and are affected by the screen resolution of your monitor.

Coloring refers to how various statements, keywords, and etc. appear in the editor.

AutoScript is similar to Microsoft’s Intellisense where the IDE ‘guesses’ at what you are doing based on what you type in. You will need ot experiment with this to get the optimal settings for your style of programming.

So now select a SingleLineEdit (SLE) control to drop on your form.

Drop a couple more for a total of three.

The last control is ‘selected’ since it has four ‘handles’ in its corners. When an object is selected the Properties window shows information about it. Each type of object will have differing properties.

The Font tab is used to set typeface related properties of the control. Note that if you change the TextSize the size of the control does not change; you will have to adjust it yourself.

The Other tab in this case is primarily used for positional changes. Note that (nearly) all properties of controls can be manipulated from within the code of the application.

Here’s a trick to saving time when placing a like number of controls on a form. If you want to change the Font, Color, Height, etc. of a control from the defaults PB gives you when you drop it on the form, change a single copy first, select it, choose the same type of control from the menu, and then drop it on the form. The new control will have some of the same properties already assigned to the one you had selected prior to dropping it.

You can also achieve this by right clicking on a control and then choosing ‘duplicate’ from the options menu.

Manipulating Control Groups

You can select multiple controls and manipulate them as a group should you so desire. Simply hold the Control key and click each control. If you selected one too many simply Control click to unselect it. Once a group of controls is selected, the Properties window shows common elements amongst the group. If all items in a group are of the same type, all the properties are available. If multiple controls are selected (say, SingleLineEdit and CommandButton) then a smaller subset of properties are available.

Group Selected Properties (all Single Line Edit controls)

Group Selected Properties for SingleLineEdit and CommandButton

You can resize and position object groups by using the Positioning drop down toolbar

Remember that the first control selected in the group serves as the ‘master’; changing position and size will all be relative to the master control. In the case of spacing between objects the first two selected serve as the the ‘master’.

Now we have the fields sized and aligned. When designing a form it’s always important to designate the ‘focus flow’. By this I mean the order in which the controls gain focus as the user tabs through the form. This is set by the Tab Order property and can be displayed from the toolbar icon or menu option.

The tab order icon (or menu) acts as a ‘toggle’ into ‘tab setting mode’ which basically means you can’t do anything other than set the tab order until you toggle back out.

In the case of the form I’ve set up, my tab order is slightly off. The tab stops themselves are displayed in the upper left corner of the control (in red). The actual order on the form is hightlighted by the arrows which have been added to show the actual flow; not the most intuitive.

Clicking on the number of a specified tab order allows it to be changed. To ‘insert’ a control between others on a form, simply use a number between the tab numbers of the controls. Once you toggle out of tab order the tab numbers themselves are recalculated into multiples of ten.

Setting the tab order of the Exit button so it is last.

Now if you run the application, the window opens and focus is set on the first SLE field. Pressing the tab key moves the focus to the subsequent fields and then the Exit button before starting over again at the initial field.

Let’s put some functionality behind the SLE fields. Right Click on the first SLE and choose Script

This takes you into the Powerscript Editor for the Event methods of the control. Clicking on the down arrow as shown will list all of the events for this particular control. The script currently in ‘edit’ mode is highlighted.

In this case the ‘modified’ event is in edit mode. In this event script we will place code to change the background color of the field to indicate that it’s contents have been changed. The following will do this: Backcolor = RGB(255,255,0). RGB is a system function which calculates the long value of a color based on it’s red, green, and blue component values (integers from 0 to 255). In this case we are changing the value to yellow.

Now run the application, change the the contents of the first SLE, and then tab out of the field.

]]>
https://anvil-of-time.com/powerbuilder/100-days-of-powerbuilder-tutorial-day-6-adding-controls-to-a-form/feed/ 0
100 Days of PowerBuilder – Tutorial- Day 5 – Building up the Application Structure https://anvil-of-time.com/powerbuilder/100-days-of-powerbuilder-tutorial-day-5-building-up-the-application-structure/ https://anvil-of-time.com/powerbuilder/100-days-of-powerbuilder-tutorial-day-5-building-up-the-application-structure/#respond Wed, 24 Mar 2021 21:51:00 +0000 https://anvil-of-time.com/?p=2198 This is part of my project ‘100 Days of PowerBuilder’ which is a series of discussions focused on basic PowerBuilder development.

Note: This article is written with examples created in PowerBuilder 2019 R3. The same steps would be pretty close with any version going back to 9.

So lets get back to work on our sample application and enhance it’s functionality. Before we do this, lets get some organization to our project. The code created in PowerBuilder is stored in binary files called PowerBuilder Libraries which have the file extension of PBL. These are known as ‘pibbles’ (PBLs). All but the most elementary applications will have more than one of these files. In many instances you will use (or develop) one or more ancestor or framework level PBLs which make development easier, faster, more uniform in apperance and behavior, and (very important) to keep you from re-inventing the wheel over and over again.

In the halcyon days (PB 5 – 6.5) there were many strategies and best practices regarding the number of PBLs to have, how many objects they should contain, how to dynamically switch libraries to improve performance, and etc. Today many of the performance issues are no longer relevant due to OS and hardware improvements made since that time. Establishing a uniform structure to the contents of your PBLs which applies across the applications you develop is much more important in terms of lessening the learning curve for new developers and improving maintenance activities.

Most applications which are at least ten years old have a hodge-podge of PBLs loosely organized at best. Many may have had better structure but over time entropy took control, especially when the initial developers moved on to something else, and there was no longer anything other than individual discipline keeping things organized. The two types of sturctures I advocate are Object Centric and Module Centric.

The Object Centric model organizes libraries by object types. This gives a structure as follows

application.pbl
window.pbl
dataobject.pbl
service.pbl
business.pbl
ancestors

The Application PBL is generally very thin. By this I mean it has only a few objects. These include the Application object, a ‘Splash’ window, the MDI Frame (if its an MDI app), and maybe a specialized ‘About’ window. If the application is PFC based, the global ‘app manager’ object resides here too.

The Window PBL contains all the application specific windows.

The Dataobject PBL contains all the datawindow/datastore objects.

The Service PBL contains application specific functions, non visual objects, and application controls which do not provide business rule interaction. For example, say your application needs to know the date of the first business day after any given date and this type of calculation is needed in several places. You create a non visual object which contains all the code to do this (and any other date related stuff) and it would reside in this PBL. If you have a need to calculate Gross Margin based on various inputs you would place this in the Business PBL.

The Business PBL contains business rule objects. Processes like scrap calculations for materials processing, claims adjudication, inventory transaction processing, etc. go here.

Ancestor/Common object PBL(s) would go last and in whatever order is dictated by the framework.

Depending upon the circumstance, it may be benefitial to create separate PBL files for report dataobjects (if the application has a large number of them). Structures and Functions may be useful as well depending upon how may of these are used.

The point of this is to provide a simple ‘rule of thumb’ for any subsequent developers who have to maintain the application. It’s also easy to find common code across applications with this approach since you would generally be looking at the Service and Business PBLs.

The Module Centric model organizes libraries by application ‘sub units’. This gives a structure which might resemble this.

application.pbl
module1.pbl
module2.pbl
setups.pbl
common.pbl
ancestors

The Application PBL is also pretty thin. It contains the application object, the main window, the ‘About’ window, maybe a global transaction or app manager object.

The Module PBLs are determined by major functionality groups within the application. Examples would be ‘order entry’, ‘scheduling’, ‘payables’, etc. Within each PBL are all, or most, of the objects needed for that functionality group.

The Setups PBL is really a specialized module which groups all the ‘Maintenance’ type activities required for the other modules. These would include code or definition creation/maintenance, rules engine setup, data imports, etc.

The Common PBL contains items needed across multiple modules or applications. These objects could include a base code search window, financial calculations, a dashboard ancestor, etc.

The Ancestor PBL(s) again go last.

This type of structure captures common functionality within the PBL allowing for easier sharing amongst applications. If even a small subset of windows is needed somewhere else, you would only have to include a single PBL in the other application library list. This structure has a more ‘organic’ feel to it and can become very confusing if growth is not managed.

Common Considerations

The PBL files themselves should be named so there are no duplicates amongst all the applications. Generally an application designator is used, e.g., abc_app.pbl, abc_window.pbl, or abc_setups.pbl.

Each PBL should reside in its own folder. This is very important for source control reasons. Name the folder the same as the name of the pbl, i.e., ‘abc_app’ contains the ‘abc_app.pbl’.

Often you will not really know how large a set of objects you will have when you start building an application. This makes setting a limit to the number of objects a PBL contains difficult. If you start to exceed 100 or so objects you might want to create a second library.

What You Don’t Want

MB_MODS9_17.PBL
MB_MODS9_30.PBL
MB_MODS.PBL
WIN95NT.PBL
MAIN.PBL
CEMAIN.PBL
ULSUPPORT.PBL
ULDW.PBL
ULDW2.PBL
MIKE.PBL
PFC.PBL
PURCHASE.PBL
ULDW_2.PBL
OBSOLETE.PBL
ANCMAIN.PBL…

Have fun with this one…

Creating and Adding a Library

Go to File – New – Select the Library Tab, then OK

Click on the Elipsis to open the File Dialog window

Navigate to the appropriate location to hold the new file, create the folder,  name the file, then Save

The new library appears in the Library Treeview.

]]>
https://anvil-of-time.com/powerbuilder/100-days-of-powerbuilder-tutorial-day-5-building-up-the-application-structure/feed/ 0
PowerBuilder – Back Tab to Last Field in DataWindow https://anvil-of-time.com/powerbuilder/powerbuilder-back-tab-to-last-field-in-datawindow/ https://anvil-of-time.com/powerbuilder/powerbuilder-back-tab-to-last-field-in-datawindow/#respond Tue, 23 Mar 2021 23:11:00 +0000 https://anvil-of-time.com/?p=2188 An annoyance in a PB window with a datawindow control on it is if you back tab to the dw, your cursor is positioned to the first field and not the last field. The following technique can eliminate that and make your application a bit more user friendly.

In the Getfocus event of the datawindow:

long ll_col = 1
long ll_pos, ll_x, ll_y, ll_tab
long ll_curr_x, ll_curr_y
string ls_list, ls_piece, ls_column, ls_setting

// user back tabbed to get here
IF Keydown(keyshift!) AND Keydown(keytab!) THEN
	// objects on datawindow
	ls_list = THIS.object.datawindow.objects + '~t'
	DO WHILE ls_list <> ''
		ll_pos = POS(ls_list, '~t')
		IF ll_pos > 0 THEN
			ls_piece = MID(ls_list, 1, ll_pos - 1)
			ls_list = TRIM(MID(ls_list, ll_pos + 1))
			IF TRIM(ls_piece) <> '' THEN
				ls_setting = THIS.Describe(ls_piece + '.Type')
				IF UPPER(ls_setting) = 'COLUMN' THEN // only look at columns
					ls_setting = THIS.Describe(ls_piece + '.Visible')
					//check for visible only	
					IF ls_setting = "1"	THEN			
						ls_setting = THIS.Describe(ls_piece + '.X')
						ll_x = long(ls_setting)
						ls_setting = THIS.Describe(ls_piece + '.Y')
						ll_y = long(ls_setting)
						ls_setting = THIS.Describe(ls_piece + '.TabSequence')
						ll_tab = long(ls_setting) //check tab sequence
						IF ll_tab > 0 AND ((ll_y = ll_curr_y AND ll_x > ll_curr_x) OR (ll_y > ll_curr_y)) THEN
							ll_curr_x = ll_x
							ll_curr_y = ll_y
							ls_column = ls_piece
						END IF
					END IF
				END IF
			END IF
		END IF
	LOOP
	IF TRIM(ls_column) <> '' THEN
		THIS.setrow(long(THIS.Object.DataWindow.LastRowOnPage))
		THIS.setcolumn(ls_column)
	END IF
END IF

On any given datawindow (if you put this in an ancestor) you need to make sure that any columns you place ‘out of view’ are either invisible or have a tab sequence of zero.

]]>
https://anvil-of-time.com/powerbuilder/powerbuilder-back-tab-to-last-field-in-datawindow/feed/ 0
PowerBuilder – Getting the Display Value from a Drop Down DataWindow https://anvil-of-time.com/powerbuilder/powerbuilder-getting-the-display-value-from-a-drop-down-datawindow/ https://anvil-of-time.com/powerbuilder/powerbuilder-getting-the-display-value-from-a-drop-down-datawindow/#respond Thu, 18 Mar 2021 20:51:00 +0000 https://anvil-of-time.com/?p=2182 This is very basic functionality but easily forgotten. Say you have a datawindow built from the ‘part’ table and a column for partType is a DDDW. You want the description of the partType code (which is displayed in the row showing the parts). To do this you use the evaluate method with the describe method on the datawindow.

string ls_desc
ls_desc = dw_parts.describe("evaluate('lookupdisplay(partTypeID)'," + string(ll_row))

Where ‘ll_row’ is the row number in dw_parts datawindow from which you want the description.

]]>
https://anvil-of-time.com/powerbuilder/powerbuilder-getting-the-display-value-from-a-drop-down-datawindow/feed/ 0
100 Days of PowerBuilder – Tutorial – Day 4: A Look at the IDE https://anvil-of-time.com/powerbuilder/100-days-of-powerbuilder-tutorial-day-4-a-look-at-the-ide/ https://anvil-of-time.com/powerbuilder/100-days-of-powerbuilder-tutorial-day-4-a-look-at-the-ide/#comments Wed, 17 Mar 2021 01:54:00 +0000 https://anvil-of-time.com/?p=2139 This is part of my project ‘100 Days of PowerBuilder’ which is a series of discussions focused on basic PowerBuilder development.

Note: This article is written with examples created in PowerBuilder 2019 R3. The same steps would be pretty close with any version going back to 9.

Mastering the PowerBuilder Integrated Development Environment (IDE) is the key to becoming a successful PB developer. As is normally the case with an IDE, there are many ways to accomplish the same task and each developer will create their own processes to do so. In this discussion we will go over some common configuration changes which should enhance productivity within the PB IDE.

Here again is the standard screen normally presented after PowerBuilder is installed and run for the first few items. Now it’s time to banish this since we no longer need it. Select the ‘Reload last workspace…’ option and unselect the ‘Show this dialog…’ and then close the window.

Now from here we have selected our ‘100day’ target and opened the ‘w_main’ object in the IDE. The system treeview on the left is a nice navigational tool but it is always on top. Sure we can resize it or move it to another place but we still have the issue that it reduces the working area within the IDE. Close this frame altogether.

While we are at it, lets set up some system options to make things a bit more friendly. Open the System Options dialogue.

Unselect the ‘Automatically Clear Output…’ option. This is especially important when searching through the code within the IDE. If you leave this option checked, previous search results are cleared; if you have a photographic memory then leave it checked. Select the ‘Disable database connection when compiling…’ option. This prevents the IDE from attempting to connect to the default database when performing a build (normally this is not important).

On the Workspaces tab you should have the ‘Reopen workspace…’ option checked.

Remember to click OK to save these changes.

Getting back to the IDE we see that the working area is fairly large now and encompasses almost all of the space within the IDE. To view our workspace and the associated object we need to bring up the Library Painter. Choose the Library Icon from the toolbar.

Notice the default location is My Computer which is of limited use. The library painter is very similar to the Explorer window in Windows. The left treeview is for navigation and the right is the detail. Right click on the treeview on the right.

Choose the Set Root option and then ‘Current Workspace’

Now when you open the Library Painter you see the Workspace treeview to the left and the current object pane on the left.

While in the Library Painter we can set some options, first on the General Tab

and then in the Include Tab.

I normally don’t make any changes here to the defaults.

Now when we click on an object to open it we see that the script painter opens much larger and is on top of the library painter.

So if we take a closer look at the Script Painter for our window we have created we have the basic layout as shown.

Along the bottom of the ‘working area’ are a series of tabs.

These show the visual layout of the object, the current script (in our case the closequery event script), the list of Events, the list of Functions, and the Declare Instance Variables script.

On the right side of the working area is the Properties pane.

Along the top are a series of tabs showing various properties. These tabs (and properties) change based on the object selected in the Layout tab of the window or from the Control list (shown below).

Along the bottom of the Properties pane are tabs for the Properties, the Control List, and the Non-Visual Object List

If you right click within the General properties area on the Properties tab you can change the layout of the tabs. The default is Labels on Top.

Here is the appearance when Labels On Left is chosen.

In this mode the information is shown in less space.

The Control List tab for our sample application looks like this.

You can select another control by clicking on it from the list. In this case we have two controls, the main window and the exit button.

You can control the various panes shown in the script painter from the View menu option. Here we are choosing to have the Event List in its own pane.

Since our system option is to have Horizontal Windows dominate the event list shows on the bottom. Not too helpful in my book.

Move the pointer over the separation bar of the Event List and the title bar appears. From this point you can ‘pin’ the titlebar by clicking on the pushpin icon.

Drag on the titlebar with the pointer to change the location of this pane. Move it to the tab area below the Properties pane (or some other location). You will see a shadow box appear indicating the location will receive the dropped pane.

Now the Properties pane is ‘split’ into two with the newly dragged Event List on the bottom.

Do the same with the Function List so now you have both lists (as tabs) underneath the Properties pane. You can now experiment with changing the Properties pane width to give your script pane the most area possible.

If you really screw things up or just want to start over, go to the View menu, choose Layouts and then (Default). You can also save layouts here once you come up with one or more you really like.

]]>
https://anvil-of-time.com/powerbuilder/100-days-of-powerbuilder-tutorial-day-4-a-look-at-the-ide/feed/ 1
100 Days of PowerBuilder – Tutorial – Day 3: Window Properties https://anvil-of-time.com/powerbuilder/100-days-of-powerbuilder-tutorial-day-3-window-properties/ https://anvil-of-time.com/powerbuilder/100-days-of-powerbuilder-tutorial-day-3-window-properties/#respond Wed, 17 Mar 2021 01:51:00 +0000 https://anvil-of-time.com/?p=2128 This is part of my project ‘100 Days of PowerBuilder’ which is a series of discussions focused on basic PowerBuilder development.

Note: This article is written with examples created in PowerBuilder 2019 R3. The same steps would be pretty close with any version going back to 9.

Continuing with the window we created on Day 2 (w_main), lets look at some of the various properties associated with it. Locate the window on the system tree and double click on it. This should ‘open’ it in what is called a ‘painter’. I call this type an ‘Edit Painter’ but it is also referred to as a ‘Window Painter’ since the object we are working on is a window.

Painters have a ‘view’ menu that can be used for opening various views. Each painter has a default layout of views which can be rearranged or hidden to suit your needs. You are able to save your personal layout of views using the ‘View – Layouts – Manage’ menu option. Remember ‘View – Layouts – (Default)’ option as this can save you a bunch of frustration when you jumble things up and close stuff you really want to see.

Anyway, we are looking for the Properties View of our window. In the default layout it is on the right side of the IDE and should look something like this:

Title – the title of the window; normally displayed in the window’s TitleBar
set in code: w_main.title = ‘100 Days of PowerBuilder’

Tag – this is often used to set the ‘microhelp’ area of a MDI frame. It can also be used to provide custom functionality based on it’s content. To set in code: w_main.tag = ‘Main application window’

MenuName – the name of the menu object associated with the window

Visible – can the window be seen?

Enabled – controls whether the window can have focus. boolean value (TRUE/FALSE) If this is FALSE (not checked) and is the main starting window of your application, when you run the app nothing will happen.

TitleBar – always TRUE for Main widow types.

ControlMenu – controls whether the Control Menu box displays in the TitleBar

MaxBox – controls whether the Maximize box displays in the TitleBar

MinBox – controls whether the Minimize box displays in the TitleBar

ClientEdge – Does the client area of the window appear sunken within it’s frame

PaletteWindow – used for Popup windows.

ContextHelp – used for Response windows.

RightToLeft – if the operating system supports right to left display, specifies how the characters should be displayed.

Center – causes the window to be centered when created

Resizable – is the window able to be resized?

Border – does the window have a border (TRUE if window type Main).

WindowType – This is set at design time and cannot be changed during runtime.

WindowState – specifies the window state when opened (Max, Min, or as designed)

BackColor – the background color

MDIClient Color – only for MDI frames

Icon – the icon displayed in the titlebar.

Transparency (%) – 0 – 100 value with 0 being totally opaque

OpenAnimation – how the window opens

CloseAnimation – how the window closes

AnimationTime(ms) – animation time in miliseconds – related to Open/Close animations

WindowDockState – relates to MDI applications and how the window appears

The Scroll tab contains settings used if the windows display area is smaller than the layout of the controls within it. The HScrollBar and VScrollBar settings allow the user to move around to see all of the window contents if needed.

The Toolbar tab is used to control the Toolbar in MDI frame windows.

These settings control the windows dimensions, initial position on the screen, pointer image, and user accessibility.

Position

X – The horizontal coordinate of the window where the upper right corner of the display is zero (0).

Y – The vertical coordinate of the window where the upper right corner of the display is zero (0).

Width – The width of the window. This dimension starts from the X position.

Height – The height of the window. This dimension starts from the Y position.

Pointer

Pointer – The image displayed when the pointer is over the window.

Accessibility

These properties are used when designing applications in compliance with various standards regarding use by individuals with disabilities. More information can be found in the Appeon PowerBuilder Help Guide.

As of PowerBuilder 2019 R2 Docking is obsolete.

Notes on the above properties

The various properties which have check boxes next to them are all boolean values (TRUE/FALSE).

Many of the drop down lists show Enumerated Values which are specific to PowerBuilder.

All window measurements are in PowerBuilder units (PBUs). A PBU is defined in terms of logical inches. The size of a logical inch is defined by the operating system (OS) as a specific number of pixels. The number is dependent on the display device. Windows typically uses 96 pixels per logical inch for small fonts and 120 pixels per logical inch for large fonts.

The window coordinates system can take a bit of getting used to since the Y position is opposite of what is taught when you learn about Cartesian coordinates. Also remember that quite often you will need to convert positions into Pixels when interacting with the OS; naturally there are methods to convert PBUs to Pixels and vice versa.

For image file designations in general you can choose items from folders available to your PC during design time. When you do this, PowerBuilder will insert the full path into the specified setting (icon, pointer, etc.). You should always remove the file path from the entry, leaving only the file name. Any graphic resources should be placed in a common folder which is contained within the PATH on your PC. If you do not do this and someone else runs your application who has a different folder structure (or the file is no longer in the location specified), these images will be missing (some will display as a red X or just be blank).

These properties can easily be experimented with by changing them in the painter, saving, then running the application. You can even set the window to be invisible or disabled – just be prepared to bring up the task manager and kill the app since you won’t be able to get to the window.

]]>
https://anvil-of-time.com/powerbuilder/100-days-of-powerbuilder-tutorial-day-3-window-properties/feed/ 0