Views and Templates

From GeoMedia Smart Client
Jump to: navigation, search
Design Views and Templates Theme Examples



The GraphicalUserInterface (GUI) of a Workflow is build with Views and Templates. They define the structure of the html markup and the widgets. This section is about the concept and the usage of Views and Templates in Workflows.


Views and Templates

Views

Based on the controller-attribute of the WorkflowNode the corresponding View is getting loaded. According to the three different Controllers Workflow, List and Form we offer three different Views. Like in common ASP.NET MVC we follow the same idea to seperate layout, structure and content. The RootMasterView describes the structure of the html-markup and seperates the GUI in different sections. The SiteMasterView describes the content of some consistent sections, like the Navigationbar and the Historybar . The variable sections are defined by one of the three Views. The View is loading, depending on the definitions of the Form, the corresponding Templates to build up the GUI.

Views

Structure and layout of a simple Form

Structure and Layout of a simple Form

Templates

Templates are stored in the same directory like views, they are also C#-Html Files (.cshtml) but they differ in usage. Views are always linked with the WorkflowSettings and templates are linked with the FormSettings. For a better understanding of the usage of templates we seperated it into two parts, Structure and Widget Templates.

Templates

Structure Templates

The templates Filter, Form, Tab, Group and Singlerow define the structure of the GUI. Except the Group and Singlerow Template, all other Structure Templates are used only once either by its parent template or View. The call graph of the templates are equal to the structure of the FormSettings, the Form Template uses the Tab Template, the Tab Template uses one or more Group Templates, the Group Template uses one or more Widget Templates and it can use additionally one or more Singlerow Templates, which also uses one ore more Widget Templates.

Structure Templates

Widget Templates

