🚀 Welcome to MDriven Learn –  MDriven is now on Discord!  Don’t miss the latest Release Notes.
MVC Generated ViewModel UI in MDrivenFramework
This page was created by Hans.karlsen on 2020-08-13. Last edited by Wikiadmin on 2026-07-29.

You can render a model-defined ViewModel as a generated Razor UI in a standard ASP.NET MVC project using MDrivenFramework; this page is for developers who want that generated UI without using the full Turnkey application.

What you build

Your MVC application creates a runtime VMClass from a ViewModel, passes it to one reusable Razor view, and lets Eco.MVC insert the generated ViewModel-specific Razor partial.

For example, an action named TestStart2 creates the SampleViewModel ViewModel and returns Views/My/GenericView.cshtml. The GenericView template renders the generated fields, grids, and actions for that ViewModel.

Part Responsibility
ViewModel in MDriven Designer Defines the data, layout, actions, and navigation that the generated UI represents.
Controller Creates the runtime ViewModel instance and returns the shared GenericView template.
GenericView.cshtml Hosts the generated partial and provides the form, validation summary, styling, and JavaScript functions used by the generated UI.
Generated Razor partial Is selected by Html.RazorPartialFile() and contains the UI generated for the current ViewModel.

Before you start

  1. Create a standard MVC C# project. This example was written for a Visual Studio 2019 MVC project.
  2. Add the MDrivenFramework dependencies required by your project, including the namespaces used below: Eco.ViewModel.Runtime and Eco.MVC.
  3. Create the ViewModel that you want to render in MDriven Designer. In the example below, its name is SampleViewModel.
  4. Ensure that your controller can use the generated EcoSpace type. The example uses EcoProject1EcoSpace; replace that type with your project's EcoSpace type.

A ViewModel is the layer that exposes the model data and operations required for one UI task. It lets you render an intended perspective of the model rather than exposing business objects directly. For ViewModel design and code generation, see Documentation:ViewModel and Documentation:MVC View Model handling.

Create the shared generated-UI view

Create this file in your MVC project:

Views/My/GenericView.cshtml

My is the controller name used in this example. You may use another controller name, but place the view in that controller's view folder or adjust your view lookup accordingly.

@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,
    .tk-data-table.ctGridYBottom .tk-data-table__content {
        height: 100%;
    }

    .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">
            <div id="viewmodelWrapper">
                @Html.Partial(Html.RazorPartialFile())
            </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">
            @* VMClassBinder uses these values to recreate the ViewModel on postback. *@
            @Html.Hidden(VMClass.ThisAsExternalId_nameOf)
            @Html.Hidden(VMClassBinder.ViewModelNameFormAttribute, Model.ViewModelClass.ViewModel.RootViewModelClass.Name)
        </div>
    </fieldset>
}

<script>
    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);
        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;
        }

        window.location.href = url;
    }

    function ToggleRow(event, target) {
        if (event !== undefined) {
            event.stopPropagation();
            if (event.target.type == 'checkbox') return;
        }
        $(".selector", target).click();
    }

    function SetCurrentRow(event, target) {
        if (event !== undefined) {
            event.stopPropagation();
            if (event.target.type == 'checkbox') return;
        }

        $(".selector", target.parentElement).removeAttr('checked');
        $(".vmTableRow", target.parentElement).removeClass("vmCurrentRow");
        $(".selector", target).click();
        $(target).closest('form').submit();
    }
</script>

How the template works

The central statement is:

@Html.Partial(Html.RazorPartialFile())

Html.RazorPartialFile() identifies the Razor partial generated for the ViewModel currently held by Model. Html.Partial(...) renders that partial inside the form.

The model declaration must remain @model VMClass. The runtime-generated ViewModel UI is rendered from this base type.

The two hidden fields are required for a stateless postback. They send the root object's external ID and the ViewModel name, allowing VMClassBinder to recreate the ViewModel when the form is posted. Do not remove them when customizing the template.

The script functions support generated actions and grid interaction:

  • SubmitAction posts the nearest form to an action route and can ask the user for confirmation.
  • NavigateTo navigates to a supplied URL and can ask the user for confirmation.
  • ToggleRow toggles a grid row selector.
  • SetCurrentRow clears the existing selection, selects the clicked row, and posts the form.

The template keeps CSS and JavaScript together so that the required structure is visible. In your application, you can move those rules and functions to your own CSS and JavaScript files, provided they are loaded on pages that render generated ViewModel UI.

Create the controller

Create Controllers/MyController.cs. The controller name can be different, but the view location and layout link in this example use My.

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);
    }
}

Replace EcoProject1EcoSpace with your EcoSpace type and SampleViewModel with the exact name of an existing ViewModel.

ViewModelHelper.CreateFromViewModel creates the runtime ViewModel instance. The returned instance is passed as the model to GenericView, which then locates and renders the generated partial.

Add a route into the page

Add a link in _Layout.cshtml, or in another navigation view, to open the controller action:

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

This link targets TestStart2 on MyController. If you renamed the controller or action, update the link to match.

Enable file upload

If the generated ViewModel UI includes a file-upload control, the enclosing form must use multipart form encoding. Change the Html.BeginForm line in GenericView.cshtml to:

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

Without enctype="multipart/form-data", the browser does not submit file content as a multipart form post.

MVC interaction behavior

Generated MVC UI does not behave exactly like the Angular UI used for most Turnkey pages.

Situation MVC behavior
A grid row has one ViewModel action Clicking the row executes that action.
A grid row has zero or multiple ViewModel actions Clicking the row selects the row.
A grid row has multiple actions MVC does not show the multiple-action popup menu that Angular shows.

You can make the rendered content navigate to another view by adding navigation buttons to the ViewModel. A grid with one action also uses that action on row click.

Customize and extend

Use this approach when you want to host generated ViewModel UI in your own MVC application and control the surrounding Razor layout. For examples of rendering generated ViewModels without Turnkey, see Documentation:Render MVC ViewModel without turnkey.

For features such as a model-driven main menu, modal windows, asynchronous loading, retaining unsaved state, or action-choice presentation, implement the required MVC behavior in your application or use MDriven Turnkey's MVC and Angular arrangement where appropriate.

If you create MVC views from code-generated ViewModels instead of rendering the runtime-generated partial, use the ViewModel binding and validation guidance in Documentation:MVC View Model handling. For a reference picklist rendered as a dropdown, see Documentation:Comboboxes in MVC from model driven ViewModel.

See also