View Model

How to add a new Model

  1. Right-click on the respective Model folder under the "Business" directory

  2. Choose "Add" > "Class..."

  3. Select "Class", type in the name of the Model file you wish to add, and click "Add"

  4. Make the class public and make it inherit from the class "ModelBase"

The Controller Signature should look like this:

public class ModelName : ModelBase

In the following, the implementation of a View Model will be described by using a specific example.

View Model Example

A View Model is a Model that implements the data logic of a specific View, thus it heavily depends on the View it corresponds to. Thus, to better describe the process of implementing a View Model, a specific View will be used as example.

The logic of the following View should be implemented:

It consists of a List Component on the left and a Detail Component on the right. Clicking a record in the List Component displays the details of the record in the Detail Component.

The Detail Component itself consists of a Header Component at the top and a List of Items at the bottom.

Model Structure

A Model can consist of "Sub Models", which are classes that are defined in the same file as the "Main Model".

How to implement a View Model

A Model Class usually consists of Properties, Constructor(s) and Methods. Optionally, additional classes can be defined in the same Model file but they are located outside of the Model class (Such additional classes will be referred to as "Sub Models" whereas the actual View Model class will be referred to as "Main Model"). The mentioned components are located as illustrated below.:

namespace Advantech.Ctos.Business.Example
{
   public class MainModelName : ModelBase
   {
      //PROPERTIES
      
      //CONSTRUCTOR(S)
      
      //METHODS
   
   }
   
   //SUB MODELS
   
}

For our example, the Main Model will be called "Example Model".

In the following, each section within the Model class will be described in detail using the example above:

Properties

The Properties of the Model and their relation to each other (without taking the methods of the Model into consideration) will be referred to as "Model Structure" in this context. The Model Structure needs to match the View hierarchy, i.e. all the Components that are found in the View, such as Tables, Forms, etc. For each of those Elements, there should be a corresponding Property in the top section of the Model. Each of them requires a Getter and a Setter function and usually all of them have public access.

In the example above, the View Hierarchy contains a List Component and a Detail Component where the content of the Detail Component corresponds to a selected record in the List Component, i.e. the data type of the Detail Component is the same as the data type of the items in the List Component. Since that data type is a complex one (containing Properties of its own), it is a Sub Model that additionally needs to be implemented in the Model file. This Sub Model will be called "OrderDetail" in our example and the Components will be called "OrderList" and "OrderDetail" respectively, so the Properties of our example are implemented like this:

namespace Advantech.Ctos.Business.Example
{
    public class ExampleModel : ModelBase
    {
        //List Component
        public List<OrderDetail> OrderList { get; set; }
        
        //Detail Component
        public OrderDetail OrderDetail { get; set; }
        
    }
    
    //SUB MODELS
        

    
}

Now, the Sub Model needs to be implemented. Since it corresponds to the Detail Section, the Properties of "OrderDetail" correspond to the Header Component and the List Component within the Detail Component. The data types of the Header as well as the data type of the Items in the List are again complex ones, so a Sub Model needs to be implemented for them, which will be called "OrderHeader" and "OrderItem". Overall, the Sub Model "Order Detail" is implemented like this:

    public class OrderDetail
    {
        public OrderHeader Header { get; set; }
        
	public List <OrderItem> Items { get; set;}
        
        ...
        
    }

For the implementation of the "OrderHeader" Sub Model, a Property is created for each Control Input that the Header contains, which are all basic data types, thus no Sub Model needs to be additionally implemented for any of them:

    public class OrderHeader
    {
        public string WorkOrder { get; set; }

        public string PlantLocation { get; set; }

        public string SalesOrder { get; set; }

        public string DeliveryNumber{ get; set; }

        public string Product { get; set; }

        public string CustomerCode { get; set; }

    }

Similarly, for the implementation of the "OrderItem" Sub Model, a Property for each column is created, which are all basic data types as well:

    public class OrderItem
    {
        public string Name { get; set; }
    
        public int Position { get; set; }
    
        public int Quantity { get; set; }
    
        public string Category { get; set; }
        
    }

Taking all the steps so far, the View Model file of "ExampleModel" currenlty looks like this:

namespace Advantech.Ctos.Business.Example
{
    public class ExampleModel : ModelBase
    {
        //List Component
        public List<OrderDetail> OrderList { get; set; }
        
        //Detail Component
        public OrderDetail OrderDetail { get; set; }
        
    }
    
    //SUB MODELS
        
    public class OrderDetail
    {
        public OrderHeader Header { get; set; }
        
	public List <OrderItem> Items { get; set; }
        
    }
    
    public class OrderHeader
    {
        public string WorkOrder { get; set; }

        public string PlantLocation { get; set; }

        public string SalesOrder { get; set; }

