DEV Community

Cover image for Reports in DotVVM with GridView, CheckBox, and TextBox controls
Daniel Gomez for DotVVM

Posted on

Reports in DotVVM with GridView, CheckBox, and TextBox controls

A table is a set of organized cells within which we can host different contents. This type of element is quite useful when making a report to display some specific information.

In previous articles on ASP.NET Core and DotVVM, we could generally see how to use predefined controls of these tools for data visualization as a report. Here are some of those articles:

Also, when we manage forms to manage information, we need to create tables before we can view the processed data. Here are some previous articles in case you want to learn how to design HTML forms:

This time, we will learn the basics of visualizing certain data and establishing some search criteria with C# and HTML, through DotVVM's controls in ASP.NET Core.

Note: The project source code that we'll discuss in this article can be found in this GitHub repository: DotVVM Reports.

Design Pattern: Model, View, ViewModel - MVVM

An important aspect to mention is on the basis of DotVVM. This framework is based on the Model, View, View model over .NET design pattern for communication between HTML (web pages) and C- (source code). The purpose of these parts are as follows:

  • The model. — is responsible for all application data and related business logic.

  • The view. — Representations for the end user of the application model. The view is responsible for displaying the data to the user and allowing manipulation of the application data.

  • Model-View or View-Model. — one or more per view; the model-view is responsible for implementing view behavior to respond to user actions and for easily exposing model data.

Report with ASP.NET Core and DotVVM

To exemplify the use of some DotVVM controls for reporting, we have a small application like this:

Considering the design pattern MVVM – Model, View, ViewModel, we will analyze in general each of these parts for this project.

Model

Considering that the application data and related business logic is handled in this section, then we'll see how the corresponding data and services are handled.

The database consists of two tables: Person and PersonType.

The SQL statements for creating these tables, their attributes, and inserting some records are as follows:

CREATE SCHEMA IF NOT EXISTS `db` DEFAULT CHARACTER SET utf8;
USE `db` ;

CREATE TABLE IF NOT EXISTS `db`.`PersonType` (
  `Id` INT NOT NULL,
  `Name` VARCHAR(45) NOT NULL,
  `Description` VARCHAR(45) NOT NULL,
  PRIMARY KEY (`Id`))
;

CREATE TABLE IF NOT EXISTS `db`.`Person` (
  `Id` INT NOT NULL AUTO_INCREMENT,
  `FirstName` VARCHAR(45) NOT NULL,
  `LastName` VARCHAR(45) NOT NULL,
  `IdPersonType` INT NOT NULL,
  PRIMARY KEY (`Id`),
  FOREIGN KEY (`IdPersonType`) REFERENCES `db`.`PersonType` (`Id`))
;

INSERT INTO `persontype` (`Id`, `Name`, `Description`) VALUES ('1', 'Type A', '');
INSERT INTO `persontype` (`Id`, `Name`, `Description`) VALUES ('2', 'Type B', '');

