Creating a Custom GridView Object

An earlier post dealt with creating a GridView programmatically.  In some cases, this GridView might need to be used multiple times throughout a site, either using the same data, or perhaps a different set of data.  Several options exist when determining how to implement this.

The simplest, of course, would be simply to copy the code and then make changes in each copy as needed.  This clearly violates the DRY principle, and will only lead to heartache (and possibly carpal tunnel syndrome) in the long run.  As soon as the customer wants the look and feel of the GridView to change, you would have to change each instance where the code was copied.

The second, and slightly more desirable option, would be to refactor this code into a method that creates the GridView by calling the method.  While this is much preferable to having 20 verbatim copies of code in your site, it’s still not ideal.  This methodology contributes to spaghetti programming, and should be avoided when feasible.

The ideal method is to create an object that inherits from the GridView class, and then setting the attributes in the object.  Once the object is instantiated, any attributes that need to be added or overridden can be at runtime, prior to a databind event.

Here is an example of a class that inherits from GridView:


Public Class MyGridView
Inherits System.Web.UI.WebControls.GridView
Public Sub New(ByVal strID As String)
  ID = strID
  CssClass = "gridview"
  AutoGenerateColumns = False
  CellPadding = 4
  DataKeyNames = New String() {"ExampleID"}
  ForeColor = Drawing.ColorTranslator.FromHtml("#2a2723")
  GridLines = GridLines.None
  Width = Unit.Percentage(100)
  AllowSorting = True
  AllowPaging = False

  Dim strHeadBack As String = "#ffcb00"
  Dim strPagerBack As String = "#009ddb"
  Dim strForeColor As String = "#000000"

  HeaderStyle.BackColor = Drawing.ColorTranslator.FromHtml(strHeadBack)
  HeaderStyle.Font.Bold = True
  HeaderStyle.ForeColor = Drawing.ColorTranslator.FromHtml(strForeColor)

  RowStyle.BackColor = Drawing.ColorTranslator.FromHtml("#FFFBD6")
  RowStyle.ForeColor = Drawing.ColorTranslator.FromHtml("#2a2723")
  RowStyle.HorizontalAlign = HorizontalAlign.Center

  AlternatingRowStyle.BackColor = Drawing.Color.White

  BorderColor = Drawing.ColorTranslator.FromHtml("#d80073")
  BorderStyle = BorderStyle.Groove

  Dim ViewButton As New ButtonField
  ViewButton.HeaderText = "View"
  ViewButton.ButtonType = ButtonType.Button
  ViewButton.Text = "View"
  ViewButton.CommandName = "ViewExample"
  Columns.Add(ViewButton)

  Dim EditButton As New TemplateField
  EditButton.HeaderText = "Edit"
  EditButton.ItemTemplate = New MyButtonTemplate ' This is a user-defined class that creates this Button
  EditButton.Visible = bEditVisible
  Columns.Add(EditButton)

  Dim Voided As New CheckBoxField
  Voided.HeaderText = "Voided"
  Voided.DataField = "Voided"
  Voided.ReadOnly = True
  Voided.Visible = bVoidVisible
  Columns.Add(Voided)

  Dim ExampleDate As New TemplateField
  ExampleDate.HeaderText = "Date/Time"
  ExampleDate.SortExpression = "Date_and_Time"
  ExampleDate.ItemTemplate = New MyLabelTemplate ' This is a user-defined class that creates this Label
  ExampleDate.ItemStyle.Wrap = False
  Columns.Add(ExampleDate)

  Dim ShortColumn As New BoundField
  ShortColumn.ItemStyle.CssClass = "left"
  ShortColumn.HeaderText = "Short Column"
  ShortColumn.SortExpression = "Short_Column"
  ShortColumn.DataField = "Short_Column"
  ShortColumn.ItemStyle.Wrap = True
  ShortColumn.ItemStyle.Width = 150
  Columns.Add(ShortColumn)

  Dim LongColumn As New BoundField
  LongColumn.ItemStyle.CssClass = "left"
  LongColumn.HeaderText = "Long Column"
  LongColumn.SortExpression = "Long_Column"
  LongColumn.DataField = "Long_Column"
  LongColumn.ItemStyle.HorizontalAlign = HorizontalAlign.Left
  LongColumn.ItemStyle.Wrap = True
  LongColumn.ItemStyle.Width = 200
  Columns.Add(LongColumn)

  Dim CreatedBy As New BoundField
  CreatedBy.HeaderText = "Created By"
  CreatedBy.SortExpression = "CreatedBy"
  CreatedBy.DataField = "CreatedBy"
  Columns.Add(CreatedBy)
End Sub
End Class

To instantiate the object, simply declare the GridView as “MyGridView”, as below:


Private WithEvents gv1 As New MyGridView("gv1")

'...

gv1.DataSource = dvDataView  'A previously defined DataView

'...any other attributes to be added or overridden

' To remove a column use:

gv1.Columns.RemoveAt(x) ' where x is the index of the column

' Bind the data to the GridView

gv1.DataBind()

phPlaceHolder.Controls.Add(gv1)

And that’s it!  The custom GridView is like any other GridView; DataBind() can be called to rebind data, it can be hidden or made visible, etc.

Running SSRS Reports with Lotus Notes Databases as the Original Data Source