        public string DeliveryNumber{ get; set; }

        public string Product { get; set; }

        public string CustomerCode { get; set; }

    }
    
    public class OrderItem
    {
        public string Name { get; set; }
    
        public int Position { get; set; }
    
        public int Quantity { get; set; }
    
        public string Category { get; set; }
        
    }
    
}

Constructor

Every class needs to have a Constructor to be able to create an instance of that class. A Constructor is a public method whose name is the same as the Class' and that contains the initialization of all Properties that are an Object (Sub Model) or a List. It can also already assign (default) values to Properties. If a Model file contains Sub Models, each needs to contain a Constructor as well. The Constructor is usually located below the Properties.

For the above example, the Constructor of the Main Model "ExampleModel" could look like this:

public ExampleModel() 
{
    OrderDetail = new OrderDetail();
    OrderList = new List<OrderDetail>();
}

Similarly, the Constructor for "OrderDetail" would look like this:

public OrderDetail()
{
    Header = new OrderHeader();
    Items= new List<OrderItem>();
}

Since "OrderHeader" and "OrderItem" neither contain Lists nor Objects (Sub Models) as Properties, a Constructor is not necessary.

Constructors can also have parameters to enable an assignment of values inputted by the user. One possibility to do that is to do it directly in the first Constructor and assigning the value to the corresponding Property. For example, if we wanted to assign a value to the Property "Name" of "OrderItem", a Constructor for "OrderItem" would look like this:

public OrderItem(string name)
{
    Name = name;
}

We can also use methods from the Base Model in the Constructor, for example the one that returns the current user's name ("ActiveUser") instead of passing a parameter:

public OrderItem()
{
    Name = ActiveUser;
}

To populate a List, one can make use of loops. Especially, when it is a List of Objects (Sub Model) as it is the case in our example. In that case, the values are usually derived from an existing Data Repository. So to populate the Property "OrderList", the Constructor of "ExampleModel" could also look like this:


public ExampleModel(string id) 
{
    OrderDetail = new OrderDetail();
    
    
    //Populating OrderList
    
    var orderList = UnitWork.ProductionOrders.Get(x => x.IsPlan && x.SapOrder.Delivery != null && x.ProductID != null, null, "", 100);

    foreach(var item in orderList)
    {
        OrderList.Add(new ExampleDetail()
        {
            Header.WorkOrder = item.ID,
            Header.PlantLocation = item.Location
            Header.SalesOrder = item.SapOrder.SalesOrder,
            Header.DeliveryNumber = item.SapOrder.Delivery,
            Header.Product = item.ProductID,
            Header.CustomerCode = item.SapOrder.CustId

        });
    }
    
}

It is also possible to create a second Constructor that inherits (i.e. overrides) the first one like this:

public ExampleModel() 
{
    OrderDetail = new OrderDetail();
    OrderList = new List<OrderDetail>();
}

public ExampleMaster(string id) : this()
{
    //Populating OrderList
    
    var orderList = UnitWork.ProductionOrders.Get(x => x.IsPlan && x.SapOrder.Delivery != null && x.ProductID != null, null, "", 100);

    foreach(var item in orderList)
    {
        OrderList.Add(new ExampleDetail()
        {
            Header.WorkOrder = item.ID,
            Header.PlantLocation = item.Location
            Header.SalesOrder = item.SapOrder.SalesOrder,
            Header.DeliveryNumber = item.SapOrder.Delivery,
            Header.Product = item.ProductID,
            Header.CustomerCode = item.SapOrder.CustId

        });
    }

}

Taking the most recent version of each Constructor, the Model file so far would overall look like this:

namespace Advantech.Ctos.Business.Example
{
    public class ExampleModel : ModelBase
    {
        //List Component
        public List<OrderDetail> OrderList { get; set; }
        
        //Detail Component
        public OrderDetail OrderDetail { get; set; }
        
        
        public ExampleModel() 
        {
            OrderDetail = new OrderDetail();
            OrderList = new List<OrderDetail>();
        }

        public ExampleMaster(string id) : this()
        {
            //Populating OrderList
    
            var orderList = UnitWork.ProductionOrders.Get(x => x.IsPlan && x.SapOrder.Delivery != null && x.ProductID != null, null, "", 100);

            foreach(var item in orderList)
            {
                OrderList.Add(new ExampleDetail()
                {
                    Header.WorkOrder = item.ID,
                    Header.PlantLocation = item.Location
                    Header.SalesOrder = item.SapOrder.SalesOrder,
                    Header.DeliveryNumber = item.SapOrder.Delivery,
                    Header.Product = item.ProductID,
                    Header.CustomerCode = item.SapOrder.CustId

                });
            }

        }
        
    }
    

        
    public class OrderDetail
    {
        public OrderHeader Header { get; set; }
        
