csharp/Aaltuj/VxFormGenerator/VxFormGenerator.Core/Render/VxFormColumnBase.cs

VxFormColumnBase.cs
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Rendering;
using System;
using System.Collections.Generic;
using System.Dynamic;
using System.Globalization;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using VxFormGenerator.Core.Layout;

namespace VxFormGenerator.Core.Render
{
    public clast VxFormColumnBase : OwningComponentBase
    {
        [CascadingParameter] public VxFormLayoutOptions FormLayoutOptions { get; set; }

        public string CssClast
        {
            get
            {

                if (FormLayoutOptions.LabelOrientation == LabelOrientation.TOP && this.FormColumnDefinition.RenderOptions.ColSpan > 0)
                    return $"col-{this.FormColumnDefinition.RenderOptions.ColSpan}";
                else
                    return "col";
            }
        }

        public string Style
        {
            get
            {

                if (FormLayoutOptions.LabelOrientation == LabelOrientation.LEFT && FormColumnDefinition.RenderOptions.ColSpan > 0)
                {
                    var colspan = Math.Round(FormColumnDefinition.RenderOptions.ColSpan * (100.0 - 25.0) / 12.0);
                    string colspanS = colspan.ToString(CultureInfo.InvariantCulture);
                    return $"flex: 0 0 {colspanS}%; max-width: {colspanS}";
                }
                return "";
            }
        }
        [Parameter] public Layout.VxFormElementDefinition FormColumnDefinition { get; set; }

        public RenderFragment CreateFormElement() => builder =>
        {
            if (FormColumnDefinition.Model.GetType() == typeof(ExpandoObject))
            {
                // Accesing a ExpandoObject requires to cast the model as a dictionary, so it's accesable by a key of type string
                var accessor = ((IDictionary)FormColumnDefinition.Model);

                foreach (var key in accessor.Keys)
                {
                    // get the value of the object
                    var value = accessor[key];

                    // Get the generic CreateFormComponent and set the property type of the model and the elementType that is rendered
                    MethodInfo method = typeof(VxFormColumnBase).GetMethod(nameof(VxFormColumnBase.CreateFormElementReferenceExpando), BindingFlags.NonPublic | BindingFlags.Instance);
                    MethodInfo genericMethod = method.MakeGenericMethod(value.GetType());
                    // Execute the method with the following parameters
                    genericMethod.Invoke(this, new object[] { accessor, key, builder, FormColumnDefinition });
                }
            }
            else // astume it's a regular clast, could be tighter scoped
            {
                var propertyFormElement = FormColumnDefinition.Model.GetType().GetProperty(FormColumnDefinition.Name);
                // Get the generic CreateFormComponent and set the property type of the model and the elementType that is rendered
                MethodInfo method = typeof(VxFormColumnBase).GetMethod(nameof(VxFormColumnBase.CreateFormElementReferencePoco), BindingFlags.NonPublic | BindingFlags.Instance);
                MethodInfo genericMethod = method.MakeGenericMethod(propertyFormElement.PropertyType);
                // Execute the method with the following parameters
                genericMethod.Invoke(this, new object[] { FormColumnDefinition.Model, propertyFormElement, builder, FormColumnDefinition });
            }
        };

        private void CreateFormElementReferencePoco(object model, PropertyInfo propertyInfo,
            RenderTreeBuilder builder, Layout.VxFormElementDefinition formColumnDefinition)
        {
            var valueChanged = Microsoft.AspNetCore.Components.CompilerServices.RuntimeHelpers.TypeCheck(
                        EventCallback.Factory.Create(
                            this, EventCallback.Factory.
                            CreateInferred(this, __value => propertyInfo.SetValue(model, __value),

                            (TValue)propertyInfo.GetValue(model))));
            // Create an expression to set the ValueExpression-attribute.
            var constant = Expression.Constant(model, model.GetType());
            var exp = Expression.Property(constant, propertyInfo.Name);
            var lamb = Expression.Lambda(exp);

            var formElementReference = new FormElementReference()
            {
                Value = (TValue)propertyInfo.GetValue(model),
                ValueChanged = valueChanged,
                ValueExpression = lamb,
                FormColumnDefinition = formColumnDefinition
            };

            var elementType = typeof(VxFormElementLoader);

            builder.OpenComponent(0, elementType);
            builder.AddAttribute(1, nameof(VxFormElementLoader.ValueReference), formElementReference);
            builder.CloseComponent();
        }

        /// 
        /// Create a  that will create a 
        /// based on the dynamic . This allows for dynamic usage of the form-generator.
        /// 
        /// 
        /// 
        /// 
        /// 
        private void CreateFormElementReferenceExpando(ExpandoObject model, string key,
            RenderTreeBuilder builder, Layout.VxFormElementDefinition formColumnDefinition)
        {
            // cast the model to a dictionary so it's accessable
            var accessor = ((IDictionary)model);

            object value = default(TValue);
            accessor.TryGetValue(key, out value);

            var valueChanged = Microsoft.AspNetCore.Components.CompilerServices.RuntimeHelpers.TypeCheck(
                        EventCallback.Factory.Create(
                            this, EventCallback.Factory.
                            CreateInferred(this, __value => accessor[key] = __value,
                            (TValue)accessor[key])));

            var formElementReference = new FormElementReference()
            {
                Value = (TValue)value,
                ValueChanged = valueChanged,
                FormColumnDefinition = formColumnDefinition
            };

            var elementType = typeof(VxFormElementLoader);

            builder.OpenComponent(0, elementType);
            builder.AddAttribute(1, nameof(VxFormElementLoader.ValueReference), formElementReference);
            builder.CloseComponent();
        }

    }
}