The BAPBUG Journal
Issue #1, March 1998
Welcome from our President
by Debbie Arczynski
How Not to Close a Window
by Greg Dzingeleski
Datawindow Ini File Service
by Greg Dzingeleski
Dynamically Changing Printers
by Susan Galli, CPD
Event Driven
Programming in PowerBuilder
By Fred Grau, CPD
Advanced Variable Declarations
by Rik Brooks
Welcome to the first issue of the Baltimore Area PBUG newsletter. This newsletter is
YOUR newsletter! We hope that it is stimulating and helps you in your quest for the answer
to the ultimate PowerBuilder question that you have on your mind! By the way,what is your
question? We are going to include in each issue a Technical Advisors Corner
in which your question can be answered, but we need to know what your question is. You can
forward all questions to any of the technical advisors. Their phone numbers and email
addresses are listed in the newsletter, along with all of the BAPBUG officers.
Please feel free to contact any of the officers if you have suggestions of topics for our
bi-monthly meetings, concerns with the running/editing of the meetings or newsletter, or
just comments in general. We also want to encourage you to submit small, medium, or large
articles of interest to the PowerBuilder community. We would like to encourage you to
submit any "PowerBuilder tips" that have helped you along your way and may help
others, such as:
If a drop-down child datawindow has a retrieval argument, and the retrieve on the child retrieves no records, PB will display a dialog box, prompting for the retrieval argument. To avoid this problem, insert a blank record into the child datawindow before the retrieve.
We want to thank Greg Dzingeleski for volunteering to take on the enormous task of organizing and putting together this newsletter. Thanks Greg!
We hope to see you at our next meeting in April, and hope to hear from you soon!
Debbie Arczynski
February Meeting
Many thanks to Mark Pfeifer for presenting Silverstream, Bill Bitman for presenting
the Window Plug-In, and Fred Grau for presenting Non-Visual Objects. If you would like any
more information about any of these topics, please feel free to contact them. Their phone
numbers and email addresses are listed in this newsletter.
Next Meeting
Our next meeting is Wednesday, April 8th at the OAG building at SSA. The
topics for this meeting will be based on the PowerBuilder DataWindow. Mark Pfeifer will be
presenting DataWindow Essentials. Susan Galli will be presenting Beyond
DataWindows. If you have any questions about datawindows, this is the meeting where
all those questions will be answered. Hope to see you there!
Future Meetings
Sybase (Powersoft) is scheduled to give a presentation called "From Here to the
Web" on Wednesday, June 3rd. It will cover all the web enabled tools that
exist in the Powersoft Tool Ensemble. They plan to discuss how to Web enable our existing
PowerBuilder applications. This meeting should be exciting! Please plan to attend.
Interested in speaking at a future user group meeting?
If you are interested in speaking on a particular subject, or would like to demo your
PowerBuilder application that you have been working on, please contact Debbie Arczynski at
(410) 796-9461 (or debbie@charm.net). There are openings for presentations or demos of any
length from 10 minutes to 2 hours. Thanks!
BAPBUG Web Page
Please take a look at our new web page designed by Anne Sola. It has lots of
information about the group itself, the officers, the agenda, events, jobs, and links to
other PB web pages. It is located at: www.geocities.com/siliconvalley/bay/8680. If you
have any suggestions and/or comments, please contact Anne Sola.
How not to close a window
Recently I came upon an error in a program I'm maintaining that highlights interesting
behavior of Powerbuilder script execution. In code similar to that shown in Listing 1 a
null object reference system error occurred when the user clicked on the close button.
This was noticed after the application was migrated from Powerbuilder 4 to Powerbuilder 5.
The interesting behavior of the close function is that the function will close the window
argument and then continue to execute the script that called the close function, even if
the object that contained the script has gone out of scope. The script being executed is
still retained although it's object is gone. In the code below a system error should alert
the developer there is a problem, but if there was no reference to a null object (cb_ok)
the script would execute and the developer may have no idea that the rest of the script
has executed. But what happens in PB 4? Doing a debug revealed that the script continued
to execute, but PB 4 missed the error and the system error was never triggered. The
obvious solution is to use a return statement after the close(parent) to make the script
stop execution.
Listing 1
long ll_ret
ll_ret=messagebox('Close','Do you want to save?',Question!, YesNo!)
if ll_ret=1 then
close(this)
end if
//disable the ok button if the user didn't want to close
cb_ok.enabled=false
//Update important global variable
gl_imp = 64738
<By Greg Dzingeleski>
DATAWINDOW INI FILE SERVICE
I've taken many of the official Powersoft courses for Powerbuilder, and one thing they
emphasize is using the datawindow (and now the datastore) as much as possible. They even
claim it is useful for 'normal' screens where data is entered but not saved in a database,
such as login screens. I've decided to take them up on this idea and I've tried it in a
couple places to see if there really is an advantage. One of the places I used it is on a
screen for database parameters to go on an ini file (I for one believe in the GUI idea and
I think nobody, even a DBA or other administrator types should have to open up an ini file
with a text editor ever). While I was at it, and being lazy when it comes to coding
tedious things, I thought it would be neat to write a function that would read the
datawindow for me and automatically write to the ini file. This would let me avoid the
tedious dw_1.object.columnname
for every field. I could also write a function to
populate the fields of the datawindow from the ini file. I would only have to make an
external source datawindow and program the functions calls to read and save the data for
the whole datawindow.
I wrote this code to fit in my framework, which is mostly service based. So in my ancestor
datawindow I put an instance variable to refer to the service object u_dw_srv_inifile. I
then made a function to initialize the service. I gave the function one boolean argument,
using true to create the service and false to destroy it (see Listing 1). The function
also calls a function in the service that tells it what object the service serves (the
datawindow that called it into being). This is stored as a private instance variable of
type u_dw.
Listing 1
u_dw_ancient
Instance variables:
Public u_dw_srv_inifile iu_iniservice
Function:
of_initiniservice(boolean ab_start) returns long
//starts the dw ini file service if true, destroys it if false
long ll_ret
if ab_start then //start up the service
if not isvalid(iu_iniservice) then
iu_iniservice=create u_dw_srv_inifile
//tell the service whom it serves
ll_ret=iu_iniservice.of_setrequestor(this)
end if
else //destroy the service
if isvalid(iu_iniservice) then
destroy iu_iniservice
end if
end if
return ll_ret
The real parts of this code are the functions that read from and save to the ini file. Starting with the save function (because I have to figure out what I'm saving before I read it in), I want to call this function and have it do all the setprofilestring calls for me automatically. Setprofilestring has 4 arguments, the ini file name, the section in the ini file, the key in the ini file and the value to be inserted. I'm going to keep the ini file as an argument to my function, because I may have a need to use other ini files. The section in the ini file I'll leave as an argument also, because it's not much trouble to code it in. I'll use the field name seen in the datawindow painter as the key. This way when I create my datawindow all I have to do is name the fields with the names I want them to have in the ini file. The value I will get from the fields themselves.
The save function will then have two arguments, the ini file and the section name. First I want to perform an accept text to get all the data, this is a datawindow after all. Then I want to get the count of all the fields in the datawindow and loop through all of them. As I loop through them I want to read the column name to use as the key. I can read any of the datawindow data types into an any type variable. The string() function will turn them all into strings to use with the setprofilestring function. One complication exists however. If the data type is date or datetime the string function will return the date as a two digit year. I read somewhere that this was a bad thing, so I'll check the data type for each column. If it's a date or datetime data type I'll use a format string with the string() function call to get the format I want. Before I save to the ini file I want to change any null data for the value argument to a space so it will get saved, otherwise deleting the value in the datawindow wont delete it in the ini file. If the setprofilestring fails, I'll exit the function giving a bad return code. Otherwise, I'll loop through the rest of the columns and reset the update flags before returning. The resulting code is in as below (Listing 2).
Listing 2
u_dw_srv_inifile.of_savetoini(string as_inifile, string as_section)
//This function scans through the datawindow and saves the data to the
//user specified ini file, using the user specified section and using
//the column name as the key. It then resets the update flags.
long ll_cnt, ll_rows, ll_ret
string ls_name, ls_data, ls_type
date ld_date
any la_data
idw_dw.accepttext()
ll_rows = integer(idw_dw.object.datawindow.column.count)
for ll_cnt=1 to ll_rows
ls_name=idw_dw.describe('#'+string(ll_cnt)+'.name')
//load in the data to an any variable, using a
string will not properly convert
//the date to a string
la_data=idw_dw.object.data[1,ll_cnt]
ls_type=idw_dw.describe('#'+string(ll_cnt)+'.coltype')
choose case ls_type
case 'date'
ls_data=string(la_data,'mm/dd/yyyy')
case 'datetime'
ls_data=string(la_data,'mm/dd/yyyy hh:mm:ss:fff')
case else
ls_data=string(la_data)
end choose
//change blanks to a space so it is saved
if isnull(ls_data) then
ls_data=' '
end if
ll_ret=setprofilestring(as_inifile,as_section,ls_name,ls_data)
//return of -1 means setprofilestring couldn't
access the as_inifile ini file
if ll_ret<=-1 then
return -1
end if
next
idw_dw.resetupdate()
return 1
The second part of the code is loading in what has been saved. The function will need to know what ini file to use and what section in the ini file to read. This is the opposite of saving except for the data conversion. As all the data coming in is text it must be converted to the proper type before insertion into the datawindow, otherwise no data will be entered using the setitem() function. As before I count the number of columns, get the column name, use that as the key in the ini file and retrieve the data. Then I have to check the data type of the column and do the appropriate data type conversions. Using the describe function to describe #colnumber.Coltype I can get back many different answers in the case of char (char(20), char(200), etc) and decimal (decimal(2), etc) because it also includes the length as well as the data type. So instead of doing a choose case on the Coltype I take only the first 5 characters. I couldnt get the datetime function to return a datetime from an ini file value (is it me?), so I had to parse the string into a date string and a time string. I check for an error during the setitem() and return with an error code if necessary. At the end I reset the update flags of the datawindow. The resulting code is:
of_fillfromini(string as_inifile, string as_section)
//This function reads the column names from the requestor datawindow
and
//retrieves values for these by reading a user specified inifile and
//section, using the column name as the key. It then resets the update
//flags on the datawindow.
//
long ll_cnt, ll_rows, ll_data
string ls_name, ls_data, ls_type
date ld_data
datetime ldt_data
time lt_data
decimal ldec_data
long ll_len, ll_size, ll_ret
string ls_piece
//count the number of columns in the dw
ll_rows = integer(idw_dw.object.datawindow.column.count)
for ll_cnt=1 to ll_rows
//get the column name
ls_name=idw_dw.describe('#'+string(ll_cnt)+'.name')
//read the data using the column name as the
key
ls_data=profilestring(as_inifile,as_section,ls_name,'')
//check the datatype
ls_type=idw_dw.describe('#'+string(ll_cnt)+'.Coltype')
//convert to the proper datatype
//use left(,5)--to cut off the end piece,
strings are type char(20), char(200), etc
//decimals are decimal(2), etc.
choose case left(ls_type,5)
case 'date'
ld_data=date(ls_data)
ll_ret=idw_dw.setitem(1,ls_name,ld_data)
setnull(ld_data)
case 'numbe','long','ulong'
ll_data=double(ls_data)
ll_ret=idw_dw.setitem(1,ls_name,ll_data)
setnull(ll_data)
case 'real','decim' //decimal(#)
ldec_data=dec(ls_data)
ll_ret=idw_dw.setitem(1,ls_name,ldec_data)
setnull(ldec_data)
case 'datet' //datetime
//extract the date
ll_len=len(ls_data)
do until ll_size>ll_len or isdate(ls_piece)
//go backwards to ensure all 4 digits of the year are taken
ll_size=ll_size+1
ls_piece=left(ls_data,ll_len - ll_size)
loop
ldt_data=datetime(date(ls_piece),time(right(ls_data,ll_size+1)))
ll_ret=idw_dw.setitem(1,ls_name,ldt_data)
setnull(ldt_data)
case 'time'
lt_data=time(ls_data)
ll_ret=idw_dw.setitem(1,ls_name,lt_data)
case else //for 'char(#)'
ll_ret=idw_dw.setitem(1,ls_name,ls_data)
end choose
if ll_ret=-1 then
return -1
end if
next
idw_dw.resetupdate()
return 1
Listing 3
Using the functions in this service is simple. Simply create a datawindow with an external data source. Name each column with the same name you want the ini file key to have. In either a post-open event for the window or constructor of the datawindow call the function to initialize the service. Load in the ini file data by calling the function of_fillfromini() passing the ini file name and section as arguments. Thats all (and of course check the return code). Saving the data uses just one function of_savetoini() with the same arguments. The only thing you lose with this system is being able to determine the default values when loading in the data if reading the ini file fails for some reason. Reading an ini file is so fast that you can do some post processing if necessary with little appreciable effect. Ive used this service and then encrypted/decrypted a database password directly after the save/fill function call. What you get from this service is a simplification of a programming chore. It is a small matter to add and delete fields and to change the display format from one control type to another (radio buttons to drop down list box for example). You can switch the display very easily. As an example imagine you handled connection information for different databases each with a different dbms. You can change the display to show the valid connection parameters for that particular dbms using one screen by simply switching the datawindow dataobjects. You also get all the datawindow functions you know and love like modifiedcount() and resetupdate().
The code needed to create the ini file service is pretty basic. The original version only used a character data type, the rest were added for this article. Thus, this hasnt been extensively tested, so you better test it yourself. I did find that the real data type would sometimes not write correctly to the ini file. Just now it changed 2.51 into 2.509999990463257. If this is used just to write ini files it shouldnt be a problem, just use the number or decimal data type. I dont imagine in a simple system it would be necessary to use data types other than date, string and decimal. I just included the other data types to be complete.
By Greg Dzingeleski, PhD. Greg works at the Social Security Administration and has been programming in Powerbuilder since October of 1996. Greg can be reached at 410-966-3328 or at Greg.dzingeleski@ssa.gov, but you should be warned hell probably try to get you to write an article for the newsletter.
At the last meeting of the Baltimore Area PowerBuilder Users Group meeting, I asked if anyone knew how to dynamically change the systems default printer without the user having to select the printer. Heres what I found out in researching the issue.
Background
One of my clients has an application that generates letters to thousands of people. The current system used a very slow method of embedded SQL, then DDE to WordPerfect to produce letters, labels and other documents. The application automatically changes the printer by sending a DDE command to WordPerfect to select the appropriate printer depending on the document being printed. Although the application was written in PB, it was a good example of how to do everything wrong! The client had let the letter generator run for three days before rebooting and deciding it didnt work. When called in, I determined that in fact this piece of code was working but the performance was unacceptable, to say the least!
As an aside, you might be interested in how bad can code get? Well, since you ask This part of the application is a batch process which checks the records in the database, selects certain records, generates a document based on the indicator in the database and prints it out. The current code has embedded SQL as I mentioned, but not just one, not just two, but upwards of eight separate statements! Then it opens WordPerfect, sends over twenty-eight data fields via DDE, merges, the document, changes the printer, prints and then closes WordPerfect. It does this for each record.
We got rid of the SQL by using datawindows and got rid of the DDE by replacing it with the RTF datawindows. It now takes a few seconds to run. But how to change the printer?
Solution
After searching the WATCOM C++ API Help library, I posted a message to the PowerBuilder news group for datawindows (powersoft.public.powerbuilder.datawindow) to find an easier solution. This resulted in a tip from Mark Pfeifer to check out the http://www.dejanews.com/ to search for topics on default printers. This utility searches many newsgroups for postings on a given topic. There were many, many messages. My favorite answer was the very crisp "Its considered rude to change the default printer. Let the user change it themselves." I do hope the author of this tidbit is not responsible for developing applications. I did find many legitimate leads, which basically fall into three categories.
RegistryGet("HKEY_LOCAL_MACHINE\Config\0001\System\CurrentControlSet\Control\Print\Printers", "default", ls_default),API Calls: This solution deals with calling the API functions to set the default printer. A simpler approach is to purchase something like PowerPrint from DigitalWave (http://www.digitw.com/) which is a product that allows the developer to change all the printer attributes.
Registry Entries: This approach centers on using the Registry functions in PB to get and set the Registry entries for printers. Ignoring the fact that function calls are very long, e.g. to get the default printer is
the function calls change depending on which Windows system your using.
INI Files: Being the last option, you can tell this is the one I liked the best. In short, you get the current default printer by getting the device in the windows section of the win.ini file. Set the default by setting the value of the device in the same section to the printer you want, for example, SetProfileString(win.ini, windows, device, ls_default). Below is the full solution.
The Details
Defining the Printers in the INI files
We defined the printers we need in the application by listing them in the application
ini file. These need to be configured on the machine being used. You can find the current
configurations by going to the Devices section of your win.ini file.
e.g. [devices]
Epson ActionLaser 1500=HPPCL5MS,LPT1:
In the application ini file, this becomes
[printers]
LASER = Epson ActionLaser 1500,HPPCL5MS,LPT1:
Note that the equal sign becomes a comma in your ini file. The format for the default device in win.ini is Device={Device name}, {Device language}, {network path/port}.
We listed printers for Dot, for preprinted forms on the dot matrix, Laser, Label, Fax, and Color.
A Function to Define Printers in the Application
In the non-visual object that handles the Ini file services, we defined six strings as instance variables to correspond to the five printer types plus one string for the users current default printer. The non-visual is declared as a global, gnv_ini.
PROTECTED:
PROTECTED Function of_get_printer (String a_printer_type, String a_key)
STRING ls_printer, ls_look_up //Get the printer name from the apps iniIn the code where the gnv_ini object initializes itself, we added the following code to set the values of the instance variables to the string that we need to set the device in the win.ini file to the corresponding printer.
//Get the users current default printer and save it
The Function to Change Printers
Now were ready to actually change the default printer on the fly.
Public Function of_set_printer( string a_new_printer) RETURN Boolean INTEGER li_set = 0Changing the Printer Dynamically
We have everything in place now. To change the printer, just use the following function call
gnv_ini.of_set_printer(ls_printer)
where ls_printer = COLOR or LASER or DOT or DEFAULT or any printer that has been set up.
Remember to also set the printer back to its original default by putting the following code in the Destructor event of gnu_ini.
This.of_set_printer( Default) Conclusion
Unlike the guy who wrote that it is "rude to change the printer", I thing that if the user tells the application once that for labels we want to use the label printer, then the application should do it until told otherwise.
Copyright 1998 by Chi Resources. All rights reserved.
Submitted by Susan Galli. Susan is an independent consultant, a CPD, and the original technical advisor for BAPBUG as well as serving on the executive committee since its inception. She has worked with PowerBuilder since 1992 - programming, teaching and mentoring. She can be reached at (410) 987-2684 or susan@annapolis.net.
As I understand OLE I can access any OLE object through Powerbuilder, but I need to know what functions to call. I've also read that you can access Powerbuilder itself as an OLE application server. Does anyone know where I can get the function calls for the powerbuilder application server?
Speaking of OLE here's something I've fooled around which is kind of interesting. In windows 95 or NT you can create a control panel folder. In explorer create a new folder and name it
Control Panel.{21EC2020-3AEA-1069-A2DD-08002B30309D}
I'm not kidding. When you type in the new name and hit return the folder changes name to Control Panel and now "points" to your control panel. You can put this in your start menu and it will open as a folder and the items will be control panel items. It will be updated if you add or delete control panel items. I don't know why this wasn't included in the normal operating system. Anyway, as far as I can tell the long number/letter sequence is the GUID or globally unique identifier that each OLE object has. If you look in the registry under the key HKEY_CLASSES_ROOT\PowerBuilder.Application\CLSID there is another GUID ({29478EF0-C371-11CE-8891-00805F70D324} if you're curious). I can give a folder any name followed by a period and the GUID (brackets included) the folder changes from a folder type to a Powerbuilder 5.0 Automation type. When I double click on it it does...nothing.
I just thought this was neat and is probably useful is some cases, but I don't know how. If anybody would like to expand upon this it would be appreciated. You could even write it up as an article. Two other useful GUIDs are Printers.{2227A280-3AEA-1069-A2DE-08002B30309D} for the printers folder and Dial Up Net.{992CFFA0-F557-101A-88EC-00DD010CCC48} for dial up networking.
Greg Dzingeleski
Event Driven Programming in PowerBuilder
By Fred Grau, CPD
One of the most difficult aspects of developing a Windows based client/server system is understanding event driven programming. Add object-oriented techniques to the mixture and youre talking total confusion. This article will attempt to alleviate some of this fear.
There are many pre-defined events assigned to window. Some of these events trigger when the user performs a specific action. These events include Clicked, Doubleclicked, and Rbuttondown. However, many of these events automatically trigger depending on the circumstance. For example, if window A and window B have been opened, and the user changes focus from window A to window B without closing window A, the following events will automatically trigger:
If your application has a frame menu but does not have a menu assigned to each sheet window, then these two events are appropriate for enabling and disabling menu items. However, as a general rule, I try to avoid having any code in these events.
As another example, these are the events that automatically trigger when a sheet window opens:
The events that automatically trigger in a DataWindow control are more confusing than a window. For example, in PowerBuilder 5.0, the following DataWindow events automatically trigger when a Retrieve() is performed:
Under PowerBuilder 6.0, the following DataWindow events automatically trigger when a Retrieve() is performed:
When a DataWindow Update() executes, the following DataWindow events trigger:
After data is retrieved for a DataWindow, events can automatically trigger under certain circumstances. For example, if a row is current, and the user clicks on another row, the following DataWindow events trigger:
If the focus was on another control before the DataWindow was clicked, then these events will trigger prior to the events listed above:
If a user clicks on a new column in a DataWindow control without clicking on a new row, then the follow DataWindow events trigger:
When a user enters data in a DataWindow and tabs to or clicks on another column in the same DataWindow, then the ItemChanged event triggers. This event occurs before the data is applied to the DataWindow. If 1 or 2 is returned from this event, then the data will not be applied to the DataWindow.
As a caveat to DataWindow events, when a user previously entered data in a DataWindow, then clicks on another control, the DataWindows ItemChanged event does not automatically trigger. An AcceptText() must be executed for the entered data to be applied to the DataWindow. Because of this, always issue an AccepText() before updating any DataWindows.
With all of these events automatically triggering, life for a developer can be very confusing. I hope some of this article clears up some of this confusion.
Fred Grau is a PowerBuilder consultant and the only employee at Woodfield Technical Services, Inc. He is also a CPD and the treasurer for BAPBUG. He has developed a variety of PowerBuilder applications at locations including VIPS, HCIA, Blue Cross and Blue Shield of Delaware, and SSA. You can reach Fred at (410) 893-9638 or fred1@flash.net.
Here's an article from Rik Brooks the author of one of my favorite web sites, Rik's Powerbuilder Dojo (http://www.1313mockingbirdlane.com/dojo).
Advanced Variable Declarations
One of the most important aspects of OOPS is the relationship of one object to another. It's a kind of odd thing, this relationship. There are actually two levels to it. There is the relationship of one object to another at run time, while your application is actually in operation. Then there is the relationship of one object to another along the inheritance tree. That is to say; two or more objects can have a relationship while the application is running and simoultaneously have a relationship with its ancestors.
This article concerns both and might be a little confusing for that reason. I'll try to keep this discussion as concrete as possible and avoid some of the typical difficulties. Each object has something called an interface. That is the hooks that it provides to other objects to get at the internal workings of the object. Often the interface is a set of functions. Most objects, visual ones at least, have a property called visible. It is a boolean and if you make it false it becomes invisible. This property makes up one part of its interface.
The objects that you create also have interfaces. For some people these interfaces are wide open. The programmer, usually out of either laziness or inexperience makes all of the instance variables of an object public (the default). This means that each one of these variables are part of the interface. Any other object can reach inside the object and change any of those variables. Usually this isn't a good idea. It might be OK, but it is not good programming practice and is certainly a violation of data encapsulation.
This article refers to the declaration of instance variables. In PowerBuilder 4 instance variables could be defined as either Public, Protected, or Private. A publicly declared instance variable (the default) is accessable to any other object without discrimination. A private was accessable only to the class in which it was declared. A protected was accessable to the class in which it was declared and any descendent of that class.
This is all very good and understood. You can limit the ability of other objects to change the values of your object. PowerBuilder gives us a lot more flexibility than these rudimentary controls.
In PowerBuilder you can control something called the Access of each of these variables. You can declare that a particular variable has a different read or write access. Note that in the syntax of PowerBuilder 4 both read and write access had to be the same. The access rights still default to Public.
After PowerBuilder 4 you can declare an instance variable to have a read access of public and a write access of protected. You might want to do this if you had an object of type Human. This object would need to allow other objects to know how old it was. Other objects would need this information to meet recruitment requirements, or to serve it a drink. In this case you would declare the ii_age instance variable of the Human class thusly:
public protectedWrite int ii_age
This indicates that the reading of ii_age is public, but the writing is restricted to the Human class itself or any descendent of it. This is exactly what we need, without any functions. Of course since public is the default it would not be needed. Thus the following is synonimous:
protectedWrite int ii_age
There is one last point to be made and then I am done with this. Notice that we have a general access description, in our case public. Then we have one that further restricts the access. Finally we have the data type and the name. There are some restrictions to how you can declare the variables. The second access right can not be less restrictive than the first. That is to say, if you declare a variable to be private, you may not give it protecedRead. In other words, the following is illegal.
private protectedRead int ii_age
That is why there are only privateRead, protectedRead, privateWrite, and protectedWrite modifiers.
Let's finish this with a few examples
1.int ii_age
2.protectedRead ii_age
3.protectedRead protectedWrite ii_age
4.privateWrite ii_age
5.protectedRead privateWrite ii_age
6.protected privateWrite ii_age
1 is entire public (the default)
2 can be read only be the class and its descendants, but can be changed by public (kind of wierd)
3 is the same as protected ii_age.
4 can be read by public, but only the class itself can change it (very reasonable).
5 can be read by itself and descendants, but only changed by itself.
6 is the same as 5, just declared in a different way. Use whichever strikes your fancy.
Rik Brooks is an independent consultant working in the NJ,Penn, and Del area. He also takes off-site contracts. He is the author of "Rik's PowerBuilder Dojo" which can be found at http://www.1313mockingbirdlane.com/dojo
With the release of our first newsletter, we are offering the opportunity for corporate sponsorship of BAPBUG. Because our bimonthly newsletter will reach approximately 200 developers, we are offering the following annual rates:
Page Size | Rate |
¼ Page |
$35 |
½ Page |
$50 |
Full Page |
$100 |
We will also be including an employment opportunity section in our newsletter. The cost per ad in this section will be $10 per issue.
Since we will be transmitting the newsletter electronically, we will need a softcopy (preferably in MS Word format) of any graphics to be included with your ad.
Please make checks payable to BAPBUG and can be sent to the following address:
BAPBUG
c/o Fred Grau
2003 Copperwood Way
Fallston, MD 21047
If there are any questions, feel free to contact Fred at 410-893-9638 or at fred1@flash.net.
BAPBUG Officers | |||
President | Debbie Arczynski | debbie@charm.net | (410) 796-9461 |
Vice President | Bill Bitman | bitman@jhuapl.edu | (410) 792-6000 |
Secretary | Gordon Giffen | gordon-g@vips.com | (410) 832-8300 |
Treasurer | Fred Grau | fred1@flash.net | (410) 893-9638 |
Tech Advisor | Robin Bates | robin.bates@powercerv.com | (703) 502-8383 |
Tech Advisor | Susan Galli | susan@annopolis.net | (410) 987-2684 |
Tech Advisor | Mark Pfeifer | mpfeifer@sprynet.com | (410) 499-8765 |
Web Co-ordinator | Anne Sola | anne-s@vips.com | (410) 832-8300 |
Newsletter Editor | Greg Dzingeleski | greg.dzingeleski@ssa.gov | (410) 966-3328 |
BAPBUG Web Page
Please take a look at our new web page designed by Anne Sola. It has lots of information about the group itself, the officers, the agenda, events, jobs, and links to other PB web pages. It is located at: http://geocities.datacellar.net/siliconvalley/bay/8680. If you have any suggestions and/or comments, please contact Anne Sola.