INSERT INTO `person` (`Id`, `FirstName`, `LastName`, `IdPersonType`) VALUES ('1', 'Sergey', 'Brin', '1');
INSERT INTO `person` (`Id`, `FirstName`, `LastName`, `IdPersonType`) VALUES ('2', 'Larry', 'Page', '1');
INSERT INTO `person` (`Id`, `FirstName`, `LastName`, `IdPersonType`) VALUES ('3', 'Tim', 'Barners', '2');
INSERT INTO `person` (`Id`, `FirstName`, `LastName`, `IdPersonType`) VALUES ('4', 'Linus', 'Torvalds', '1');
INSERT INTO `person` (`Id`, `FirstName`, `LastName`, `IdPersonType`) VALUES ('5', 'Larry', 'Ellison', '1');
INSERT INTO `person` (`Id`, `FirstName`, `LastName`, `IdPersonType`) VALUES ('6', 'Steve', 'Ballmer', '2');
INSERT INTO `person` (`Id`, `FirstName`, `LastName`, `IdPersonType`) VALUES ('7', 'Steve', 'Jobs', '2');
INSERT INTO `person` (`Id`, `FirstName`, `LastName`, `IdPersonType`) VALUES ('8', 'Marc', 'Benioff', '1');
INSERT INTO `person` (`Id`, `FirstName`, `LastName`, `IdPersonType`) VALUES ('9', 'Ray', 'Ozzie', '2');
INSERT INTO `person` (`Id`, `FirstName`, `LastName`, `IdPersonType`) VALUES ('10', 'Nicholas', 'Negroponte', '2');
INSERT INTO `person` (`Id`, `FirstName`, `LastName`, `IdPersonType`) VALUES ('11', 'Diane', 'Green', '1');
INSERT INTO `person` (`Id`, `FirstName`, `LastName`, `IdPersonType`) VALUES ('12', 'Sam', 'Palmisano', '1');
INSERT INTO `person` (`Id`, `FirstName`, `LastName`, `IdPersonType`) VALUES ('13', 'Blake', 'Ross', '2');
INSERT INTO `person` (`Id`, `FirstName`, `LastName`, `IdPersonType`) VALUES ('14', 'Ralph', 'Szygenda', '2');
INSERT INTO `person` (`Id`, `FirstName`, `LastName`, `IdPersonType`) VALUES ('15', 'Rick', 'Dalzell', '2');
Enter fullscreen mode Exit fullscreen mode

With the database set, the data access layer part refers to the definition of classes to work with the database features and the context to establish communication between ASP.NET Core and the database, which in this case MySQL is the one being used.

For this purpose, we need to install three NuGet packages:

  • Microsoft.EntityFrameworkCore.Design
  • Microsoft.EntityFrameworkCore.Tools
  • MySql.Data.EntityFrameworkCore

Now, we need to use the package management console to scaffold from the database (automatically generate the context and feature classes) using the following command:

Scaffold-DbContext "server=servername;port=portnumber;user=username;password=pass;database=databasename" MySql.Data.EntityFrameworkCore -OutputDir Entities -f
Enter fullscreen mode Exit fullscreen mode

With this first part, the connection to the database is ready. What follows is the definition of models with which the website will be worked. These models are:

public class PersonModel
{
    public int Id { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public int IdPersonType { get; set; }
    public string NamePersonType { get; set; }
}
Enter fullscreen mode Exit fullscreen mode
public class PersonTypeModel
{
    public int Id { get; set; }
    public string Name { get; set; }
    public string Description { get; set; }
}
Enter fullscreen mode Exit fullscreen mode

For each of these models there is a service, which has the following operations:

PersonService:

  • GetAllPersonsAsync()
  • GetPersonByIdAsync(int personId)
  • GetPersonByIdAndTypeAsync(int personId, int personTypeId)
  • GetAllPersonsByTypeAsync(int personTypeId)

PersonTypeService:

  • GetAllPersonTypesAsync()
  • GetPersonTypeByIdAsync(int personTypeId)

In Visual Studio 2019 we'll have something like this:

Vistamodel

public class DefaultViewModel : MasterPageViewModel
{
    private readonly PersonService personService;
    public GridViewDataSet<PersonModel> Persons { get; set; } = new GridViewDataSet<PersonModel>();
    public List<int> PersonTypes { get; set; } = new List<int>();
    public string IdSearch { get; set; }
    public bool SearchByTextVisible { get; set; } = false;

    public DefaultViewModel(PersonService personService)
    {
        this.personService = personService;
    }

    public override async Task PreRender() {}

    public async Task UpdatePersonList()
    {
        IdSearch = null;

        if (PersonTypes.Count == 2)
        {
            Persons.Items = await personService.GetAllPersonsAsync();
            SearchByTextVisible = true;
        }
        else if (PersonTypes.Count == 1)
        {
            int IdPersonType = PersonTypes.FirstOrDefault();
            Persons.Items = await personService.GetAllPersonsByTypeAsync(IdPersonType);
            SearchByTextVisible = true;
        }
        else
        {
            Persons.Items.Clear();
            SearchByTextVisible = false;
        }
    }

