1. Arnaud Bailly
  2. PowerBuilder
  3. Wednesday, 13 January 2021 08:24 AM UTC

Hello (and Happy New Year),

We have a simple function that iterates over the rows of a datawidnow and fill some objects from some of the items in the row. This function looks like the following:

public subroutine fill_recipe_components(datawindow a_dw)
long ll_index, ll_tot
ll_tot = a_dw.rowCount()

for ll_index = 1 to ll_tot
st_recipe_component lst_recipe_component
lst_recipe_component.s_code = a_dw.GetItemString(ll_index, 'article_code_article')
lst_recipe_component.d_percentage = a_dw.GetItemNumber(ll_index, 'pourcentage')
ist_recipe_component[ll_index] = lst_recipe_component
next

end subroutine

We have another function that does exactly the same thing, but on a datastore:

public subroutine fill_recipe_components(datastore a_dw)
long ll_index, ll_tot
ll_tot = a_dw.rowCount()

for ll_index = 1 to ll_tot
st_recipe_component lst_recipe_component
lst_recipe_component.s_code = a_dw.GetItemString(ll_index, 'article_code_article')
lst_recipe_component.d_percentage = a_dw.GetItemNumber(ll_index, 'pourcentage')
ist_recipe_component[ll_index] = lst_recipe_component
next

end subroutine

As one may notice, those functions are strictly identical (to the point we misnamed the datastore parameter a_dw). Is there a way to have a single function instead of 2? It seems like datastore and datawindow are related and have at least a subset of their methods as common interfaces, but the compiler rejects passing one when the other is expected. We tried wrapping a datawindow in a datastore by assigning the dw to the object property of a (new) datastore but this does not work.

 

Benjamin Gaesslein Accepted Answer Pending Moderation
  1. Friday, 15 January 2021 09:32 AM UTC
  2. PowerBuilder
  3. # 1
2
Votes
Undo

I usually write functions for Datastores only. If I need to apply the function to a Datawindow, I convert the Datawindow to a Datastore first. I've written a small function to do this:

public function datastore of_dwtods (datawindow adw_source, dwbuffer adwb_source)

n_ds lds_store

lds_store = create n_ds
lds_store.dataobject = adw_source.dataobject
lds_store.create( adw_source.describe('DataWindow.Syntax') )

adw_source.rowscopy(1, adw_source.rowcount(), adwb_source, lds_store, 1, adwb_source )

return lds_store

[Disclaimer: I use a create here because I sometimes use this function on Datawindows that have their syntax altered via modify. Otherwise the rowscopy might not work if columns are added that weren't in the original dataobject

n_ds is inherited from datastore, should work with the standard dataype just as well]

You could probably leave out the rowscopy and have the target Datawindow share its data with the Datastore created by this function instead. This way, changes to the data would automatically be applied to the target Datawindow. Otherwise you'd have to copy the rows back to the DW.

Comment
Thanks Benjamin, it worked with a few changes:

1. The syntax for the rowscopy function is different in our case, probably because of different PB versions (we are on 2019R2)

2. leaving out the rowscopy did not work, eg. the datastore appeared empty so not sure how to do the sharing you are speaking of.



  1. Arnaud Bailly
  2. Friday, 15 January 2021 10:20 AM UTC
1. How did you change the rowscopy syntax? I'm using 2019R2, too.

2. It works for me; you have to do the following:

n_ds lds_ds

lds_ds = of_dwtods(adw_yourdatawindow, Primary!)

adw_yourdatawindow.sharedata(lds_ds)



Then you can do any processing on the newly created Datastore and any data changes should be reflected in your source DW. You can then disconnect the datastore from the DW via lds_ds.sharedataoff() and safely destroy it.
  1. Benjamin Gaesslein
  2. Friday, 15 January 2021 10:35 AM UTC
Nice one Benjamin!
  1. Miguel Leeuwe
  2. Friday, 15 January 2021 13:21 PM UTC
There are no comments made yet.
mike S Accepted Answer Pending Moderation
  1. Wednesday, 13 January 2021 15:15 PM UTC
  2. PowerBuilder
  3. # 2
4
Votes
Undo

the link below is the presentation i did on this topic from this year's elevate - how to create a common ancestor so you can do exactly what you asked about.  Also talk about pros/cons of all the options to this (dynamic, if/then, overloading, shared).

