How to fill custom properties dynamically with model information at runtime?

Answered How to fill custom properties dynamically with model information at runtime?

  • Thursday, August 04, 2005 11:35 PM
     
     

    Is there a way to fill custom properties with model information at runtime? For example, instead of defining an enumeration at design time with hard coded values "One" and "Two", I would like to generate the enumeration values dynamically at runtime based in values defined somewhere in my model. So I'd say something like "this enumeration shall contain all properties values of all value properties in domain call A and B". In consequence, if the referenced model values change, the values in my enumeration would change dynamically as well.

    This would be interesting to enforce model consistency. Instead of asking the user to type or select “a” value, I would make sure only those values can be selected (and subsequently used for code generation) that already exist in the model.


    Cheers
    Alex

All Replies

  • Wednesday, August 10, 2005 7:47 PM
     
     
    Alex,

    What you're describing sounds similar to the scenario we had in mind when we added support for reference relationships in the property grid.  If you define two classes, A and B, and a 1-* reference relationship between them, if you select an A on your designer, you'll see a property show up in the grid with a drop-down that allows you to select from all the instances of B in the model.  So, if you were to maintain a set of B's in your model, one corresponding to each possible value of the property on A, we'd handle all of the property grid stuff for you atutomatically.

    Of course, this may not fit your particular scenario.  For instance, you may want the choices available to be restricted based on some other properties of the particular instance of A, or you may not want to model the property as a relationship.  If this is the case, then you can write some custom code to modify the property grid behavior.  You'll notice in the domain model designer that there is a property called ClrAttributes on a domain property.  This allows you to specify custom CLR attributes that get placed in the generated code, including the standard set of .NET attributes which control property grid behavior.  For example, you could create a custom TypeConverter class which controlled the values that appeared in the drop-down list, and apply the TypeConverterAttribute to your domain property, specifying the type of your custom type converter.  If you went with this approach, you'd have to specify the type of your domain property as string.

    We don't have an existing code sample that illustrates the custom propreties approach, but please let me know if you'd like to see such a sample.

    Thanks,
    Grayson
  • Thursday, August 11, 2005 9:44 PM
     
     

    Hi Grayson,

    thanks for the reply. An example would be much appreciated to better digest all details.

    Thanks
    Alex

  • Friday, August 12, 2005 1:50 AM
     
     Answered
    OK, here's a simple one I came up with.  "Kind" is a string domain property on a class called "Entity".  There is a second domain property on "Entity" called "AllowCustomValues".  If this is true, then the property grid will accept any values you want to type in for the "Kind" property, if it is false, then you'll be restricted to using the two pre-defined values "Persistent" and "Transient".

    Step 1: Define the TypeConverter in a file called KindTypeConverter.cs in the DomainModel project, as follows:


    using System;
    using System.ComponentModel;
    using System.Collections.Generic;
    using System.Text;
    using Microsoft.VisualStudio.Modeling;

    namespace Fabrikam.EntityRelationship.DomainModel
    {
       
    class EntityKindTypeConverter : StringConverter
       
    {

          /// <summary>
          
    /// Indicates that we support drop-down editing for this property.
          
    /// </summary>
          
    public override bool GetStandardValuesSupported(ITypeDescriptorContext context)
          {
             return
    true;
          }

          /// <summary>
          
    /// Indicates whether we or not we restrict the user to an exclusive list of values.
          
    /// </summary>
          
    public override bool GetStandardValuesExclusive(ITypeDescriptorContext context)
          {
             
    // BUG: the value of context.Instance should always be the entity itself, since the
             
    // domain property is defined on the Entity class. However, currently it is shape, if
             
    // you select it on the design surface. Code below works around this.
             
    Entity entity;
             
    PresentationElement pe = context.Instance as PresentationElement;
             if (pe != null)
             {
                entity = pe.ModelElement
    as Entity;
             }
             
    else
             
    {
                entity = context.Instance
    as Entity;
             }
          
             
    return entity != null ? !entity.AllowCustomValues : true;
          }

          /// <summary>
          
    /// Return the actual collection of values we support
          
    /// </summary>
          
    public override StandardValuesCollection GetStandardValues(ITypeDescriptorContext context)
          {
             
    return new StandardValuesCollection(new string[] { "Persistent", "Transient" });
          }
       }
    }


     



    Step 2: Apply the attribute to you domain model which hooks up the domain property to this custom type converter (you do this through the designer, so I'll just describe how):

    a.  Select the Kind property.
    b.  In the property grid, open the custom editor for the CLR Attributes property.
    c.  Press the "New" button.
    d.  Enter this text for the name: System.ComponentModel.TypeConverter
    e.  Enter this text for the parameter text (replace with your namespace): typeof(Fabrikam.EntityRelationship.DomainModel.EntityKindTypeConverter)
    f.  Generate code and rebuild.

    That should do it.  As you've probably noticed, you could also modify the sample above slightly to dynamically restrict the values which appear based on some other calculation using your domain model.  Hope this helps.

    Thanks,
    Grayson