I had a need to be able to report on data inside a set of Lotus Notes 7.0 databases, and I wanted to use SQL Server Reporting Services (SSRS) to do this. There is no direct interface between SSRS and Notes, but I found that IBM provided an ODBC connection from Notes using their NotesSQL drivers.

I have set up an interface between Lotus Notes and Microsoft SQL Server 2000 using IBM’s NotesSQL ODBC drivers. The Lotus Notes 7 Client, NotesSQL 8.5.1 drivers, and an ODBC DSN (using the NotesSQL client) for each Lotus Notes database are all installed on the SQL Server. These ODBC drivers are compatible with 32-bit SQL 2000 but not SQL 2008 R2 (64-bit). I have not had an opportunity to test on any other platform as yet, though it’s certainly safe to assume that other versions of Lotus Notes up to 8.5.1 should work, since that’s what the NotesSQL drivers were written for.

I created a Linked Server on the SQL Server for each ODBC DSN; that is, each connected Notes database. The views and tables in the Notes DBs can be accessed via OPENQUERY commands, which can be included in a SQL database on the SQL Server. Because the connection between the two using only the Linked Server seemed so slow, I built a SQL Server database called Noteslink.

To test, I connected Query Analyzer to the NotesLink database on the SQL Server. I created Stored Procedures with the convention sp_DBname_AllDocuments to pull the All Documents view from the Notes database whose DSN had the name DBname. (In each case where there is a “DBname” below, substitute the name of the ODBC DSN.)

USE [NotesLink]
GO

/****** Object:  StoredProcedure [dbo].[sp_DBname_AllDocuments]  ******/
SET ANSI_NULLS ON
GO

SET QUOTED_IDENTIFIER OFF
GO
CREATE PROCEDURE [dbo].[sp_DBname_AllDocuments] AS
BEGIN
SELECT * FROM OPENQUERY (DBname,
'Select * from All_Documents')
END

GO

Run the command “EXEC sp_DBname_AllDocuments” in Query Analyzer and this will pull the All Documents view from the DBname database.

From here, I built various jobs that would insert the results into tables in the “NotesLink” SQL Server database. I connected the SSRS reports to the “NotesLink” database, and fun was had by all. This exact configuration will not work if you need real-time reports; you would have to use OPENQUERY statements in the SSRS queries to pull directly from the Notes databases.

Please let me know if you have tried this and what other ways this could be accomplished. As SQL Server 2000 is reaching end-of-life, a newer solution using SQL 2005/2008/2012 could become necessary.

Implementing a Factory Pattern in ASP.NET

A question posed by @Ramsharma1234 on Twitter this morning asked how to implement Factory Patterns in ASP.NET.

(definition of Factory Pattern)

Below is what would be considered a very basic form of Factory Pattern.  This method will essentially instantiate a generic Object, which is the parent of all types of Objects, and later be treated as if it were the type of child object that is used as a parameter in calling the method.  On a Web site where I needed to build Web controls dynamically, based on values from a database query, I created a method called “AddControl” that would add a generic Object to a Placeholder on my Web form:


Private Sub AddControl(ByVal oControl As Object)
   Placeholder1.Controls.Add(oControl)
End Sub

To call this method, I would instantiate a Web control such as a Button and add it to the Placeholder with the method:


Dim btnPrint As New Button
btnPrint.Attributes.Add("onclick", "javascript:window.print();")
btnPrint.Visible = False
AddControl(btnPrint)

Any type of Web control (TextBox, Label, Literal, etc.) could be instantiated and then added using this method.  For instance, if the table value for a particular field indicated the creation of a TextBox, this would be how that could be accomplished:


Dim oControl As New Object
'...
oControl = New TextBox
With oControl
.ID = strFieldName & "_mltxt"
.TextMode = TextBoxMode.MultiLine
.MaxLength = 2000
.Style("overflow") = "hidden"
.Height = 300
.Width = 500
.BorderStyle = BorderStyle.None
.Enabled = True
.ReadOnly = True
End With
AddControl(oControl)

Setting the Connection String Programmatically on a Crystal Reports ReportDocument

Programmatically setting the connection string for a Crystal Reports ReportDocument will allow you to have the report automatically use the active connection string for the rest of the application (as in previous posts). In this case, the report is created using a method that is triggered by clicking a button on a form. The log on information must be applied to each table in the ReportDocument.

        Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
            Dim crReportDocument As ReportDocument
            crReportDocument = New ReportDocument
            crReportDocument.Load(<<name of reportdocument>>)

            Dim sConn As SqlConnectionStringBuilder = New SqlConnectionStringBuilder(<<connection string>>)

            Dim tables As CrystalDecisions.CrystalReports.Engine.Tables = crReportDocument.Database.Tables

            For Each table As CrystalDecisions.CrystalReports.Engine.Table In tables
                Dim tableLogOnInfo As CrystalDecisions.Shared.TableLogOnInfo = table.LogOnInfo
                tableLogOnInfo.ConnectionInfo.ServerName = sConn.DataSource
                tableLogOnInfo.ConnectionInfo.DatabaseName = sConn.InitialCatalog
                tableLogOnInfo.ConnectionInfo.IntegratedSecurity = True
                table.ApplyLogOnInfo(tableLogOnInfo)
            Next

            ' ...

       End Sub