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)
- Create Window Application Layer and name it WinFormBrowserMVC
- Drag & Drop Tab Control with two TabPages, Add/Edit Contact and List Contacts. Drop WebBrowser Control into each TabPages.
- 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
- Create Class Library Project and name it ClassLibDatabaseMVC
- 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.
- 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.
- At current solution, create a new project and name it ClassLibPresentationMVC.
- 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.
- 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. - 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> </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
Pingback: Connecting Windows Forms to Database(Sql Server) with Navigation Buttons-Previous, Next, First, Last | Programming Palace
Pingback: Connecting Windows Forms to Database(Sql Server) with Navigation Buttons-Previous, Next, First, Last | The Tech Story