DataWindow and DataStore Missing Link - YouTube

Comment
There are no comments made yet.
John Fauss Accepted Answer Pending Moderation
  1. Wednesday, 13 January 2021 15:08 PM UTC
  2. PowerBuilder
  3. # 3
1
Votes
Undo

Hi, Arnaud -

Arthur is correct when he states the common ancestor to both is PowerObject. Your premise that there should be a common ancestor providing the data-related functions because the methods are identical is not correct. Yes, the data manipulation methods are identical, but that is by design because the DataStore was intended to be a non-visual DataWindow, not because there is a common ancestor that provides this functionality.

Have you considered using ShareData? DataWindows and DataStores can share their data buffers.

You would have to incur the overhead of enabling/disabling data sharing (which I believe is minimal), but then one function would indeed work for both.

Regards, John

Comment
in the past, sharing had been my favorite method of using the same code between datawindows and datastores. however, shared has some major drawbacks as well.
  1. mike S
  2. Wednesday, 13 January 2021 15:19 PM UTC
Out of curiosity John, have you ever seen PowerBuilder's source code or used some other clever hacking method to confirm that there is not a common ancestor? What I have half wondered is that if PowerBuilder itself was written in C/C++ which supports multiple inheritance, there could be a data manipulation object common to bot DataWindow, DataStore, and DataWindowChild objects but because PowerBuilder itself does not support multiple inheritance, there was not a convenient way to expose this object, thus it remains hidden.



Obviously, whether there is such a thing or not, it is all theoretical and one is left with the alternatives brought up on this thread of using ShareData, Dynamic calls, or building the pseudo-ancestor object as described in the Elevate presentation.
  1. Andrew Barnes
  2. Wednesday, 13 January 2021 17:39 PM UTC
Hi, Andy! No, I have never been privy to any PB source code... and even if I had, I'd likely be subject to a stringent non-disclosure agreement lol. My opinion on this matter (and that's all it is), is based on two things: 1) The published system object hierarchy, which can be viewed in expanded treeview format in the PB Object Browser window, and 2) The historical timeline of the DataWindow control and the DataStpre NVO. The DWC came first, and the DS came later, version 5, I believe. The development of the DS was, in large part, PowerSoft's response to the need of the development community for a non-visual DataWindow. Hidden DWC's were being plopped onto windows like crazy as data models and applications evolved from relatively simple maintenance windows and master-detail paradigms. Since the DW is THE key component of PowerBuilder, I really doubt PowerSoft took the huge risk of re-engineering the DataWindow in order to make the DataStore possible. Much less risk to duplicate the existing functionality while omitting the visual aspects..

Does considering the history prove anything? No. There may very well be internal functionality under the covers whereby the two share or utilize a common code base or oodles of internal methods. But what matters is the publicized and enforced object hierarchy and methods that PB exposes, and those say there's no common interface. Everything else is fun to speculate about, but in the end it doesn't matter.
  1. John Fauss
  2. Wednesday, 13 January 2021 19:12 PM UTC
There are no comments made yet.
Miguel Leeuwe Accepted Answer Pending Moderation
  1. Wednesday, 13 January 2021 14:29 PM UTC
  2. PowerBuilder
  3. # 4
0
Votes
Undo

And here's the other way around, no need for OpenUserObject() and no need for u_dw object.

See the attached zipped sample:

datawindow ldw_2
datastore lds_temp, lds_toworkwith
any la_data
long ll_i

if apo.typeof() = datawindow! then
    messagebox('debug1', "I'm a DataWindow")
    ldw_2 = apo
    // EDIT: this following comment is absent in the zipped sample app.
    // now make a datastore using the passed in datawindow object:
    lds_toworkwith = create datastore
    lds_toworkwith.dataobject = ldw_2.dataobject
    lds_toworkwith.object.data = ldw_2.object.data
elseif apo.typeof() = datastore! then
    messagebox('debug2', "I'm a DataStore")
    // EDIT: the comment in this zipped sample app. is incorrect, should be above.
    lds_toworkwith = apo
end if

// here you do all the rest of coding of your function:
Messagebox('debug4', lds_toworkwith.rowcount())

