Adding controls at runtime - Forum - OpenEdge Development - Progress Community
 Forum

Adding controls at runtime

  • I am trying to re-create an application that I did in 10.1 GUI where I would populate a frame at runtime with labels and textboxes, using create statements, based on records in a temp-table.

    I know that you can add controls at runtime a couple of ways:

    define private variable TextBox1 as System.Windows.Forms.TextBox no-undo.

    TextBox1 = new TextBox().

    this-object:Controls:Add(TextBox1).

    or

    this-object:Controls:Add(new System.Windows.Forms.TextBox()).

    Since I don't know how many controls I will have to add I can't really define the TextBox by name. The second method doesn't allow for naming it at instantiation (if I understand this correctly) so setting the properties becomes pretty difficult. I am thinking that the only way would be to find the controls individually by index in the Controls collection and assign the properties there (e.g. Name).

    I admit it's been a really long time since I've done much with .Net controls so I'm sort of stuck with the best solution. Any ideas would be appreciated.

  • You can use a temp-table. It is possible to define a field of type Progress.Lang.Object in a temp-table. You can assign the object reference to that field and CAST the value to whatever you like when you need to manipulate the object.

  • Looks like you already have a preference to access them by name.

    I'd go with the first approach, set the Name property and add it to the Controls collection.

    Then you can access them later using THIS-OBJECT:Controls .

  • Thanks to both of you. I just need to try and try a couple of things out to get it straight in my head. Gave me great ideas though.

  • Okay, I'm sort of playing around with this. Here's what I did to test out what's in my head.

    I have a temp-table definition:

    define temp-table tt-controls

    field fieldName as character

    field formControl as Progress.Lang.Object

    .

    for the sake of simplicity, I have a single method where I create and use the record...

    create tt-controls.

    assign

    tt-controls.fieldName = "myTextBox"

    tt-controls.formControl = new Progress.Lang.Object().

    ...since I already have the buffer...

    this-object:Controls:Add(cast(tt-controls.formControl, System.Windows.Forms.TextBox)).

    When I run the app I get:

    Invalid cast from Progress.Lang.Object to System.Windows.Forms.TextBox....

    Am I missing something obvious here?

  • the tt record can only store a progress.lang.object, so you need to create the control first, then cast it to an object before storing it in the tt record.

    have a look at the dynamic-new command to create the appropriate control (text box / label / etc).

    Once you created the control, cast, and store. You probably also should store the object type in the tt record so that you can recast it back to the original object type.

  • I don't have a machine handy so not tested but you would do it the other way around:

    define temp-table tt-controls

    field fieldName as character

    field formControl as Progress.Lang.Object.

    create tt-controls.

    assign

    tt-controls.fieldName = "myTextBox"

    tt-controls.formControl = this-object:Controls:Add(System.Windows.Forms.TextBox).

    So you are assigning the object handle to the formControl field.

    You need to cast it for subsequent manipulation:

    CAST(tt-controls.formControl,System.Windows.Forms.TextBox):Text.

    or even better (untested):

    DYNAMIC-CAST(tt-controls.formControl,tt-Controls.objectType):Text.

    where you store the Object Type in the tt as well.

    Oops of course you need to NEW the TextBox first but you get the idea.

    Message was edited by:

    Peter van Dam

  • There is an example of this kind of usage in my old Collection Classes (http://www.oehive.org/CollectionClasses) ... ah 10.1A so long ago, but Peter is bang on here. The basic idea is that a Progress.Lang.Object is the ultimate super, so it isn't much, really. You can't turn it into something it isn't with a cast. But, since it is a super, you can store anything in it and cast it back into its original type.

    Possibly, you could use something other than Progress.Lang.Object, as long as it was super to anything you were going to store there.

    Consulting in Model-Based Development, Transformation, and Object-Oriented Best Practice  http://www.cintegrity.com

  • have a look at the dynamic-new command to create the

    appropriate control (text box / label / etc).

    DYNAMIC-NEW won't work with .NET Classes. It just woks with ABL Classes (or ABL Classes inherriting form .NET Classes).

  • oh : I was reading the manual for dynamic-new *note the sentence "If expression specifies a .NET object" *

    [ , parameter ][ NO-ERROR ]

    ..snip..

    ..snip..

  • I actually just realized this myself.

    The idea of this application is to fill a dataset on the server with records that will coincide with parameters for a report. Ship it down to the client, then create the appropriate text and label fields for the UI.

    I'm afraid I'm still sort of confused on what I need here.

  • Well, then it must be a bug:

    System.TypeLaodException: The type System.Windows.Forms.Control in Assembly Progress.NetUI, Version=1.03229.34577, Culture=neutral, PublicKeyToken=null could not be loaded.

  • When I try your code, (or any code) I get

    wonder why there is a difference ...

    null

    edited for neatness ...

  • Dynamic new .NET style:

    define variable t as System.Type no-undo.

    /* The type helper avoids having to spell out the fully qualified type name */

    t = Progress.Util.TypeHelper:GetType("System.Windows.Forms.Control").

    define variable obj as Progress.Lang.Object no-undo.

    /* all objects inherit from Progress.Lang.Object, but to create a .NET object we can use the .NET reflection API */

    obj = System.Activator:CreateInstance(t).

    /* show the class name */

    MESSAGE obj:ToString()

    VIEW-AS ALERT-BOX.

    Added some comments to the code

  • Well .NET reflection is a powerful utility.

    But why does the DYNAMIC-NEW docu mention .NET classes, when there's actually a problem with it? A bug?