MTran
MTran

Reputation: 1809

CodeFluent Aspect: How to Set up DropDown Input with Entity Properties

I'm developing an Full-Text Index Aspect, and I got to the point where I can specify a property to be a full-text index.

However, the next thing I want to do is specifying inside the SQL Full Text Index syntax, the "TYPE COLUMN xx", where "xx" is another property of the same entity.

To that end, I would like to ask with the CodeFluent Aspects, how do I set it to provide a drop down list of all other persisted properties for current entity for an aspect input?

Here's the CodeFluent Aspect XML code I have so far:

        static FullTextIndexing()
        {
            Descriptor = new XmlDocument();
            Descriptor.LoadXml(
@"<cf:project xmlns:cf='http://www.softfluent.com/codefluent/2005/1' defaultNamespace='FullTextIndexing'>
    <cf:pattern name='Full Text Indexing' namespaceUri='" + NamespaceUri + @"' preferredPrefix='ftind' step='Tables'>
        <cf:message class='_doc'>CodeFluent Full Text Indexing Aspect</cf:message>
        <cf:descriptor name='fullTextIndex'
            typeName='boolean'
            category='Full Text Index'
            targets='Property'
            defaultValue='false'
            displayName='Full-Text Index'
            description='Determines if property should be a full text index.' />
        <cf:descriptor name='fullTextIndexTypeColumn'
            typeName='text'
            category='Full Text Index'
            targets='Property'
            displayName='Type Column'
            description='The type column for the full text index.' />
    </cf:pattern>
</cf:project>");
        }

This gives me a "text box". What I want is a drop down of the other properties of the same entity.

CodeFluent Image of Aspect Inputs

Edit One:

I tried to use the UITypeEditor to make a dropdown, but it doesn't seem to be working. The "Type Column" is greyed out, and it has a black box.

Image of Type Editor Error

I might be doing something wrong.

My custom UITypeEditor class is as follows:

namespace CodeFluent.Aspects.AspectEditors
{
    public class OtherPropertyDropDownEditor : UITypeEditor
    {
        private IWindowsFormsEditorService _editorService;
        
        public override UITypeEditorEditStyle GetEditStyle(ITypeDescriptorContext context)
        {
            // drop down mode (we'll host a listbox in the drop down)
            return UITypeEditorEditStyle.DropDown;
        }

        public override object EditValue(ITypeDescriptorContext context, IServiceProvider provider, object value)
        {
            _editorService = (IWindowsFormsEditorService)provider.GetService(typeof(IWindowsFormsEditorService));

            // use a list box
            ListBox lb = new ListBox();
            lb.SelectionMode = SelectionMode.One;
            lb.SelectedValueChanged += delegate
            {
                // close the drop down as soon as something is clicked
                _editorService.CloseDropDown();
            };

            // use the Property.Name property for list box display
            lb.DisplayMember = "Name";

            // this is how we get the list of possible properties
            IEnumerable<Property> otherProperties = GetOtherPersistentProperties(context);
            foreach (Property otherProperty in otherProperties)
            {
                int index = lb.Items.Add(otherProperty);
                if (otherProperty.Equals(value))
                {
                    lb.SelectedIndex = index;
                }
            }


            // show this model stuff
            _editorService.DropDownControl(lb);
            if (lb.SelectedItem == null) // no selection, return the passed-in value as is
                return value;

            return lb.SelectedItem;
        }

        private IEnumerable<Property> GetOtherPersistentProperties(ITypeDescriptorContext context)
        {
            // context is of type ITypeDescriptorContext, got from EditValue overloads.
            var property = TypeNameEditor.GetObject<Property>(context);

            IEnumerable<Property> otherEntityProperties = null;
            if (property != null && property.Entity != null)
                otherEntityProperties = property.Entity.Properties.Where(p => p.IsPersistent && p != property);
            return otherEntityProperties;
        }
    }
}

The XML I have so far is this. Notice I added the "editorTypeName".

        static FullTextIndexing()
        {
            Descriptor = new XmlDocument();
            Descriptor.LoadXml(
@"<cf:project xmlns:cf='http://www.softfluent.com/codefluent/2005/1' defaultNamespace='FullTextIndexing'>
    <cf:pattern name='Full Text Indexing' namespaceUri='" + NamespaceUri + @"' preferredPrefix='ftind' step='Tables'>
        <cf:message class='_doc'>CodeFluent Full Text Indexing Aspect</cf:message>
        <cf:descriptor name='fullTextIndex'
            typeName='boolean'
            category='Full Text Index'
            targets='Property'
            defaultValue='false'
            displayName='Full-Text Index'
            description='Determines if property should be a full text index.' />
        <cf:descriptor name='fullTextIndexTypeColumn'
            category='Full Text Index'
            targets='Property'
            editorTypeName='CodeFluent.Aspects.AspectEditors.OtherPropertyDropDownEditor, CodeFluent.Aspects.AspectEditors.OtherPropertyDropDownEditor, CodeFluent.Aspects.AspectEditors'
            displayName='Type Column'
            description='The type column for the full text index.'
            />
    </cf:pattern>
</cf:project>");
        }

Upvotes: 3

Views: 90

Answers (1)

Simon Mourier
Simon Mourier

Reputation: 139187

What you can do is add an xml attribute to your descriptor to define a custom TypeConverter type name, for example:

<cf:descriptor name='fullTextIndexTypeColumn'
            typeName='text'
            category='Full Text Index'
            targets='Property'
            displayName='Type Column'
            description='The type column for the full text index.'
            typeConverterTypeName='ClassLibrary1.MyAspectConverter, ClassLibrary1'
            />

Then you need to implement the MyAspectConverter class (here in a ClassLibrary1.dll), for example like this:

public class MyAspectConverter : StringConverter
{
    public override bool GetStandardValuesSupported(ITypeDescriptorContext context)
    {
        return true;
    }

    public override StandardValuesCollection GetStandardValues(ITypeDescriptorContext context)
    {
        var list = new List<string>();
        var property = TypeNameEditor.GetObject<Property>(context);
        if (property != null && property.Entity != null)
        {
            list.AddRange(property.Entity.Properties.Where(p => p.IsPersistent).Select(p => p.Name));
        }
        return new StandardValuesCollection(list);
    }
}

ClassLibrary1 needs to reference CodeFluent.Runtime.dll, CodeFluent.Model.Common.dll and CodeFluent.Model.dll (in general from C:\Program Files (x86)\SoftFluent\CodeFluent\Modeler).

You will need to copy the ClassLibrary1.dll that contains this converter to Visual Studio where the IDE can load it from, for example in C:\Program Files (x86)\Microsoft Visual Studio 14.0\Common7\IDE for Visual Studio 2015.

Note if you define your aspect in code, you can put this converter class in the same DLL, but you'll always need to copy it in Visual Studio directory.

Restart Visual Studio, and you should see something like this in the Visual Studio property grid:

enter image description here

As stated in the comments, you can also create a UITypeEditor using the same principle, if you need more advanced editing (and use the 'editorTypeName' XML attribute instead of the 'typeConverterTypeName' attribute), but it's not needed for a list of strings.

Upvotes: 1

Related Questions