How to improve the design of a page with multiple case statements - asp.net

My page contains: GridView1, GridView2, Button1, Button2, DropDownList1
I bind Gridviews to the table selected in dropdown like this:
Dim results as DataTable
Select Case ddl1.SelectedValue
Case 0
results = dtZero
Case 1
results = dtOne
Case 2
results = dtTwo
Case 3
results = dtThree
End Select
GridView1.DataSource = results
GridView1.DataBind()
Then I have two buttons with the following code:
Protected Sub btn1_Click(sender As Object, e As EventArgs) Handles btn2.Click
Select Case ddl1.SelectedValue
Case 0
RunZero()
Case 1
RunOne()
Case 2
RunTwo()
Case 3
RunThree()
End Select
End Sub
And
Protected Sub btn2_Click(sender As Object, e As EventArgs) Handles btn2.Click
Select Case ddl1.SelectedValue
Case 0
RemoveZero()
Case 1
RemoveOne()
Case 2
RemoveTwo()
Case 3
RemoveThree()
End Select
End Sub
For me it looks like a lot of overhead. How can I improve then design and only specify that I'm working on the following record in dropdown list without specifying the Case condition every time? Should I change my design or leave it like it is?
Update:
RunZero, RunOne, RunTwo, RemoveZero, RemoveOne, RemoveTwo, RemoveThree - Execute six different stored procedures.

This was a fun one to solve. Thank you.
Sounds like you should be using actions.
First Create a class that will keep track of all your "operations" represented by your dropdownlist...
EDIT: Using Actions instead to show that you can use parameters for your functions if you want or not.
Class OpMode
Public DropDownKey As String
Public RunFunction As Action(Of Int32) 'This one will support a parameter'
Public RemoveFunction As Action
Public dt As Data.DataTable
Sub New(DropDownKey As String, RunFunction As Action(Of Int32), RemoveFunction As Action, dt As Data.DataTable)
Me.DropDownKey = DropDownKey
Me.RunFunction = RunFunction
Me.RemoveFunction = RemoveFunction
Me.dt = dt
End Sub
End Class
You should already have methods for running your procs.
EDIT: In this example I've allowed a parameter for the "Run" procs...
Private Sub RunOne(MyVariable As Int32)
Response.Write("RunOne")
End Sub
Private Sub RunTwo(MyVariable As Int32)
Response.Write("RunTwo")
End Sub
Private Sub RemoveOne()
Response.Write("RemoveOne")
End Sub
Private Sub RemoveTwo()
Response.Write("RemoveTwo")
End Sub
I assume you have those datatables somewhere...
Private dtOne As New Data.DataTable
Private dtTwo As New Data.DataTable
Create a readonly property to hold all your OpModes you want to use. It's light to recreate so no need to cache it.
Private ReadOnly Property OpModes As List(Of OpMode)
Get
Dim _OpModes As New List(Of OpMode)
_OpModes.Add(New OpMode("1", AddressOf RunOne, AddressOf RemoveOne, dtOne))
_OpModes.Add(New OpMode("2", AddressOf RunTwo, AddressOf RemoveTwo, dtTwo))
Return _OpModes
End Get
End Property
Make a function for searching through your list of opmodes for the one that matches your dropdownlist...
Private Function GetOpByDropDownKey(DropDownKey As String) As OpMode
Return (From x In OpModes Where x.DropDownKey = DropDownKey).First
End Function
Make a click event that works for both buttons and invokes the appropriate method...
EDIT: In this example, the RunFunction has a parameter but the RemoveFunction does not.
Private Sub btn_Click(sender As Object, e As System.EventArgs) Handles btn1.Click, btn2.Click
With GetOpByDropDownKey(dd1.SelectedValue)
If sender Is btn1 Then
.RunFunction(12345)
Else
.RemoveFunction()
End If
End With
End Sub
Now you can bind the correct datatable programmatically...
GridView1.DataSource = GetOpByDropDownKey(dd1.SelectedValue).dt
GridView1.DataBind()
This should be a lot easier to maintain once you get it working. You will have less places where you could have accidently forgotten to include an item in your case statement, since all that definition is defined once in your readonly property.
EDIT: (Removed note about delegates. Actions are all you need.)
I hope this helps you.

