WinForm Model View Controller Using VB.Net WebBrowser Control

Introduction

A lot of example of Model View Controller Pattern (MVC) are related to Web application but we can make it on Winform.

As a brief, Model View Controller is a way to separate between bussiness logic and presentation. Each layers are independent from others or loose coupling.
Model layer is a business logic. It can be database access or any business rules. View layer is a application layout design. Controller is a connector between model and view and act as main program at MVC.
Users see a controller in the application. MVC is useful for developing medium to large product with several programmers. Those programmers can work simultanously. They can work independently as much as possible.
I already have simple example of web MVC using php at Web Model View Controller (MVC) Concept with PHP example

In this example I use VB.Net Winform Application and using WebBrowser Control as a user interface. I have earlier post that explain on Using VB.Net WebBrowser Control as WinForm user interface.
I create 3 projects in the solution to represents Model View Controller them self.
The three projects are ClassLibDatabaseMVC.vbproj as Database/Model Layer, ClassLibPresentation.vbproj as View Layer and WinFormBrowserMVC as Controller Layer.

Implementation

I am using VB 2010 Express and SQL Server 2005 to do this sample. Using one solution and three projects in it. The database name is TestDB and table name is Contacts.
Sample Table Contacts has only three columns. They are ContactID (PK, int, identity), ContactName (nvarchar(255)) and RegDate (datetime).

First, Create Database And Table

