Creating a Collection property in a user control - Forum - OpenEdge Development - Progress Community

Creating a Collection property in a user control

 Forum

Creating a Collection property in a user control

  • 10.2B04

    Is it possible yet to create a collection property in a user control and have it available in the designer when that control is dropped on a form? I seem to be able to get it to almost work.

    Depending on the type of collection (Generics), I can sometimes get the property to display with a button to edit the collection's items, but either the Add button is disabled, or I can't edit the item (System.Object) once added. (Image1, Image2)

    I've tried a number of collection types and signatures but cannot seem to come up with a combination that works.

    "System.Collections.Generic.Dictionary<TKey, TValue>"

    "System.Collections.Generic.KeyValuePair<TKey, TValue>"

    "System.Collections.Generic.SortedList<TKey, TValue>".

    etc.

    At the moment, all I'm trying to accomplish is a simple collection of text strings that can be added via the designer. I've had TKey and TValue set to System.String and/or System.Object with no luck.

    Thanks,

    Jim

  • Do you need a collection with a key? That would be two elements, one for the key, one for the value.

    Did you try a generic list of System.String?

    That's only a single object per list member. Should work better.

  • Hi Mike,

    No, I this case I don't necessarily need a key. I did try this earlier (wasn't in my post)...

    DEFINE PUBLIC PROPERTY ColTest AS "System.Collections.Generic.List<System.String>" NO-UNDO GET. SET.

    When the items dialog displayed and I select Add, I get an error (see attached). (The image button in these posts are disabled but the video button is not)

    Jim

  • Ah, I see.

    The System.String does not have a constructor without any parameter But the code generator cannot generate code for the constructor parameter. You'll probably have to create a "holder class" in C# (or VB.NET if you have to) and use a generic list of that type.

    That holder class should have a constructor with no parameter (default constructor) and a System.String property to accept your text.

    Or you'll find an existing .NEZ type that you can use. But not an primitive type like System.String.

  • Integer works fine, and I'm guessing the other primitive types do as well:

        define public property ColTest as "System.Collections.Generic.List" no-undo
        get.
        set.

    The exception is coming from the .NET designer code.  I just tried in visual studio 2010 and it generates the exact same error if you use a list of strings.

    You'll need to write your own designer class to handle it as it appears the MS default collection designer cannot handle it.

    mattB

  • Yes, once the Collection is shown in the property grid it should cause the same issues in the Visual Studio (well done Matt et al.).

    The simple holder class written in C# should work without the need to define a specialized Desinger type, as we are using that in our framework.

  • Thanks guys for digging into this.

    Mike,

    Can you elaborate on a 'holder' class?

    Thanks,

    Jim

  • Basically just a simple C# class with a Single property of type System.String.

  • Ok, I think I got it.

    I'll give that a try.

    Thanks!

  • Mike,

    I'm wondering if I'm just not understanding exactly how to do this.

    I created a class in C# that contains a single property:

    using System;

    using System.Collections.Generic;

    using System.Linq;

    using System.Text;

    namespace Activant.StringHolder

    {

        public class StringHolder

        {

            public string holderString

            {

                get { return holderString; }

                set { holderString = value; }

            }

        }

    }

    I built the VS project and copied the dll into my OEA project. I added the generic property to my UserControl:

    USING Activant.StringHolder.* FROM ASSEMBLY.

    DEFINE PUBLIC PROPERTY ColTest AS "System.Collections.Generic.List<StringHolder>" NO-UNDO GET. SET.

    After saving the UserControl, I added it to my form in the Visual Designer. The ColTest property was available. When I selected the collection button, the dialog displayed properly (Image 4).

    I selected the Add button and received the error shown in Image 5.

    I also tried creating the holder class without the get and set.

    public class StringHolder

    {

        public string holderString;

    }

    After going through the same steps above, when I clicked Add it created an item, but I couldn't edit the value (Image 6).

    In addition, after selecting OK and saving the form, the item that was added was not written to InitializeComponent in the form class. When I selected the collection button again, there were no items.

    Regards,

    Jim

  • Ok, just reviewed our solution a second time.

    What's probably missing on your end is the

    [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]

    attribute on the List property in the UserControl. We are setting that using using our own ICustomTypeDescriptor implementation. This is - to my knowledge - the only way of adding attributes like the DesignerSerializationVisibilityAttribute in the OpenEdge Architect Visual Designer. When implementing this Interface in a Control or Component the design time instance itself becomes responsible to return (at design runtime) all information about how to handle the properties of the class etc.. Using that way you can "inject" additional attributes.

    This is what I get with our way of injecting the DesignerSerializationVisibilityAttribute:

            /*  */

            /* testUserControl1 */

            /*  */

            THIS-OBJECT:testUserControl1:BackColor = System.Drawing.Color:Red.

            THIS-OBJECT:testUserControl1:Location = NEW System.Drawing.Point(117, 79).

            THIS-OBJECT:testUserControl1:Name = "testUserControl1".

            THIS-OBJECT:testUserControl1:Size = NEW System.Drawing.Size(124, 139).

            stringHolder1:Value = "aaa".

            stringHolder2:Value = "bbb".

            THIS-OBJECT:testUserControl1:StringList:Add(stringHolder1).

            THIS-OBJECT:testUserControl1:StringList:Add(stringHolder2).

            THIS-OBJECT:testUserControl1:TabIndex = 0.

    Our C# class basically looks like yours. My String property is called "Value" and I have added an empty default constructor. But I doubt that is relevant.

    A few resources to look up for you:

    http://msdn.microsoft.com/en-us/library/system.componentmodel.icustomtypedescriptor.aspx

    http://blog.consultingwerk.de/consultingwerkblog/2011/06/presentation-download-for-customizing-the-openedge-architect-visual-designer/

    A while back we've made a strategic decision to not make that ICustomTypeDescriptor implementation open source because it's one of the key building blocks of our SmartComponent Libraries design time functionality.

    I know that Progress has this feature (ability to specify the .NET property attributes in a simpler way) on the list of future enhancements. I doubt it's high on the list though.

    A way to work around this as well would be to import Progress.NetUI.dll as an Assembly reference into that same C# project and create the user control with the property there. There you can use the simple .NET way of adding the attribute.

    Nachricht geändert durch Mike Fechner

  • Ok, I have something working (sort of).

    I created a class in C# that is just a holder for a System.String property. One difference from my earlier attempt is the [Serializable] attribute.

    using System;
    using System.Linq;
    using System.Text;

    namespace Activant.StringHolder
    {
       
    [Serializable]
        public class MyString
        {
            private string _value;
            public string Value
            {
                get
                {
                    return _value;
                }
                set
                {
                    _value = value;
                }
            }

            public MyString()
            {
            }
        }

    }

    After building the dll and bringing it into OEA, I added the following to my user control:

    DEFINE PUBLIC PROPERTY ColTest AS "System.Collections.Generic.List" NO-UNDO GET. SET.

    and in the Constructor, after InitializeComponent:

    ColTest = NEW "System.Collections.Generic.List"().

    When the User Control is added to my Form, there is a Property named ColTest in the Properties tab with a Collections button. After selecting it, adding some items, and selecting OK, the section for the UserControl in InitializeComponent looks like this:

    /*  */
    /* collectionProperty1 */
    /*  */
    THIS-OBJECT:collectionProperty1:ColTest = CAST(resources:GetObject("collectionProperty1.ColTest"), "System.Collections.Generic.List").
    THIS-OBJECT:collectionProperty1:Location = NEW System.Drawing.Point(73, 40).
    THIS-OBJECT:collectionProperty1:Name = "collectionProperty1".
    THIS-OBJECT:collectionProperty1:Size = NEW System.Drawing.Size(234, 222).
    THIS-OBJECT:collectionProperty1:TabIndex = 0.

    The collection's item values are written to the resx file and persist.

    This is not too bad of a workaround and it at least gives me the property grid for simple lists at design time. I should be able to extend this to allow the user of a keyed collection such as SortedList.

    Please feel free to comment on the solution.

    Jim

  • The collection's item values are written to the resx file and persist.

     

    If I'm not mistaking, the collection items are binary serialized to the resx file that way, right?

    I've came across the advice to not use that, when investigating about these topics on the web. The binary serialization is dependent on all the Assembly attributes like name, version, vendor key and language. If one of those changes, the binary serialization becomes worthless.

    From what you can influence, you shouldn't change the version of your assembly etc.. Not necessarily a recommended approach.

  • Hmmm. Yes, you are correct.

       

            AAEAAAD/////AQAAAAAAAAAMAgAAAENTdHJpbmdIb2xkZXIsIFZlcnNpb249MS4wLjAuMCwgQ3VsdHVy

            ZT1uZXV0cmFsLCBQdWJsaWNLZXlUb2tlbj1udWxsBAEAAACIAVN5c3RlbS5Db2xsZWN0aW9ucy5HZW5l

            cmljLkxpc3RgMVtbQWN0aXZhbnQuU3RyaW5nSG9sZGVyLk15U3RyaW5nLCBTdHJpbmdIb2xkZXIsIFZl

            cnNpb249MS4wLjAuMCwgQ3VsdHVyZT1uZXV0cmFsLCBQdWJsaWNLZXlUb2tlbj1udWxsXV0DAAAABl9p

            dGVtcwVfc2l6ZQhfdmVyc2lvbgQAACBBY3RpdmFudC5TdHJpbmdIb2xkZXIuTXlTdHJpbmdbXQIAAAAI

            CAkDAAAAAQAAAAQAAAAHAwAAAAABAAAABAAAAAQeQWN0aXZhbnQuU3RyaW5nSG9sZGVyLk15U3RyaW5n

            AgAAAAkEAAAADQMFBAAAAB5BY3RpdmFudC5TdHJpbmdIb2xkZXIuTXlTdHJpbmcBAAAABl92YWx1ZQEC

            AAAABgUAAAAEdGVzdAs=

     

    Any suggestions outside of creating the User Control in C#. Not sure I want to (or can, for other reasons) go there.

  • Any suggestions outside of creating the User Control in C#. Not sure I want to (or can, for other reasons) go there.

    None that I haven't mentioned before. ICustomTypeDescriptor is my favorite. And it's actually a great way of getting to understand .NET internals.

    Of course you might purchase a framework that supports this... (sorry, couldn't resist). Or parts of it.