    public async Task SearchById()
    {
        if (PersonTypes.Count == 2)
        {
            if (!string.IsNullOrEmpty(IdSearch))
            {
                List<PersonModel> list = new List<PersonModel>(); ;
                list.Add(await personService.GetPersonByIdAsync(Int32.Parse(IdSearch)));
                Persons.Items = list;
            }
            else {
                Persons.Items = await personService.GetAllPersonsAsync();
            }
        }
        else if (PersonTypes.Count == 1)
        {
            if (!string.IsNullOrEmpty(IdSearch))
            {
                int IdPersonType = PersonTypes.FirstOrDefault();
                List<PersonModel> list = new List<PersonModel>(); ;
                list.Add(await personService.GetPersonByIdAndTypeAsync(Int32.Parse(IdSearch), IdPersonType));
                Persons.Items = list;
            }
            else
            {
                int IdPersonType = PersonTypes.FirstOrDefault();
                Persons.Items = await personService.GetAllPersonsByTypeAsync(IdPersonType);
            }
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

View

<dot:Content ContentPlaceHolderID="MainContent">
    <div class="page-center">
        <div class="page-grid-top">
            <div class="student-image"></div>
            <h1>Person Report</h1>
        </div>

        <p>
            <h4>Search by type:</h4>
            <p />
            <dot:CheckBox CheckedItems="{value: PersonTypes}"
                      Changed="{command: UpdatePersonList()}"
                      CheckedValue="{value: 1}" Text="Type A" />
            <br />
            <dot:CheckBox CheckedItems="{value: PersonTypes}"
                      Changed="{command: UpdatePersonList()}"
                      CheckedValue="{value: 2}" Text="Type B" />
        </p>

        <p >
            <h4>Search by text:</h4>
            <p />
            ID Number:
            <dot:TextBox Text="{value: IdSearch}" Type="Number" class="page-input" Visible="{value: SearchByTextVisible}" />
            <dot:Button Text="Search" Click="{command: SearchById()}" class="page-button" Visible="{value: SearchByTextVisible}" />
        <p />

        <h4>Report:</h4>

        <dot:GridView DataSource="{value: Persons}" class="page-grid">
            <Columns>
                <dot:GridViewTextColumn ValueBinding="{value: Id}" HeaderText="Id" />
                <dot:GridViewTextColumn ValueBinding="{value: FirstName}" HeaderText="Firstname" />
                <dot:GridViewTextColumn ValueBinding="{value: LastName}" HeaderText="LastName" />
                <dot:GridViewTextColumn ValueBinding="{value: NamePersonType}" HeaderText="Type" />
            </Columns>
            <EmptyDataTemplate>
                There are no search results.
            </EmptyDataTemplate>
        </dot:GridView>

    </div>
</dot:Content>
Enter fullscreen mode Exit fullscreen mode

Web application analysis

In the following GIF we can see in a general way the interaction with this small web page.

The first element we will analyze is the GridView, a DotVVM control that allows us tables to represent specific data. This component allows us to specify the data source through the DataSource property, in this case, the data source is defined as follows:

public GridViewDataSet<PersonModel> Persons { get; set; } = new GridViewDataSet<PersonModel>();
Enter fullscreen mode Exit fullscreen mode

The data source, in addition to a list of type GridViewDataSet, can also be defined through another collection of type List. The GridViewTextColumn tag is used for column definition. In this case, we can find the columns Id, FirstName, LastName and Type. These names come from the data type of the data source, in this case, from the PersonModel model.

<dot:GridView DataSource="{value: Persons}" class="page-grid">
    <Columns>
        <dot:GridViewTextColumn ValueBinding="{value: Id}" HeaderText="Id" />
        <dot:GridViewTextColumn ValueBinding="{value: FirstName}" HeaderText="Firstname" />
        <dot:GridViewTextColumn ValueBinding="{value: LastName}" HeaderText="LastName" />
        <dot:GridViewTextColumn ValueBinding="{value: NamePersonType}" HeaderText="Type" />
    </Columns>
    <EmptyDataTemplate>
        There are no search results.
    </EmptyDataTemplate>
</dot:GridView>
Enter fullscreen mode Exit fullscreen mode

Another gridview sub-tags is EmptyDataTemplate. This tag allows us to display some HTML content in case the list of elements is empty. In the end, with the GridView we will visualize something like this:

Learn more about the GridView control here:https://www.dotvvm.com/docs/controls/builtin/GridView/2.0.

However, from this table there are several operations that can be performed, for example, to set additional components to create search criteria and update this table based on the search.

The first case is using a DotVVM CheckBox. As in HTML or any other design environment, the CheckBox has the role as a check box for selecting items in an option set. For this example, the goal is to have two check boxes, which correspond to the types of people. Depending on the selection, either Type A, Type B, or both, the table of records will be updated in accordance with this decision.

In the view part, we find the CheckedItems property that stores the value of the selected items. We also find the Changed property, which allows us to specify the method that will perform the actions when this element is activated or disabled.

<dot:CheckBox CheckedItems="{value: PersonTypes}"
            Changed="{command: UpdatePersonList()}"
            CheckedValue="{value: 1}" Text="Type A" />
<br />
<dot:CheckBox CheckedItems="{value: PersonTypes}"
            Changed="{command: UpdatePersonList()}"
            CheckedValue="{value: 2}" Text="Type B" />
Enter fullscreen mode Exit fullscreen mode

In the update method, for example, if we select one of the two types, then we will query the database according to the defined service: PersonService, to get the list of people according to the selected id. With this list retrieved, we will update the database by re-setting the items in the GridView data source.

int IdPersonType = PersonTypes.FirstOrDefault();
Persons.Items = await personService.GetAllPersonsByTypeAsync(IdPersonType);
SearchByTextVisible = true;
Enter fullscreen mode Exit fullscreen mode

Something similar will happen if we don't select any of the checkboxes. In this case we will only clean the items of the GridView.

Persons.Items.Clear();
Enter fullscreen mode Exit fullscreen mode

As we saw in the GIF, the result of using the CheckBox control is as follows:

Learn more about the CheckBox control here:https://www.dotvvm.com/docs/controls/builtin/CheckBox/2.0.

Another of the controls that allow us to continue adding functionality to the GridView to set search criteria to this report are the TextBox and 'Button' elements. In this case, these components can be used to search for something specific to the report through a text entry. To exemplify, in this application, controls are used to find a specific person according to their Id.

<dot:TextBox Text="{value: IdSearch}" Type="Number" class="page-input" Visible="{value: SearchByTextVisible}" />

<dot:Button Text="Search" Click="{command: SearchById()}" class="page-button" Visible="{value: SearchByTextVisible}" />
Enter fullscreen mode Exit fullscreen mode

Updating the elements of the GridView is similar to the CheckBox case. The result is as follows:

Alt Text

All the information about the TextBox control can be found in: https://www.dotvvm.com/docs/controls/builtin/TextBox/2.0. And the button control in: https://www.dotvvm.com/docs/controls/builtin/Button/2.0.

What's next?

With this article, we learned certain features of the GridView, CheckBox, TextBox, and Button components to display a list of data and set search criteria through the Model, View, ViewModel design pattern in ASP.NET Core and DotVVM.

The source code for this implementation is available in this repository: DotVVM Reports.

Additional resources

Want to continue to acquire new knowledge about ASP.NET Core and DotVVM?, these resources might be of interest to you:

Thank you!:

If you have any concerns or need help in something particular, it will be a pleasure to be able to help.

See you in Twitter!! :)

Top comments (0)