CREATE TABLE [dbo].[Contacts](
	[ContactID] [int] IDENTITY(1,1) NOT NULL,
	[ContactName] [nvarchar](255) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL,
	[RegDate] [datetime] NOT NULL,
 CONSTRAINT [PK_Contacts] PRIMARY KEY CLUSTERED
(
	[ContactID] ASC
)WITH (PAD_INDEX  = OFF, IGNORE_DUP_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY]

Second, Create Main Form as Controller Layer (WinFormBrowserMVC.vbproj)

  1. Create Window Application Layer and name it WinFormBrowserMVC
  2. Drag & Drop Tab Control with two TabPages, Add/Edit Contact and List Contacts. Drop WebBrowser Control into each TabPages. 
  3. Leave at it is. We will back to this form later. We only make a grand design of application.

Third, Create model layer project ClassLibDatabaseMVC.vbproj

  1. Create Class Library Project and name it ClassLibDatabaseMVC
  2. Add new Class File (Database.vb):
    Imports System.Data
    Imports System.Data.SqlClient
    
    Friend Class Database
        Private connstr As String
        Public Sub New()
            connstr = "Data Source=.\SQLEXPRESS;Initial Catalog=TestDB;Integrated Security=true;"
        End Sub
    
        Public Sub ExecuteNonQuery(ByVal query As String)
            Dim conn As New SqlConnection(connstr)
            conn.Open()
            Dim cmd As New SqlCommand(query, conn)
    
            cmd.ExecuteNonQuery()
            conn.Close()
    
            cmd.Dispose()
            conn.Dispose()
        End Sub
    
        Public Function ReadData(ByVal query As String) As DataTable
            Dim conn As New SqlConnection(connstr)
            conn.Open()
    
            Dim da As New SqlDataAdapter(query, conn)
            Dim dt As New DataTable()
    
            da.Fill(dt)
    
            conn.Close()
    
            da.Dispose()
            conn.Dispose()
    
            Return dt
        End Function
        Public Function ReadData(ByVal query As String, ByVal startRow As Integer, ByVal maxRow As Integer) As DataTable
            Dim conn As New SqlConnection(connstr)
            conn.Open()
    
            Dim da As New SqlDataAdapter(query, conn)
            Dim dt As New DataTable()
    
            da.Fill(startRow, maxRow, dt)
    
            conn.Close()
    
            da.Dispose()
            conn.Dispose()
    
            Return dt
        End Function
    End Class
    

    Any class in ClassLibDatabaseMVC will use this database class. Its constructor set connection string.
    Database class has basic db operation like Read Data (‘select’ statement) and Execute Non Query (‘insert/update/delete/alter/drop/create’ statement).

    ReadData has overload function with query syntax string as main parameter and I use SqlDataAdapter to read sql code. startRow and maxRow parameters used in SqlDataAdapter.Fill method to limit records retrieval.

    ExecuteNonQuery procedure encapsulates SqlCommand.ExecuteNonQuery method. Overall developer does not have to write SqlConnection, SqlCommand, SqlDataAdapter over and over again. He/she only have to implement this class and run related functions.

  3. Add new Class File (Contact.vb):
    Public Class Contact
        Private _ContactID, _ContactName As String
        Private _RegDate As DateTime
    
        Public Sub New()
    
        End Sub
        Public Sub New(ByVal ContactID As Integer)
            Me._ContactID = ContactID
            Dim db As New Database()
            Dim dt As DataTable = db.ReadData("select * from Contacts where ContactID=" & ContactID)
            If dt.Rows.Count > 0 Then
                _ContactName = dt.Rows(0)("ContactName").ToString()
                _RegDate = Convert.ToDateTime(dt.Rows(0)("RegDate"))
            End If
        End Sub
    
        Public Property ContactName As String
            Get
                Return _ContactName
            End Get
            Set(ByVal value As String)
                _ContactName = value
            End Set
        End Property
        Public Property RegisterDate As DateTime
            Get
                Return _RegDate
            End Get
            Set(ByVal value As DateTime)
                _RegDate = value
            End Set
        End Property
        Public Property ContactID As String
            Get
                Return _ContactID
            End Get
            Set(ByVal value As String)
                _ContactID = value
            End Set
        End Property
    
        Public Sub SaveContact(ByVal strContactName As String)
            Dim db As New Database()
    
            Dim query As String
            If _ContactID = "" Then
                query = "Insert into Contacts (ContactName, RegDate) Values ('" & strContactName & "',getdate())"
            Else
                query = "update Contacts set ContactName='" & strContactName & "' Where ContactID=" & _ContactID
            End If
            db.ExecuteNonQuery(query)
    
        End Sub
        Public Sub Delete()
            Dim db As New Database()
            db.ExecuteNonQuery("delete from Contacts Where contactid=" & _ContactID)
        End Sub
    
        Public Shared Function ListContact() As List(Of Contact)
            Dim query As String = "select * from Contacts order by RegDate desc"
            Dim db As New Database()
            Dim dt As New DataTable()
            dt = db.ReadData(query)
            Dim lstContact As New List(Of Contact)
            For Each dr As DataRow In dt.Rows
                Dim clsContact As New Contact(Convert.ToInt32(dr("ContactID")))
                clsContact.ContactID = dr("ContactID").ToString()
                clsContact.ContactName = dr("ContactName").ToString()
                clsContact.RegisterDate = Convert.ToDateTime(dr("RegDate"))
                lstContact.Add(clsContact)
            Next
    
            Return lstContact
        End Function
    
    End Class
    

    This Contact class has two constructors with ContactID as an alternative parameter. It is to differentiate between Empty instance of Contact and ContactID filled in instance.

    Generally, the class has SaveContact, Delete and ListContact. As you can see that no SqlConnection, SqlCommand and SqlDataAdapter code attached at all functions, I only have to write with Database class.

Fourth, Create view layer project ClassLibPresentationMVC.vbproj

Since I use WebBrowser control then the layout design made by HTML code.

  1. At current solution, create a new project and name it ClassLibPresentationMVC.
  2. Add new Class Library file (iHTML.vb):
    Public Interface iHTML
        Sub BuildPage(ByVal browser As System.Windows.Forms.WebBrowser)
    End Interface
    

    This vb code is an interface and it has one procedure only BuildPage(browser) to write HTML code into specific browser.

  3. Add new Class Library file (AddEditContactHTML.vb)
    Imports System.Text
    Public Class AddEditContactHTML
        Implements iHTML
        Private _ContactID, _ContactName As String
    
        Public Sub New()
    
        End Sub
        Public Sub New(ByVal ContactID As String, ByVal ContactName As String)
            _ContactID = ContactID
            _ContactName = ContactName
        End Sub
    
        Public Sub BuildPage(ByVal browser As System.Windows.Forms.WebBrowser) Implements iHTML.BuildPage
            Dim strHtml As New StringBuilder()
            If _ContactID = "" Then
                strHtml.Append("<h2>Add Contact</h2>")
            Else
                strHtml.Append("<h2>Edit Contact</h2>")
            End If
    
            strHtml.AppendLine("<fieldset><legend>Contact</legend>")
            strHtml.AppendLine("<table>")
            strHtml.AppendLine("<tr><td><b>Contact Name:</b></td><td><input type='text' id='txcontactname' size=30 value='" & Me._ContactName & "'></td></tr>")
            If _ContactID = "" Then
                strHtml.AppendLine("<tr><td colspan=2><input type=button id='btnSave' value='Save' onclick=""javascript:location.href='contact://add'""></td></tr>")
            Else
                strHtml.AppendLine("<tr><td colspan=2><input type=button id='btnSave' value='Save' onclick=""javascript:location.href='contact://edit?id=" & _ContactID & "'""></td></tr>")
            End If
            strHtml.AppendLine("</table>")
            strHtml.AppendLine("</fieldset>")
    
            browser.DocumentText = strHtml.ToString()
        End Sub
    End Class
    

    This class is layout to create Add/Edit Contact Page. AddEditContactHTML class implements iHTML interface. So that BuildPage method must be included.

    This class has two constructors with and without arguments. Constructor without argument means empty contact so click event on button to save data redirect url to insert record into database.
    Otherwise constructor with arguments means this class has contact data.

    Button save click event redirects url to update record.
    BuildPage method has StringBuilder to store HTML code and write that to browser’s DocumentText.
    Onclick event has location.href=’contact://add’ and ‘contact://edit?id=xxx’. Those tricks has a purpose at WebBrowser_Navigating event. We can catch those urls in Navigating Event and use e.Cancel, e.Url.Scheme and e.Url.Host to run our codes like database operation.
    Please read my previous post Using VB.Net WebBrowser Control as Winform User Interface to explain this.

  4. Add new Class Library file (ListContactHTML.vb):
    Imports ClassLibDatabaseMVC
    Imports System.Text
    Public Class ListContactHTML
        Implements iHTML
    
        Private _lstContacts As List(Of Contact)
        Public Sub New(ByVal listContacts As List(Of Contact))
            _lstContacts = listContacts
        End Sub
    
        Public Sub BuildPage(ByVal browser As System.Windows.Forms.WebBrowser) Implements iHTML.BuildPage
            Public Sub BuildPage(ByVal browser As System.Windows.Forms.WebBrowser) Implements iHTML.BuildPage
            Dim strHtml As New StringBuilder()
            strHtml.Append("<h2>List Contacts</h2>")
            strHtml.AppendLine("<table>")
            strHtml.AppendLine("<thead>")
            strHtml.AppendLine("<tr><th>Contact Name</th>")
            strHtml.AppendLine("<th>Registered Date</th>")
            strHtml.AppendLine("<th>&nbsp;</th></tr>")
            strHtml.AppendLine("</thead><tbody>")
    
            For Each clsContact As Contact In _lstContacts
                strHtml.AppendLine("<tr>")
                strHtml.AppendLine("<td>" & clsContact.ContactName & "</td><td>" & clsContact.RegisterDate.ToString() & "</td>")
                strHtml.Append("<td><a href='contact://edit?id=" & clsContact.ContactID & "'>Edit</a> <a href='contact://delete?id=" & clsContact.ContactID & "'>Delete</a></td>")
                strHtml.AppendLine("</tr>")
    
            Next
            strHtml.AppendLine("</tbody></table>")
            browser.DocumentText = strHtml.ToString()
        End Sub
    End Class
    

    This class is layout to create List of Contacts Page and implements iHTML interface. So BuildPage method must be implemented.

    This class imports ClassLibDatabaseMVC to have Contact Class. This project need to add reference to ClassLibDatabaseMVC.vbproj to be able to imports ClassLibDatabaseMVC

    List Contact Page will show all contact names with link to edit and delete. Same as AddEditContactHTML, ListContactHTML class has StringBuilder to store HTML code and write that to browser’s DocumentText.

Fifth, Back to Form at WinFormBrowserMVC.vbproj

The code behind of this Form (name=frmMain)

Imports ClassLibDatabaseMVC
Imports ClassLibPresentationMVC
Public Class frmMain

    Private Sub TabControl1_SelectedIndexChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles TabControl1.SelectedIndexChanged
        If TabControl1.SelectedIndex = 0 Then
            LoadAddContact()

        ElseIf TabControl1.SelectedIndex = 1 Then
            LoadListContact()

        End If
    End Sub

    Private Sub frmMain_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
        Dim clsAddPresentation As New AddEditContactHTML()
        clsAddPresentation.BuildPage(Me.brwaddcontact)
    End Sub
    Private Sub LoadAddContact()
        Dim clsAddPresentation As New AddEditContactHTML()
        clsAddPresentation.BuildPage(Me.brwaddcontact)
    End Sub

    Private Sub LoadListContact()
        Dim lstContacts As List(Of Contact) = Contact.ListContact()

        Dim clsListPresentation As New ListContactHTML(lstContacts)
        clsListPresentation.BuildPage(Me.brwlistcontact)
    End Sub
    Private Sub brwaddcontact_Navigating(ByVal sender As Object, ByVal e As System.Windows.Forms.WebBrowserNavigatingEventArgs) Handles brwaddcontact.Navigating
        Dim clsContact As Contact
        Dim _get As Dictionary(Of String, String)
        If e.Url.Scheme = "contact" Then
            e.Cancel = True
            If e.Url.Host = "add" Then
                clsContact = New Contact()
                clsContact.SaveContact(Me.brwaddcontact.Document.GetElementById("txcontactname").GetAttribute("value"))
                MsgBox("Data Saved")
                LoadAddContact()
            ElseIf e.Url.Host = "edit" Then
                _get = extract_querystring(e.Url.Query)
                clsContact = New Contact(_get("id"))
                clsContact.SaveContact(Me.brwaddcontact.Document.GetElementById("txcontactname").GetAttribute("value"))
                MsgBox("Data Saved")
                TabControl1.SelectTab(1)
            End If
        End If
    End Sub

    Private Sub brwlistcontact_Navigating(ByVal sender As Object, ByVal e As System.Windows.Forms.WebBrowserNavigatingEventArgs) Handles brwlistcontact.Navigating
        Dim _get As Dictionary(Of String, String)
        Dim clsContact As Contact
        If e.Url.Scheme = "contact" Then
            _get = extract_querystring(e.Url.Query)
            e.Cancel = True
            If e.Url.Host = "edit" Then
                TabControl1.SelectTab(0)
                clsContact = New Contact(_get("id"))
                Dim clsEditPresentation As New AddEditContactHTML(_get("id"), clsContact.ContactName)
                clsEditPresentation.BuildPage(Me.brwaddcontact)
            ElseIf e.Url.Host = "delete" Then
                clsContact = New Contact(_get("id"))
                clsContact.Delete()
                LoadListContact()
            End If
        End If
    End Sub
    Private Function extract_querystring(ByVal strquery As String) As Dictionary(Of String, String)
        Dim strtemp As String = Replace(strquery, "?", "")
        Dim arrkeyval As String()
        Dim arrdict As New Dictionary(Of String, String)
        arrkeyval = strtemp.Split("&")
        For Each strkeyval As String In arrkeyval

            Dim pos As Integer
            Dim key, value As String
            pos = InStr(1, strkeyval, "=", Microsoft.VisualBasic.CompareMethod.Text)
            key = strkeyval.Substring(0, pos - 1)
            value = strkeyval.Substring(pos)

            arrdict.Add(key, value)
        Next
        Return arrdict
    End Function
End Class

This project need to add reference to ClassLibDatabaseMVC.vbproj and ClassLibPresentationMVC.vbproj due to it Imports ClassLibDatabaseMVC
and ClassLibPresentationMVC.

This project can access Contact Class (Business Logic), AddEditContactHTML (View) and ListContactHTML(View)

We run SaveContact method depending on ‘add’ or ‘edit’ option in brwaddcontact_Navigating procedure. Also we have e.Cancel=True means browser’s navigation is canceled when the e.Url.Scheme=”contact”.
Remember the redirected url is ‘contact://add’. e.Url.Scheme value is ‘contact’ part and e.Url.Host value is ‘add’/’edit’ part.
brwlistcontact_Navigating procedure has ‘edit’ and ‘delete’ option. We have extract_querystring method to get query id value like getting querystring value on web app

Run Controller WinFormBrowserMVC project to see the result. 

Regards,
Agung Gugiaji

2 responses to “WinForm Model View Controller Using VB.Net WebBrowser Control

  1. Pingback: Connecting Windows Forms to Database(Sql Server) with Navigation Buttons-Previous, Next, First, Last | Programming Palace

  2. Pingback: Connecting Windows Forms to Database(Sql Server) with Navigation Buttons-Previous, Next, First, Last | The Tech Story

Leave a comment