A FormField has based on its type attribute an own default template. It is possible to load a custom template for the widget by editing the template attribute of the FormField (see Custom Template). A Widget Template is always strongly typed on the Workflow-Server (see .Net API Reference - FormField Class and on the Workflow-Client (see JavaScript API - FieldModels). It never uses a Structure Template and defines only the represantaion of the Workflow-Server Model and the data-bindings (see Knockout.js Binding-Syntax and R.Niemeyer Knockout-Kendo) between Html-Markup and Workflow-Client Model.

Widget Template

Controls and Contents

The JavaScript API, the Workflow Theme and additional JavaScript - Files (.js) and CascadingStyleSheets (.css) are getting loaded in partial views which we prefix with "Script" or "Style" and postfix with "Control", for example ScriptFormControl contains all JavaScript - Files which is necessary for the Form View.

Controls and Contents

Script Controls

A Script Control is a partial view which is getting loaded by and . It renders the ASP.NET MVC ScriptBundles (see MVC Bundling and Minification).


Theme

Blue.png The theme folder and file structure has been changed in GMSC 2014. Visit the new Workflow theme-Documentation.



GMSC 2013
The Workflow uses as default theme the SmartClient Theme. We use DotLess to compile the Workflow Theme, the result is a dynamic CascadingStyleSheet (CSS) which we add to our IG - Theme Bundle (see MVC Bundling and Minification). As Theme we define a special Folder / File - Structure which always looks as follows:

Structure File Description
Structure of a Theme ig-icon-sprite.png All IG-Icons as a sprite
variables-layout.less Definitions of sizing and layout of the GUI. Based on the mixins of Twitter Bootstrap
variables-content.less Style-definitions of widgets in form-content-area. Based on KendoUI - Theme
variables-menu.less Style-definitions of widgets in form-menu-area. Inherits definitions of variables-content.less


Client Model

As Workflow Client Model we define the JavaScript-Object IG.Form which represents the model of the form on the Workflow Client. We use a JavaScript Model behind the Html-Markup to handle interaction with the GUI and to reach the same approach of ASP.NET Model View Control (MVC). Additionally, we provide in the JavaScript API some JavaScript Utility Functions and JavaScript Validations to extend functionality of the Workflow Client.


Workflow Server - Client Communication


Server Client Communication

Customization

The GeoMedia Smart Client Workflow Engine provides optionality customization in a number of certain Areas. The next chapters of this documentaion section is about customization on Views, Templates and Workflow Client Model


Custom View

This section describes an example how to use a Custom View, therefore we create a simple Workflow SearchAddress with following settings:

Form Settings - SearchAddress
  1. <FormList>
  2.   <Form name="SearchingAddress">
  3.     <FormTab name="SearchingAddressTab" label="Tab1">
  4.       <FormGroup name="SearchingAddressGroup">
  5.         <FormField name="NAME" type="autocomplete" datatype="string" help="Name of a building" 
  6.                    lov="SQL[Select NAME From ADDRESS Where upper(NAME) Like '%' || upper({FORM.NAME}) || '%' order by NAME]" />
  7.         <FormField name="ADDRESS" type="textarea" datatype="string" defaultvalue="SQL[Select ADDRESS From ADDRESS Where NAME = {FORM.NAME}]">
  8.           <FormAction name="Notify" image="ig-icon-ok" action="SCRIPT[IG.notify(IG.getItemById('ADDRESS').value()).show()]" />
  9.         </FormField>
  10.       </FormGroup>
  11.     </FormTab>
  12.     <FormTab name="SearchingAddressTab2" label="Tab2">
  13.       <FormGroup name="SearchingAddressGroup2">
  14.         <FormField name="Information" />
  15.       </FormGroup>
  16.     </FormTab>
  17.     <FormAction name="Ok" action="SCRIPT[IG.notify('You pressed - OK').show()]" />
  18.   </Form>
  19. </FormList>
Workflow Settings - SearchAddress
  1. <WorkflowRoot>
  2.   <WorkflowNode id="0">
  3.     <WorkflowNode id="1" label="Searching for a address" controller="Form" form="SearchingAddress" emptyform="true"  />
  4.   </WorkflowNode>
  5. </WorkflowRoot>



According to that settings we obtain following GUI with the default Form View:

Default Form View

Default Form View

As in section Views described the Form View is using two Master Views, which are representing the structure of the GUI.
The goal of this Workflow example is to change the structure and layout of the Form, which shall fit into a LeftPanel - bar of another application. Therefore we created a new Form View named OnceColumnForm, which uses custom a SiteMaster named OneColumnSiteMaster and a custom RootMaster named OneColumnRootMaster.

Following tables are showing the difference between the default RootMaster and the custom OneColumnRootMaster. Both files are located in the views directory "Views\Shared".
Default RootMaster
  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4.     <title>@Html.Raw(ViewBag.Title)</title>
  5.     <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
  6.     <meta http-equiv="X-UA-Compatible" content="IE=Edge" />
  7.     @{ Html.RenderPartial("StyleRootControl"); }
  8.     @RenderSection("RowActionTemplateSection", required: false)
  9.     @{
  10.         Html.RenderPartial("ScriptRootControl");
  11.         Html.RenderPartial("IGNotificationTemplate");
  12.         Html.RenderPartial("IGConfirmTemplate");
  13.     }
  14.     @RenderSection("ScriptSection", required: false)
  15.     @{ Html.RenderPartial("ScriptDebugControl"); }
  16. </head>
  17. <body>
  18.     <div id="ig-root" class="ig-root">
  19.         <!--Start header -->
  20.         @{Html.RenderPartial("FormHeader");}
  21.         <!--End Header -->
  22.         <!--Start Workflow -->
  23.         @RenderSection("WorkflowSection", required: false)
  24.         <!--End Workflow -->
  25.         <!--Start Footer -->
  26.         @{Html.RenderPartial("FormFooter");}
  27.         <!--End Footer -->
  28.     </div>
  29.     @{  Html.ScriptKendoLast("Notify", 
  30.              "IG.notifyServerMessage(IG.WorkflowContainer.notification);");
  31.         Html.ScriptRenderKendo();
  32.         Html.ScriptRenderFormHelp();
  33.     }
  34. </body>
  35. </html>
Custom OneColumnRootMaster
  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4.     <title>@Html.Raw(ViewBag.Title)</title>
  5.     <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
  6.     <meta http-equiv="X-UA-Compatible" content="IE=Edge" />
  7.     @Styles.Render("~/Content/Css/ig/ig-onecolumn.css")
  8.     @RenderSection("RowActionTemplateSection", required: false)
  9.     @{
  10.         Html.RenderPartial("ScriptRootControl");
  11.         Html.RenderPartial("IGNotificationTemplate");
  12.         Html.RenderPartial("IGConfirmTemplate");
  13.     }
  14.     @RenderSection("ScriptSection", required: false)
  15.     @{ Html.RenderPartial("ScriptDebugControl"); }
  16. </head>
  17. <body>
  18.     <div id="ig-root" class="ig-root">
  19.         <!--Start header -->
  20.         @{Html.RenderPartial("FormHeader");}
  21.         <!--End Header -->
  22.         <!--Start Workflow -->
  23.         @RenderSection("WorkflowSection", required: false)
  24.         <!--End Workflow -->
  25.  
  26.  
  27.  
  28.     </div>
  29.     @{  Html.ScriptKendoLast("Notify", 
  30.              "IG.notifyServerMessage(IG.WorkflowContainer.notification);");
  31.         Html.ScriptRenderKendo();
  32.         Html.ScriptRenderFormHelp();
  33.     }
  34. </body>
  35. </html>




Following tables are showing the difference between the default SiteMaster and the custom OneColumnSiteMaster. Both files are located in the views directory "Views\Shared".
Default SiteMaster
  1. @{
  2.     Layout = "~/Views/Shared/_RootMaster.cshtml";
  3. }
  4. @section ScriptSection{
  5.     @RenderSection("ScriptSection", required: false)
  6. }
  7. @section RowActionTemplateSection{
  8.     @RenderSection("RowActionTemplateSection", required: false)
  9. }
  10. @section WorkflowSection{
  11.     <div id="ig-root-container" class="ig-root-container">
  12.         <div id="ig-root-container-column-menu" class="ig-root-container-column-menu">
  13.             @RenderSection("WorkflowMenuSection", required: false)
  14.         </div>
  15.         <div id="ig-root-container-column-content" class="ig-root-container-column-content">
  16.             <div id="ig-workflow-header-wrapper">
  17.                 <div id="ig-history-container" class="ig-history-container">
  18.                     @{  Html.RenderPartial("HistoryTemplate"); }
  19.                 </div>
  20.                 <div id="ig-workflow-header">
  21.                     @RenderSection("WorkflowHeaderSection", required: false)
  22.                 </div>
  23.                 <div id="ig-notify-container" class="ig-notify-container">
  24.                 </div>
  25.                 <div id="ig-workflow-filter-wrapper">
  26.                     @RenderSection("WorkflowFilterSection", required: false)
  27.                 </div>
  28.             </div>
  29.             <div id="ig-workflow-main">
  30.                 <div id="ig-workflow-loadsymbol" class="k-loading k-loading-image">
  31.                 </div>
  32.                 <div id="ig-workflow-container">
  33.                     @RenderSection("WorkflowMainSection", required: false)
  34.                 </div>
  35.             </div>
  36.             @{ Html.RenderPartial("DebugModeTemplate"); }
  37.         </div>
  38.     </div>
  39. }
Custom OneColumnSiteMaster
  1. @{
  2.     Layout = "~/Views/Shared/_OneColumnRootMaster.cshtml";
  3. }
  4. @section ScriptSection{
  5.     @RenderSection("ScriptSection", required: false)
  6. }
  7. @section RowActionTemplateSection{
  8.     @RenderSection("RowActionTemplateSection", required: false)
  9. }
  10. @section WorkflowSection{
  11.     <div id="ig-root-container" class="ig-root-container">
  12.  
  13.  
  14.  
  15.         <div id="ig-root-container-column-content" class="ig-root-container-column-content">
  16.             <div id="ig-workflow-header-wrapper">
  17.                 <div id="ig-notify-container" class="ig-notify-container">
  18.  
  19.                 </div>
  20.  
  21.  
  22.  
  23.  
  24.  
  25.  
  26.  
  27.  
  28.             </div>
  29.             <div id="ig-workflow-main">
  30.                 <div id="ig-workflow-loadsymbol" class="k-loading k-loading-image">
  31.                 </div>
  32.                 <div id="ig-workflow-container">
  33.                     @RenderSection("WorkflowMainSection", required: false)
  34.                 </div>
  35.             </div>
  36.             @{ Html.RenderPartial("DebugModeTemplate"); }
  37.         </div>
  38.     </div>
  39. }



Following tables are showing the difference between the default Form View and the custom OneColumnForm View. Both files are located in the views directory "Views\Shared".
Default Form View
  1. @using Intergraph.Emea.Workflows
  2. @model WorkflowViewModel<Form>
  3. @{
  4.     ViewBag.Title = Model.Node.Label;
  5.     ViewBag.ScriptBag = new Dictionary<string, string>();
  6.     ViewBag.FormHelpBag = new List<FormHelp>();
  7.     Layout = "~/Views/Shared/_SiteMaster.cshtml";
  8. }
  9. @section ScriptSection{
  10.     @{ Html.RenderPartial("ScriptFormControl"); }
  11.     @Html.IncludeCustomScripts(Url, Model.SubModel.CustomScripts)
  12.     @Html.IncludeCustomStyles(Url, Model.SubModel.CustomStyles)
  13.     <script type="text/javascript" id="ClientViewModel">
  14.         @Html.Raw(
  15.               string.Concat("IG.WorkflowContainer =", 
  16.               Html.ToJson(Model), 
  17.               "; IG.formSettings = IG.WorkflowContainer.submodel;"))
  18.     </script>
  19. }
  20. @section WorkflowMainSection{
  21.     @using (Html.BeginForm("Form", "Save"))
  22.     {
  23.         @Html.EditorFor(m => Model.SubModel, "FormTemplate")
  24.     }
  25. }
  26. @section WorkflowMenuSection{
  27.     <div class="ig-column-menu-header">
  28.     </div>
  29.     @Html.DisplayFor(m => Model.WorkflowNavigation, "WorkflowNavigationTemplate")
  30. }
Custom OneColumnForm View
  1. @using Intergraph.Emea.Workflows
  2. @model WorkflowViewModel<Form>
  3. @{
  4.     ViewBag.Title = Model.Node.Label;
  5.     ViewBag.ScriptBag = new Dictionary<string, string>();
  6.     ViewBag.FormHelpBag = new List<FormHelp>();
  7.     Layout = "~/Views/Shared/_OneColumnSiteMaster.cshtml";
  8. }
  9. @section ScriptSection{
  10.     @{ Html.RenderPartial("ScriptFormControl"); }
  11.     @Html.IncludeCustomScripts(Url, Model.SubModel.CustomScripts)
  12.     @Html.IncludeCustomStyles(Url, Model.SubModel.CustomStyles)
  13.     <script type="text/javascript" id="ClientViewModel">
  14.         @Html.Raw(
  15.               string.Concat("IG.WorkflowContainer =", 
  16.               Html.ToJson(Model), 
  17.               "; IG.formSettings = IG.WorkflowContainer.submodel;"))
  18.     </script>
  19. }
  20. @section WorkflowMainSection{
  21.     @using (Html.BeginForm("Form", "Save"))
  22.     {
  23.         @Html.EditorFor(m => Model.SubModel, "FormTemplate")
  24.     }
  25. }


After creating the new OneColumnForm View and Master Views we have to change the Workflow Settings of our Workflow exmaple SearchAddress to load our custom Form View instead of the default.

Workflow Settings - SearchAddress
  1. <WorkflowRoot>
  2.   <WorkflowNode id="0">
  3.     <WorkflowNode id="1"
  4.                   label="Searching for a address"
  5.                   controller="Form"
  6.                   form="SearchingAddress" 
  7.                   emptyform="true"  />
  8.   </WorkflowNode>
  9. </WorkflowRoot>



Workflow Settings - SearchAddress with OneColumnForm View
  1. <WorkflowRoot>
  2.   <WorkflowNode id="0">
  3.     <WorkflowNode id="1"
  4.                   label="Searching for a address"
  5.                   controller="Form"
  6.                   form="SearchingAddress" 
  7.                   emptyform="true"
  8.                   view="OneColumnForm" />
  9.   </WorkflowNode>
  10. </WorkflowRoot>



Now we obtain following GUI rendered:

Custom Form View - OneColumn

Custom Form View - OneColumn


Custom Template

In some cases it is required to use Custom Templates with special layout or behaviour. This section describes how to use Custom Templates, therefore we created following use-case scenario for example:

  1. Custom ListTemplate: change the default KendoUI Grid Columns Configuration
  2. Custom HyperlinkTemplate: open URL on same browser frame


A Custom Template has to be located in the EditorTemplates Direcotry - "Views\Shared\EditorTemplates". If you want to assign a Custom Template to a FormField in your Form, you have to change the template attribute of your FormField in your FormSettings.


As in section Widget Templates described, a Template is strongly typed on Workflow Server and on Workflow Client Side, if you want to use the Workflow Client Model functionality (e.g. management of editable / visible state of a FormField) on your own Custom Widget Template, you have to make sure that all data-bindings on your Custom Templates are valid.


Custom ListTemplate

The default template of the grid in the default List View is Rendering a grid with the default KendoUI - Grid Column Template Configuration. We want to create own columns configuration and use for the grid in the default List View.
Because this use case is very often asked, we simplefied the way to change the columns configuration, therefore we created a JavaScript Object named IG.customListColumnTemplate, which has to be modified in a custom script (see JavaScript CookBook: How can I add custom Javascripts to my workflow?)

The next screenshot of a simple Workflow List Example shows a grid with three columns:

  1. FIRSTNAME
  2. LASTNAME
  3. BIRTHDAY


List with default Header

We want to change the KendoUI - Grid Column Template Configuration to obtain following column structure:

  1. FIRSTNAME + (line break) + LASTNAME (with an Icon as column header)
  2. BIRTHDAY (new title) + (bold content)


List with custom Header

Custom JavaScript CustomListHeader.js (see JavaScript CookBook: How can I add custom Javascripts to my workflow?)
  1. IG.customListColumnTemplate = [ 
  2.                 {
  3.                     title: 'FULLNAME',
  4.                     field: 'LASTNAME',
  5.                     template: "${ FIRSTNAME } <br/> ${ LASTNAME }",
  6.                     width: '300px',
  7.                     headerTemplate: "<img src='http://smartclient.intergraph.at/documentation/images/d/d1/Red_text_icon.PNG'>"
  8.  
  9.                 },
  10.                 {
  11.                     title: 'Day of birth',
  12.                     field: 'BIRTHDAY',
  13.                     template: function (dataItem) {
  14.                         return "<strong>" + kendo.toString(new Date(dataItem.BIRTHDAY), IG.util.getDateFormat('d')) + "</strong>";
  15.                     }
  16.                 }];




Custom HyperlinkTemplate

The default HyperlinkTemplate opens the defined URL on a new blank browser window, we want to create an own custom HyperlinkTemplate which opens the given URL on same browser frame.

Following tables are showing the difference between the default HyperlinkTemplate and the custom CustomHyperlinkTemplate. Both files are located in the views directory "Views\Shared\EditorTemplates".


The whole HyperlinkTemplate.cshtml. Only the code in-between the segment "Html.BeginFormFieldWidgetWrapper()" should be customized, if you want to keep the known workflow form structure.


Blue.png CsHtml-Code is based on GMSC 2014



Default Template - HyperlinkTemplate
  1. @using Intergraph.Emea.Workflows
  2. @model FormField
  3.  
  4. @{
  5.     HtmlFormFieldClassModel classModel = HtmlHelperExtensions.MaxLengthToClassModel(Model.MaxLength);
  6.  
  7.     FormHelp formHelp = Html.GetFormHelp(Model.Name, Model.Name, Model.Label, Model.Help);
  8.     if (formHelp != null)
  9.     {
  10.         classModel.InputHelpClass = FormHelp.GetInputClass(classModel.InputHelpClass);
  11.     }
  12. }
  13.  
  14. @using (Html.BeginFormFieldWrapper(Model.Name, Model.DisplayType))
  15. {
  16.     Html.RenderPartial("FormFieldLabelTemplate",Model);
  17.  
  18.     using(Html.BeginFormFieldInputWrapper(Model.Name, classModel.InputDivClass))
  19.     {
  20.         using (Html.BeginFormFieldWidgetWrapper())
  21.         {
  22.             <div style="float: inherit">
  23.                 <a id="@Model.Name" 
  24.                    class="@classModel.InputHelpClass" 
  25.                    data-bind="attr:{ 
  26.                         @HtmlHelperExtensions.DataBindModel(Model.Name, "href", "hyperlink")}, 
  27.                         @HtmlHelperExtensions.DataBindModel(Model.Name, "text", "value")" 
  28.                    target="_blank"  ></a>
  29.             </div>
  30.         }
  31.  
  32.         if (formHelp != null)
  33.         { 
  34.             @Html.EditorFor(m => formHelp, formHelp.Template) 
  35.         }
  36.     }
  37.  
  38.     using (Html.BeginFormFieldActionWrapper("field"))
  39.     {
  40.         @Html.EditorForFormActions(Model.Actions)
  41.     }
  42. }
Code-snippet of HyperlinkTemplate.cshtml
  1.         using (Html.BeginFormFieldWidgetWrapper())
  2.         {
  3.             <div style="float: inherit">
  4.                 <a id="@Model.Name" 
  5.                    class="@classModel.InputHelpClass" 
  6.                    data-bind="attr:{ 
  7.                         @HtmlHelperExtensions.DataBindModel(Model.Name, "href", "hyperlink")}, 
  8.                         @HtmlHelperExtensions.DataBindModel(Model.Name, "text", "value")" 
  9.                    target="_blank"  ></a>
  10.             </div>
  11.         }
Code-snippet of custom template - CustomHyperlinkTemplate.cshtml
  1.         using (Html.BeginFormFieldWidgetWrapper())
  2.         {
  3.             <div style="float: inherit">
  4.                 <a id="@Model.Name" 
  5.                    class="@classModel.InputHelpClass" 
  6.                    data-bind="attr:{ 
  7.                         @HtmlHelperExtensions.DataBindModel(Model.Name, "href", "hyperlink")}, 
  8.                         @HtmlHelperExtensions.DataBindModel(Model.Name, "text", "value")" 
  9.                    target="_self"  ></a>
  10.             </div>
  11.         }



Language: English