destroy lds_toworkwith
Attachments (1)
Comment
There are no comments made yet.
Miguel Leeuwe Accepted Answer Pending Moderation
  1. Wednesday, 13 January 2021 14:18 PM UTC
  2. PowerBuilder
  3. # 5
0
Votes
Undo

here's the little sample app that I spoke about in one of the comments. See attached.

It's in 2019 format, let me know if you need a lower version.

regards,

Attachments (1)
Comment
There are no comments made yet.
Miguel Leeuwe Accepted Answer Pending Moderation
  1. Wednesday, 13 January 2021 12:00 PM UTC
  2. PowerBuilder
  3. # 6
1
Votes
Undo

Yes, when using DYNAMIC, anything will compile.

You said "We tried wrapping a datawindow in a datastore by assigning the dw to the object property of a (new) datastore but this does not work".

Have you tried it the other way around? Like "wrapping a datastore in a datawindow"?

A datawindow has all the properties of a datastore, but a datastore doesn't have all the properties of a datawindow.

Regards.

Comment
How can I do that? Initially, we tried something like:



datastore n_ds

n_Ds = Create datastore

n_ds.object = <my datawindow>

...



How would wrapping a DS in a DW look like?
  1. Arnaud Bailly
  2. Wednesday, 13 January 2021 13:04 PM UTC
I don't know ... you said you tried to wrap a ds into a dw...

I've tried doing something like this:



u_dw ldw_2

datastore lds_temp

any la_data

long ll_i



if apo.typeof() = datawindow! then

messagebox('debug1', "I'm a DataWindow")

ldw_2 = apo

elseif apo.typeof() = datastore! then

messagebox('debug2', "I'm a DataStore")

// now make a datawindow using the passed in datastore object:

ldw_2 = create u_dw

lds_temp = apo

ldw_2.dataobject = lds_temp.dataobject

// ldw_2.settransobject(sqlca)

// la_data = lds_temp.object.data



for ll_i = 1 to lds_temp.rowcount()

ldw_2.insertrow(0)

next



// if not isnull(la_data) then

// ldw_2.object.data = la_data

// end if

//

// if not isnull(la_data) then

// ldw_2.object.data = la_data

// end if



end if



Messagebox('debug4', ldw_2.rowcount())





When passing in a datastore with 3 rows, it correctly assigns the dataobject (can be read back after assignment) and inserts 3 rows, but when you then do a rowcount() it still gives 0 rows.

The commented stuff were other things I tried, but it all fails.

Sorry, seems difficult to be realized.
  1. Miguel Leeuwe
  2. Wednesday, 13 January 2021 13:52 PM UTC
Okay,

Instead of doing a CREATE y have to use OpenUserObject.

u_dw is a standard visual object that I made of type 'datawindow' and it's marked as 'invisible' so it doesn't show when I do OpenUserObject.

-----

u_dw ldw_2

datastore lds_temp

any la_data

long ll_i



if apo.typeof() = datawindow! then

messagebox('debug1', "I'm a DataWindow")

ldw_2 = apo

elseif apo.typeof() = datastore! then

messagebox('debug2', "I'm a DataStore")

// now make a datawindow using the passed in datastore object:

openUserobject(ldw_2)

lds_temp = apo

ldw_2.dataobject = lds_temp.dataobject

la_data = lds_temp.object.data

if not isnull(la_data) then

ldw_2.object.data = la_data

end if



end if



Messagebox('debug4', ldw_2.rowcount())

-----



I'll post a sample app in a different answer.
  1. Miguel Leeuwe
  2. Wednesday, 13 January 2021 14:13 PM UTC
There are no comments made yet.
Arthur Hefti Accepted Answer Pending Moderation
  1. Wednesday, 13 January 2021 08:33 AM UTC
  2. PowerBuilder
  3. # 7
1
Votes
Undo

Hi

You could powerobject instead of datawindow / datastore as type passed and call the functions dynamic.

public subroutine fill_recipe_components(powerobject a_dw)
long ll_index, ll_tot
ll_tot = a_dw.DYNAMIC rowCount()

...

Regards
Arthur

 

Comment
Thanks for the suggestion. We tried it but unfortunately it does not work, although it compiles.
  1. Arnaud Bailly
  2. Wednesday, 13 January 2021 10:19 AM UTC
There are no comments made yet.
  • Page :
  • 1


There are no replies made for this question yet.
However, you are not allowed to reply to this question.