	public List <OrderItem> Items { get; set; }
	
	public OrderDetail()
        {
            Header = new OrderHeader();
            Items= new List<OrderItem>();
        }
        
    }
    
    public class OrderHeader
    {
        public string WorkOrder { get; set; }

        public string PlantLocation { get; set; }

        public string SalesOrder { get; set; }

        public string DeliveryNumber{ get; set; }

        public string Product { get; set; }

        public string CustomerCode { get; set; }

    }
    
    public class OrderItem
    {
        public string Name { get; set; }
    
        public int Position { get; set; }
    
        public int Quantity { get; set; }
    
        public string Category { get; set; }
        
        public OrderItem()
        {
            Name = ActiveUser;
        }
        
    }
    
}

Methods

The Methods of the Model will be referred to as "Model Functionality" in this context. The different functions of the Model are implemented below the Constructor(s).

The methods of the Data Repository as well as the Base Model can be used here as well, just like the methods "Get" and "ActiveUser" have also been used before in the Constructor.

In the following, some of the most typical types of methods will be described continuing the example from before:

Get

A common method that is needed is to get specific data from a Data Repository to be displayed. In our example, it would be needed to get the data that should be displayed in the Detail Component upon clicking a record in the List Component.

Such a method would return an instance of the object and needs an id as parameter to find the right one in the Data Repository. Consequently, it first creates a new instance of the object, checks if the inputted id is empty or not, if not, fetches the record from the data repository whose id matches the inputted one, then assigns the fetched values to the corresponding Properties and finally returns the object. Overall, it would be implemented like this:

public ExampleDetail GetOrderDetail(string id)
{
    var orderDetail = new OrderDetail();

    if (!string.IsNullOrWhiteSpace(id))
    {
        var order = UnitWork.ProductionOrders.GetByID(id);
        
        orderDetail.WorkOrder = order.ID;
        orderDetail.Delivery = order.SapOrder.Delivery;
        orderDetail.CustomerID = order.SapOrder.CustId;
        orderDetail.SalesOrder = order.SapOrder.SalesOrder;
        orderDetail.Product = order.ProductID;
        orderDetail.Location = order.Location;
        
        foreach(var item in order.Components)
        {
            orderDetail.Components.Add(new ExamplePart()
            {
                Name = item.Name,
                Position = item.Position,
                Quantity = item.QtyInSU,
                Category = item.Category
            });
        }
    }

    return orderDetail;
}

This method would be called in the corresponding HTTPGET Action of the Controller file. It could also be called in a Constructor.

Save

If the Detail Component is a Form that allows for editing the selected record, it would also require a Save or Update Function which is another common type of method.

Such a method would take the data of the submitted Form as parameter, uses the id Property of it to fetch the corresponding record from the data repository, checks if the fetched record is empty or not, if not, assigns the values from the Form ("data") to the corresponding Properties of the fetched record, calls the Update method on the data repository passing the now updated record as parameter, and finally returns the boolean value "true" to signalize that the Updating process has been successful. Overall, it would be implemented like this:

public bool SavePart(ExamplePart data)
{
    var order = UnitWork.ExampleRepository.GetByID(data.ID);

    if (order != null)
    {
        order.Location = data.Location;
        order.SapOrder.SalesOrder = data.SalesOrder;
        order.SapOrder.Delivery = data.Delivery;

        UnitWork.ProductionOrders.Update(order);
    }

    return true;
}


public bool SaveOrder(OrderDetail data)
{
    var order = UnitWork.ProductionOrders.GetByID(data.WorkOrder);

    if (order != null)
    {
        order.Location = data.Header.PlantLocation;
        order.SapOrder.SalesOrder = data.Header.SalesOrder;
        order.SapOrder.Delivery = data.Header.DeliveryNumber;
        order.ProductID = data.Header.Product;
        order.CustID= data.Header.CustomerCode;

        UnitWork.ProductionOrders.Update(order);
    }

    return true;
}

This method would be called in the corresponding HTTPPOST Action of the Controller file.

Delete

Another method that is commonly needed is one that deletes a record from the Data Repository. In our example, it could be that the List or the Form contains a Delete Icon or a Delete Button that would trigger a Delete process.

Such a method would take the id of the selected item as parameter, uses it to fetch the corresponding record from the Data Repository, check if the fetched record is empty or not, and if not, deletes the record using the Delete method on the Data Repository passing the id as parameter. Overall, it would be implemented like this:

public void DeleteOrder(string id)
{
    var data = UnitWork.ProductionOrders.GetByID(id);
    if (data == null)
    {
        ThrowError("No Order is deleted.");
    }
    UnitWork.ProductionOrders.DeleteByID(id);
}

This method would be called in the corresponding HTTPDELETE Action of the Controller file.

Last updated