EDIT: This is suitable for VB.Net only, not ASP.Net.
A simpler way to acomplish this is to create a class with a ToString function and populate the list with that.
Class OpInfo
Public Sub New(ByVal Text As String, ByVal DataTable As DataTableSelect, ByVal RunMethod As Action, ByVal RemoveMethod As Action)
_Text = Text
_DataTable = DataTable
_RunMethod = RunMethod
_RemoveMethod = RemoveMethod
End Sub
Public ReadOnly Property DataTable As DataTableSelect
Get
Return _DataTable
End Get
End Property
Private _DataTable As DataTableSelect
Public Sub Run()
_RunMethod()
End Sub
Private _RunMethod As Action
Public Sub Remove()
_RemoveMethod()
End Sub
Private _RemoveMethod As Action
Public Function Overrides ToString() As String
Return _Text
End Function
Private _Text As String
End Class
You can populate the list with instances of this class. By type casting the selected item, you have access to all of its members.
Class Form1
Public Sub New()
InitializeComponents()
ddl1.Items.Add(New OpInfo("Zero", dtZero , AddressOf RunZero , AddressOf RemoveZero )
ddl1.Items.Add(New OpInfo("One", dtOne , AddressOf RunOne , AddressOf RemoveOne )
ddl1.Items.Add(New OpInfo("Two", dtTwo , AddressOf RunTwo , AddressOf RemoveTwo )
ddl1.Items.Add(New OpInfo("Three", dtThree, AddressOf RunThree, AddressOf RemoveThree)
End Sub
Protected Sub ddl1_SelectedIndexChanged(sender As Object, e As EventArgs)
If ddl1.SelectedIndex = -1 Then
' Code for when nothing is selected.
Else
GridView1.DataSource = CType(ddl1.SelectedItem, OpInfo).DataTable
GridView1.DataBind()
End If
End Sub
Protected Sub btn1_Click(sender As Object, e As EventArgs) Handles btn2.Click
If ddl1.SelectedIndex <> -1 Then CType(ddl1.SelectedItem, OpInfo).Run
End Sub
Protected Sub btn2_Click(sender As Object, e As EventArgs) Handles btn2.Click
If ddl1.SelectedIndex <> -1 Then CType(ddl1.SelectedItem, OpInfo).Remove
End Sub
End Class

Related

asp.net how do i transfer listbox values generated from a query to anoter listbox without a NullReferenceException error?

What i'm trying to do is to generate data on a ListBox from a query and then i can select some values, press a button and move those values to another ListBox.
When i try to move the values from a ListBox by the selecting the values and pressing the button i get the following error message:
System.NullReferenceException: 'Object reference not set to an instance of an object.' i think this is because im making a reference to the value and i'm not creating an instance of the value (correct me if i am wrong)
To fix this i believe i might have to instantiate each query value in an array to add to the listbox.
If this is correct what is the correct way to implement that array?
How i populate the ListBox
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
FillSitesListbox()
End Sub
Private Sub FillSitesListbox()
Try
dt = SQL.ExecQuery("SELECT s.Str_ID, s.Nm, d.str_grp_id, (Cast(s.Str_ID As varchar) + ' - ' + s.Nm + ' [ ' + Cast(d.str_grp_id As varchar) + ' ]' ) as IDDesc
FROM Retail_Store s left outer join store_group d on ( s.str_id = d.str_id )
ORDER BY s.Str_ID")
Catch ex As Exception
MsgBox(ex.Message)
Exit Sub
End Try
SitesListBox.DataTextField = "IDDesc"
SitesListBox.DataSource = dt
SitesListBox.DataBind()
End Sub
How i move values between ListBoxes
Protected Sub FromSiteButton_Click(sender As Object, e As EventArgs) Handles FromSiteButton.Click
SitesListBox.Items.Add(StoresListBox.SelectedItem)
SitesListBox.Items.Remove(SitesListBox.SelectedItem)
End Sub
Protected Sub FromStoreButton_Click(sender As Object, e As EventArgs) Handles FromSTOREButton.Click
StoresListBox.Items.Add(SitesListBox.SelectedItem)
StoresListBox.Items.Remove(SitesListBox.SelectedItem)
End Sub
How i execute a query
Public Function ExecQuery(query As String) As DataTable
Dim DBDT = New DataTable
Using DBCon As New SqlConnection(ConStr),
DBCmd As New SqlCommand(query, DBCon)
Params.ForEach(Sub(p) DBCmd.Parameters.Add(p))
Params.Clear()
DBCon.Open()
DBDT.Load(DBCmd.ExecuteReader)
End Using
Return DBDT
End Function
'Add Params
Public Sub AddParam(Name As String, Value As Object)
Dim NewParam As New SqlParameter(Name, Value)
Params.Add(NewParam)
End Sub
End Class
It seems that the code that moves the items is wrong.
In the first click (FromSiteButton_Click) you add the SelectedItem of the StoresListBox to the SiteListBox and then remove the SelectedItem from SitesListBox. I think you should add to the StoreListBox and remove from Sites selected item of that box. The same happens in the move FromStore
In any case, to avoid NRE you should always test if the reference variables that you are using are Nothing or not before using them
Protected Sub FromSiteButton_Click(sender As Object, e As EventArgs) Handles FromSiteButton.Click
If SitesListBox.SelectedItem IsNot Nothing Then
StoresListBox.Items.Add(SitesListBox.SelectedItem)
SitesListBox.Items.Remove(SitesListBox.SelectedItem)
End If
End Sub
Protected Sub FromStoreButton_Click(sender As Object, e As EventArgs) Handles FromSTOREButton.Click
If StoresListBox.SelectedItem IsNot Nothing Then
SitesListBox.Items.Add(StoresListBox.SelectedItem)
StoresListBox.Items.Remove(StoresListBox.SelectedItem)
End If
End Sub

How can I add to a dictionary if the values do not exist outside of an if statement?

How can I add to a dictionary if the values do not exist outside of an if statement?
I have tried making the variable public:
Public dictAdd As Dictionary(Of String, Integer) = New Dictionary(Of String, Integer)
I have tried making a function for it:
Public Sub dictEdit(serviceName As String, serviceValue As Integer)
dictAdd.Add(serviceName, serviceValue)
End Sub
Add with:
'dictAdd.Add(serviceName, serviceValue)
dictEdit(serviceName, serviceValue)
No matter what I do though, anything I add to the dictionary doesn't exist outside of the scope (like an if statement). I need to add and remove values throughout my entire code.
EDIT:
Here is my code:
Partial Class openapps_myapp
Inherits System.Web.UI.Page
Public dictAdd As Dictionary(Of String, Integer) = New Dictionary(Of String, Integer)
Public Sub dictEdit(serviceName As String, serviceValue As Integer)
dictAdd.Add(serviceName, serviceValue)
End Sub
'Add CBLAvailable item to CBLModifications
Protected Sub BtnAdd_Click(sender As Object, e As EventArgs) Handles BtnAdd.Click
'Some code here that gets serviceName and serviceValue from a checkboxlist
'Add each selected item from CBLAvailable to dictAdd
'dictAdd.Add(serviceName, serviceValue)
dictEdit(serviceName, serviceValue)
End Sub
Protected Sub BtnEdit_Click(sender As Object, e As EventArgs) Handles BtnEdit.Click
'Some code here that defines replaceString
If dictAdd.ContainsKey(replaceString) Then
CBLModifications.Items.Remove(CBLModifications.Items(i))
'right here is my error - dictAdd doesn't have any values - it's empty
End If
End Sub
'More irrelevant code here

Object reference not set to an instance of an object in ASP(vb)

I came across to this code that creates control dynamically. I tried it. However, whenever I run it, Object reference not set to an instance of an object error pops out and points out to a certain line in the code( i put ---> on the line). I'm a newbie in this programming language. I don't know what to do.
Here's the code I got:
Imports System.Collections.Generic
'Imports System.Data.Odbc
Partial Public Class main
Inherits System.Web.UI.Page
Private controlCounter As Integer = 0
Private myControlList As List(Of String)
Protected Overrides Sub LoadViewState(ByVal savedState As Object)
MyBase.LoadViewState(savedState)
myControlList = DirectCast(ViewState("myControlList"), List(Of String))
For Each ctlID As String In myControlList
controlCounter += 1
Dim hyper As New HyperLink()
hyper.ID = ctlID
Dim lineBreak As New LiteralControl("<br />")
PlaceHolder1.Controls.Add(hyper)
PlaceHolder1.Controls.Add(lineBreak)
Next
End Sub
Protected Sub Page_Load(ByVal sender As Object, ByVal e As EventArgs)
If Not IsPostBack Then
myControlList = New List(Of String)()
ViewState("myControlList") = myControlList
End If
End Sub
Protected Sub Button1_Click(ByVal sender As Object, ByVal e As EventArgs) Handles Button1.Click
controlCounter += 1
Dim hyper As New HyperLink()
hyper.Text = "a new text Box"
hyper.ID = "hyperlink" + controlCounter.ToString()
Dim lineBreak As New LiteralControl("<br />")
PlaceHolder1.Controls.Add(hyper)
PlaceHolder1.Controls.Add(lineBreak)
--> myControlList.Add(hyper.ID)
ViewState("myControlList") = myControlList
End Sub
End Class
please help me out. Thanks.
A button click initiates a postback, and you only set myControlList to a thing if the request is not a post back, so it's nothing.
If you're wanting to create the list on page load, and then only keep adding to it with subsequent clicks, say, then you'll need to shove myControlList into the Session, or something, after creating it, then on button click retrieve it again, add to it, and re-set it in the Session.
To do this add and getting from the Session stuff, do,
Session("someDistinctKey") = myControlList;
myControlList = CType(Session("someDisctintKey"), List(Of String))
respectively.

Having trouble with dynamic controls on my ASP.Net web site

I have an asp.net web page which is giving me some problems. I developed the following classes to generate search filters for the page. The search process is not important as it already works. I want to dynamically generate the search filters.
Imports Microsoft.VisualBasic
Imports System.Collections.Generic
Imports System.Collections.ObjectModel
Imports MEI.SPDocuments.Type
Public Class SearchFilter
Private WithEvents _genreDropDown As DropDownList
Private WithEvents _subGenreDropDown As DropDownList
Private WithEvents _txtValue As TextBox
Private _txtBoxAutoCompleteExtender As AjaxControlToolkit.AutoCompleteExtender
Private _filterGenres As Collection(Of String)
Private _programSubGenres() As String = {"Program ID,GetPrograms", "Territory ID,GetTerritories", _
"Rep Name,GetRepNames", "District ID,GetDistricts", "DM Name,GetDMNames", "Region ID,GetRegions", _
"RM Name,GetRMNames", "Speaker Counter,GetSpeaker", "Vedor ID,GetVendors", "Vendor Name,GetVendorName", _
"Date Range,", "City,", "State,", "Pay To,", "Expense Range,", "PIF ID,GetPifs"}
Private _speakerSubGenres() As String = {"Speaker Counter,GetSpeaker", "Speaker Last Name,GetSpeakerLNames", "Speaker First Name,GetSpeakerFNames"}
Private _expenseSubGenres() As String = {"Expense Counter,GetExpenses"}
Private _vendorSubGenres() As String = {"Vendor ID,GetVendors", "Vendor Name,GetVendorName"}
Private _trackSubGenres() As String = {"Track Number,GetTracks", "HCP First Name,GetHCPFName", "HCP Last NameGetHCPLName"}
Public Sub New(ByVal company As CompanyCode, ByVal year As DocumentYearCode)
_filterGenres = New Collection(Of String)
_txtValue = New TextBox
_txtValue.ID = Guid.NewGuid.ToString
_txtBoxAutoCompleteExtender = New AjaxControlToolkit.AutoCompleteExtender
With _txtBoxAutoCompleteExtender
.ID = "AC__" + _txtValue.ID
.MinimumPrefixLength = 1
.EnableCaching = False
.ServicePath = "~/AutoComplete.asmx"
.ServiceMethod = "PlaceHolder"
.TargetControlID = _txtValue.ID
.CompletionListCssClass = "CompletionList"
.CompletionListHighlightedItemCssClass = "ItemHighlighted"
.CompletionListItemCssClass = "ListItem"
.DelimiterCharacters = ""
.Enabled = True
End With
Select Case company
Case CompanyCode.AbbottAnimalHealth, CompanyCode.AbbottDiabetesCare, CompanyCode.AbbottDiagnosticsDivision, CompanyCode.AbbottMedicalOptics, CompanyCode.AbbottMolecular, _
CompanyCode.AbbottPointOfCare, CompanyCode.AbbottVascular, CompanyCode.Corporate, CompanyCode.DivAbbottNutrition, CompanyCode.EstablishedProductsDivision, _
CompanyCode.GlobalPharmaceuticalResearchAndDevelopment, CompanyCode.GlobalStrategicMarketingAndServices, CompanyCode.PharmaseuticalProductsGroup, _
CompanyCode.ProprietaryPharmaceuticalsDivision, CompanyCode.RegulatoryAffairsPPG
_filterGenres.Add("Div Docs")
Case Else
_filterGenres.Add("Program")
_filterGenres.Add("Speaker")
_filterGenres.Add("Expense")
_filterGenres.Add("Vendor")
End Select
_genreDropDown = New DropDownList
_genreDropDown.AutoPostBack = True
_genreDropDown.Attributes.Add("runat", "server")
AddHandler _genreDropDown.SelectedIndexChanged, AddressOf _genreDropDown_ItemChanged
_subGenreDropDown = New DropDownList
_subGenreDropDown.AutoPostBack = True
_subGenreDropDown.Attributes.Add("runat", "server")
AddHandler _subGenreDropDown.SelectedIndexChanged, AddressOf _subGenreDropDown_ItemChanged
PopulateDDls()
End Sub
Private Sub PopulateDDls()
_genreDropDown.Items.Add("")
For Each s As String In _filterGenres
_genreDropDown.Items.Add(s)
Next
End Sub
Private Sub _genreDropDown_ItemChanged(ByVal sender As Object, ByVal e As System.EventArgs)
_subGenreDropDown.Items.Clear()
_subGenreDropDown.Items.Add("")
Select Case _genreDropDown.SelectedItem.ToString
Case "Program"
For Each s As String In _programSubGenres
Dim li As New ListItem(s.Split(CChar(","))(0), s.Split(CChar(","))(1))
_subGenreDropDown.Items.Add(li)
Next
Case "Speaker"
For Each s As String In _speakerSubGenres
Dim li As New ListItem(s.Split(CChar(","))(0), s.Split(CChar(","))(1))
_subGenreDropDown.Items.Add(li)
Next
Case "Expense"
For Each s As String In _expenseSubGenres
Dim li As New ListItem(s.Split(CChar(","))(0), s.Split(CChar(","))(1))
_subGenreDropDown.Items.Add(li)
Next
Case "Vendor"
For Each s As String In _vendorSubGenres
Dim li As New ListItem(s.Split(CChar(","))(0), s.Split(CChar(","))(1))
_subGenreDropDown.Items.Add(li)
Next
Case "Div Docs"
For Each s As String In _trackSubGenres
Dim li As New ListItem(s.Split(CChar(","))(0), s.Split(CChar(","))(1))
_subGenreDropDown.Items.Add(li)
Next
End Select
End Sub
Private Sub _subGenreDropDown_ItemChanged(ByVal Sender As Object, ByVal e As System.EventArgs)
_txtBoxAutoCompleteExtender.ServiceMethod = _subGenreDropDown.SelectedValue.ToString
If _subGenreDropDown.SelectedValue.ToString = String.Empty Then _txtBoxAutoCompleteExtender.ServiceMethod = "PlaceHolder"
End Sub
Public Function createMyTableRow() As HtmlTableRow
Dim myRow As New HtmlTableRow
myRow.Cells.Add(New HtmlTableCell())
myRow.Cells.Add(New HtmlTableCell())
myRow.Cells.Add(New HtmlTableCell())
myRow.Cells(0).Controls.Add(_genreDropDown)
myRow.Cells(1).Controls.Add(_subGenreDropDown)
myRow.Cells(2).Controls.Add(_txtValue)
myRow.Cells(2).Controls.Add(_txtBoxAutoCompleteExtender)
Return myRow
End Function
Private Sub newAutoCompleteExtender(ByVal genre As String)
If _txtValue.Parent.Controls.Count = 2 Then
_txtValue.Parent.Controls.RemoveAt(1)
End If
_txtValue.Parent.Controls.Add(_txtBoxAutoCompleteExtender)
End Sub
End Class
Public Class SearchFilterGroup
Private _searchFilterCollection As Collection(Of SearchFilter)
Private _tableContainer As HtmlTable
Private _company As CompanyCode
Private _year As DocumentYearCode
Public WithEvents _addFilterButton As New Button
Public Sub New(ByVal company As CompanyCode, ByVal year As DocumentYearCode)
_searchFilterCollection = New Collection(Of SearchFilter)
_tableContainer = New HtmlTable
_company = company
_year = year
_addFilterButton.Text = "Add Filter"
_addFilterButton.Attributes.Add("runat", "server")
_addFilterButton.ID = "btnAddFilter"
AddHandler _addFilterButton.Click, AddressOf _addFilterButton_Click
End Sub
Public Sub _addFilterButton_Click(ByVal Sender As Object, ByVal e As System.EventArgs)
_searchFilterCollection.Add(New SearchFilter(_company, _year))
_tableContainer.Rows.Add(_searchFilterCollection(_searchFilterCollection.Count - 1).createMyTableRow)
End Sub
Public Function table() As HtmlTable
_tableContainer.Rows.Add(New HtmlTableRow)
_tableContainer.Rows(0).Cells.Add(New HtmlTableCell)
_tableContainer.Rows(0).Cells(0).ColSpan = 3
_tableContainer.Rows(0).Cells(0).Controls.Add(_addFilterButton)
_addFilterButton_Click(Nothing, Nothing)
_addFilterButton_Click(Nothing, Nothing)
Return _tableContainer
End Function
End Class
I am having problems devising a way to persist the generate controls. Any help would be appreciated.
Basically, for task which you do is better to create custom server control. In ASP.NET exist CompositeControl class which simplifies developing your own server controls which for example should include some other server controls (like panels, texboxes and etc) as children.
A very good link to start with creating your own server controls is Dino Esposito article: A Crash Course on ASP.NET Control Development: Building Composite Controls

Pass GridView Cell Value to Sub Routine in ASP.NET / VB.NET

I have a GridView with Cell 0 containing the ID that I need to pass to a Public Sub.
I cannot figure out how to pick the value from Cell 0 in order to pass it to the Sub. I have tried experimenting (see the Dimmed EventID below) but have failed. Here is my code:
Protected Sub gvAppointmentsCalls_RowCommand(ByVal sender As Object, ByVal e As System.Web.UI.WebControls.GridViewCommandEventArgs) Handles gvAppointmentsCalls.RowCommand
Dim EventID As String = gvAppointmentsCalls.Rows(e.RowIndex).Cells(0).Text
If e.CommandName = "gvAdd2Outlook" Then
Send_iCal_Call(EventID)
End If
End Sub
If I type the value directly e.g. Send_iCal_Call(123) then it works perfectly.
Public Sub Send_iCal_Call(ByRef Event_ID As Integer)
' My code in here
End Sub
Use the CommandArgument to pass the ID to the RowCommand-Handler.
For example:
CommandName="gvAdd2Outlook" CommandArgument='<%# Bind("EventID")%>'
In my opinion you should obtain your ID value within GridView's DataKeyNames property. You should define it in your grid markup this property like here
<asp:GridView DataKeyNames="EvenID">
and then will access it in code behind:
Protected Sub GridView1_RowCommand(ByVal sender As Object, ByVal e As System.Web.UI.WebControls.GridViewCommandEventArgs) Handles GridView1.RowCommand
If e.CommandName = "gvAdd2Outlook" Then
Dim EventIDString As String = GridView1.DataKeys.Item(e.RowIndex).Value.ToString()
Dim EventID As Integer
If Integer.TryParse(EventIDString, EventID) = False Then Throw New ArgumentException("Wrong EventID=" & EventID)
Send_iCal_Call(EventID)
End If
End Sub

Resources