MVC Generated ViewModel UI in MDrivenFramework
No edit summary
No edit summary
Line 1: Line 1:
Note: since this article was written we have added FileUpload control to MVC and to use that you must have a form definition that sets the encoding type to multipart like this:
Html.BeginForm("Submit", Html.GetControllerName(), FormMethod.Post, new { '''enctype = "multipart/form-data"''' }))
This article describes how to use a standard MVC c# Visual Studio 2019 project with MDriven generated ViewModel views.
This article describes how to use a standard MVC c# Visual Studio 2019 project with MDriven generated ViewModel views.



Revision as of 14:36, 25 October 2022

Note: since this article was written we have added FileUpload control to MVC and to use that you must have a form definition that sets the encoding type to multipart like this:

Html.BeginForm("Submit", Html.GetControllerName(), FormMethod.Post, new { enctype = "multipart/form-data" }))

This article describes how to use a standard MVC c# Visual Studio 2019 project with MDriven generated ViewModel views.

You are free to use any name on the controller - in this sample it is called "My".

The MDriven generated MVC UI needs a view called GenericView.cshtml , a template is provided here:

@using Eco.ViewModel.Runtime
@using Eco.MVC
@model VMClass

@{
    ViewBag.Title = Model.ViewModelClass.Name;
}

<style>
    .tk-data-table {
        padding: 5px;
        display: flex;
        flex-direction: column;
        justify-content: flex-start;
        flex-grow: 1;
        width: 100%;
        min-height: 90px;
        height: 100%;
        overflow-y: auto;
    }

    .tk-data-table.ctGridMidAirY .tk-data-table__content {
        height: 100%;
    }

        .tk-data-table.ctGridYBottom .tk-data-table__content {
            height: 100%;
        }

    .tk-data-table__content {
        border-color: #dadce0;
    }

    .tk-data-table__content {
        margin-top: 10px;
        position: relative;
        width: 100%;
        overflow: auto;
        border-radius: 4px;
        border: 1px solid #dadce0;
    }

    .tk-text-field {
        padding: 5px;
        display: flex;
        flex-direction: column;
        justify-content: flex-start;
    }

    .tk-button.NoLabel {
         padding: calc(1rem * 1.5 + 5px) 5px 5px 5px; 
    }



</style>

@using (Html.BeginForm("Submit", Html.GetControllerName(), FormMethod.Post))
{
<fieldset>
    <div id="contentWrapper" class="@Model.ViewModelClass.Name mvc-rendered">
        <!-- Start of inserted MVC ViewModel body -->
        <div id="viewmodelWrapper">
            @Html.Partial(Html.RazorPartialFile());
            <!-- End of inserted MVC ViewModel body -->
        </div>
    </div>
    <div>
        <button id="SubmitButton" type="submit" value="Submit" class="tk-state-action ripple-effect update-action">Submit</button>
    </div>
    <div id="validationMessageWrapper">
        <div class="validationMessage">
            @Html.ValidationSummary(true)
        </div>
    </div>
    <div class="form-group" style="display:none">
        @* When form is posted, the VMClassBinder recreates the viewmodel with data from the form, starting with the viewmodels name and root id below *@
        @Html.Hidden(VMClass.ThisAsExternalId_nameOf) <!-- This creates a hidden form attribute with the root objects external ID -->
        @Html.Hidden(VMClassBinder.ViewModelNameFormAttribute, Model.ViewModelClass.ViewModel.RootViewModelClass.Name)
    </div>
</fieldset>
}


<script>
    var _this = this;


    function SubmitAction(event, target, theaction, areYouSureQuestion) {
        if (areYouSureQuestion === void 0) { areYouSureQuestion = ""; }
        if (event !== undefined)
            event.stopPropagation();
        if (areYouSureQuestion !== undefined && areYouSureQuestion != '') {
            var answer = window.confirm(areYouSureQuestion);
            if (!answer)
                return;
        }
        var theform = $(target).closest('form');
        $(theform).attr('action', '/'+theaction); // a bit unclear why I need the '/' , but routing adds controller twice otherwise ; probably not needed in Turnkey because of use of Angular JQuery
        theform.submit();
    };

    function NavigateTo(event, target, url, areYouSureQuestion) {
        if (areYouSureQuestion === void 0) { areYouSureQuestion = ""; }
        if (event !== undefined)
            event.stopPropagation();
        if (areYouSureQuestion !== undefined && areYouSureQuestion != '') {
            var answer = window.confirm(areYouSureQuestion);
            if (!answer)
                return;
        }
        var theform = $(target).closest('form');
        window.location.href = url;
    };
    // Toggle row check-box
    function ToggleRow(event, target) {
        if (event !== undefined) {
            event.stopPropagation();
            if (event.target.type == 'checkbox')
                return; // if the automated click below bubbles up and calls again
        }
        var theform = $(".selector", target).click();
    };
    // Toggle row check-box
    function SetCurrentRow(event, target) {
        if (event !== undefined) {
            event.stopPropagation();
            if (event.target.type == 'checkbox')
                return; // if the automated click below bubbles up and calls again
        }
        $(".selector", target.parentElement).removeAttr('checked'); // Unselect all checked in the list
        $(".vmTableRow", target.parentElement).removeClass("vmCurrentRow"); // Remove the current visual style 
        $(".selector", target).click(); // Click the hidden checkbox
        var theform = $(target).closest('form');
        theform.submit();
    };

    
</script>

Note that you may want to seperate the css and script into your own files - but they are kept as one unit for better overview here.

The scripts are mainly for allowing buttons to execute actions, and grid rows to allow row select and possibly action execution.

The central part where the generated UI is inserted by Eco.MVC is here:

 @Html.Partial(Html.RazorPartialFile());

Copy the above html template into a file you create in your MVC project here Views/My/GenericView.cshtml.

Create a controller in Controllers/MyController.cs and make the controller a subcklass of a ModelDrivenControllerBase like this:

  public class MyController : ModelDrivenControllerBase<EcoProject1EcoSpace>
  {
    public MyController() : base()
    {
      RenderSettings.UseCSSGridByDefault = true;

    }

    public ActionResult TestStart2()
    {
      var vmc = Eco.ViewModel.Runtime.ViewModelHelper.CreateFromViewModel("SampleViewModel", this.EcoSpace, null, false);
      return View("GenericView", vmc);
    }
  }

The "SampleViewModel" should be an existing MDriven ViewModel. You can then add a MVC meny option in your _Layout.cshtml to show this view like this:

@Html.ActionLink("Test2", "TestStart2", "My")

Note that the content of the view can be made to shift (navigate to other view) by placing navigating buttons on your viewmodel in MDriven or having a single action on grid rows.

To do more advanced stuff as; render the main menu from model, bring up modal windows, ajax async load data, keep unsaved state, bring up action choices etc - you can either roll your own with standard c# MVC or refer to MDriven Turnkey that provides all this functionality for you.

This page was edited 81 days ago on 02/10/2024. What links here