ASP.Net GridView doesn't refresh - asp.net

I am relativity new to asp.net programming, so this one has me stumped. I manually created a dataset and set its value to the Datasource of the GridView control and then call the Databind method, but it isn't refreshing. I recreated a simple version of what I am doing so someone can advise me what I am doing wrong. I didn't include the Master file, as I didn't see it as pertainent.
Code for ASP page
<%# Page Language="vb" MasterPageFile="~/MasterPage.master" AutoEventWireup="false" CodeBehind="Default.aspx.vb" Inherits="TestGridView._Default" %>
Updating price please stand by.
<p>
<asp:GridView ID="GV1" runat="server" AutoGenerateColumns="false">
<Columns>
<asp:BoundField DataField="VendorNumber" HeaderText="Vendor" />
<asp:BoundField DataField="PartNumber" HeaderText="Part or Sku" />
<asp:BoundField DataField="Message" HeaderText="Error Message" />
</Columns>
</asp:GridView>
</p>
<br />
<br />
<%
If Not Page.IsPostBack Then
Response.Write(Me.UpdatePricing())
End If
%>
Code for Code Behind
Public Class _Default
Inherits System.Web.UI.Page
Public Function UpdatePricing() As String
Dim showerrors As Boolean = False
Dim Head As New PnAHead()
Dim ds As DataSet = Head.GetErrorDataset()
If (ds.Tables("Errors").Rows.Count > 0) Then
showerrors = True
End If
If showerrors Then
GV1.DataSource = ds
GV1.DataBind()
End If
Return "Sales Line Number has been updated."
End Function
End Class
Class that creates the Dataset
Public Class PnAHead
Public Function GetErrorDataset() As DataSet
Dim dstemp = New DataSet()
Dim tbl As DataTable = dstemp.Tables.Add("Errors")
Dim col As DataColumn = tbl.Columns.Add("VendorNumber", System.Type.GetType("System.String"))
col.MaxLength = 20
col = tbl.Columns.Add("PartNumber", System.Type.GetType("System.String"))
col.MaxLength = 50
col = tbl.Columns.Add("Message", System.Type.GetType("System.String"))
col.MaxLength = 500
Dim row As DataRow
row = tbl.NewRow()
row("VendorNumber") = "Vendor 1"
row("PartNumber") = "Part Number 1"
row("Message") = "Message for Part 1"
tbl.Rows.Add(row)
row = tbl.NewRow()
row("VendorNumber") = "Vendor 2"
row("PartNumber") = "Part Number 2"
row("Message") = "Message for Part 2"
tbl.Rows.Add(row)
Return dstemp
End Function
End Class

You need to add your call to update pricing into the Page_Load event. You can also remove the Response.Write().
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
If Not Page.IsPostBack Then
Me.UpdatePricing()
End If
End Sub
You can do this in the .aspx page by wrapping the Page_Load event in script tags, like this:
<script type="text/VB" runat="server">
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
If Not Page.IsPostBack Then
Me.UpdatePricing()
End If
End Sub
</script>
Or you can just add the Page_Load event to your code behind, which i think is preferable as you already have a code behind page.

Why don't you debug your code and check of the dataset has data or not,u can do this by adding a break point on the row where you set the data source,you can get into the properties of the dataset..and you will find a magnifier icon..click it,,a pop up window will open showing the set of data retrieved.

Related

Edit Gridview update issue

I have got a gridview which when you press select on a row it transfers you to another gridview page,
which displays more columns from that table to give more detail about that row.
I know how to do this using:
<asp:HyperLinkField DataNavigateUrlFields="MISAppID" DataNavigateUrlFormatString="ApplicationsDetails.aspx?MISAppID={0}" Text="Select" />
In the 1st Gridview then using a Stored-procedure on the second page it displays the correct row using the ID field.
In my current site on the second page, I have added an edit button that does edit the row correctly in my database but on completion, it breaks the site and I can't work out how to get it to just refresh the gridview
This is the error I get:
Exception Details: System.NotSupportedException: Updating is not
supported by data source 'SqlDataSource1' unless UpdateCommand is
specified.
Is it the case that my BindGrid is missing something or is the way I am using my Stored-procedure?
Here is my VB code:
Public Sub BindGrid() Handles SqlDataSource1.Selecting
End Sub
Protected Sub OnRowEditing(sender As Object, e As GridViewEditEventArgs)
GridView1.EditIndex = e.NewEditIndex
Me.BindGrid()
End Sub
Protected Sub OnRowUpdating(sender As Object, e As GridViewUpdateEventArgs)
Dim row As GridViewRow = GridView1.Rows(e.RowIndex)
Dim misappId As Integer = Convert.ToInt32(GridView1.DataKeys(e.RowIndex).Values(0))
Dim application As String = TryCast(row.Cells(2).Controls(0), TextBox).Text
Dim url As String = TryCast(row.Cells(3).Controls(0), TextBox).Text
Dim access_group As String = TryCast(row.Cells(4).Controls(0), TextBox).Text
Dim creator_ein As String = TryCast(row.Cells(5).Controls(0), TextBox).Text
Dim data_location As String = TryCast(row.Cells(6).Controls(0), TextBox).Text
Dim purpose As String = TryCast(row.Cells(7).Controls(0), TextBox).Text
Dim active As String = TryCast(row.Cells(8).Controls(0), TextBox).Text
Dim business_owner As String = TryCast(row.Cells(9).Controls(0), TextBox).Text
Dim area As String = TryCast(row.Cells(10).Controls(0), TextBox).Text
Dim constr As String = ConfigurationManager.ConnectionStrings("myLocalConnectionString").ConnectionString
Using con As New SqlConnection(constr)
Using cmd As New SqlCommand("UPDATE tbl_AutomationCompassApplications SET Application = #Application, URL = #URL, Access_Group = #Access_Group, Creator_EIN = #Creator_EIN, Data_location = #Data_location, Purpose = #Purpose, Active = #Active, Business_Owner = #Business_Owner, Area = #Area WHERE MISAppID = #MISAppID")
cmd.Parameters.AddWithValue("#MISAppID", misappId)
cmd.Parameters.AddWithValue("#Application", application)
cmd.Parameters.AddWithValue("#URL", url)
cmd.Parameters.AddWithValue("#Access_Group", access_group)
cmd.Parameters.AddWithValue("#Creator_EIN", creator_ein)
cmd.Parameters.AddWithValue("#Data_location", data_location)
cmd.Parameters.AddWithValue("#Purpose", purpose)
cmd.Parameters.AddWithValue("#Active", active)
cmd.Parameters.AddWithValue("#Business_Owner", business_owner)
cmd.Parameters.AddWithValue("#Area", area)
cmd.Connection = con
con.Open()
cmd.ExecuteNonQuery()
con.Close()
End Using
End Using
GridView1.EditIndex = -1
Me.BindGrid()
End Sub
Ok, say we have a list of hotels - we want to display them, and then click on a row to eit.
(and you writing WAY too much code here).
So, lets say we drop in a gridview. Use the connection wizard - let it generate the markup.
THEN REMOVE the data source on the page, remove the datasource property of the gridview.
So, in less time then it takes me to write above? We have this markup:
<asp:GridView ID="GridView1" runat="server" AutoGenerateColumns="False">
<Columns>
<asp:TemplateField>
<ItemTemplate>
<asp:Button ID="cmdView" runat="server" Text="Edit"
PK = '<%# Container.DataItemIndex %>' OnClick="cmdView_Click" />
</ItemTemplate>
</asp:TemplateField>
<asp:BoundField DataField="FirstName" HeaderText="FirstName" />
<asp:BoundField DataField="HotelName" HeaderText="HotelName" />
<asp:BoundField DataField="City" HeaderText="City" />
</Columns>
</asp:GridView>
Note the cool trick I used to get the PK row index.
When you are looking at the grid, you can't double click on the button to wire up a event (code behind), but you CAN DO THIS!!!!
Note VERY care full in above - I typed in OnClick "=", when you HIT "=", then NOTE the inteli-sense that popped up - the create NEW event is what we need. Click on create new event - NOTHING seems to happen, but if we NOW go to code behind, we have a code stub for the button!!!
And note how I needed/wanted the PK row value - so I just shoved in and created my OWN custom attribute for that button. ("PK").
Ok, so our code to load up the grid is now this - and I included the button click code:
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
If IsPostBack = False Then
LoadGrid()
End If
End Sub
Sub LoadGrid()
Dim strSQL As String
strSQL = "SELECT * from tblHotels Order by HotelName"
Using cmdSQL As New SqlCommand(strSQL, New SqlConnection(My.Settings.TEST3))
cmdSQL.Connection.Open()
Dim MyTable As New DataTable
MyTable.Load(cmdSQL.ExecuteReader)
GridView1.DataSource = MyTable
GridView1.DataBind()
Session("MyTable") = MyTable
End Using
End Sub
Protected Sub cmdView_Click(sender As Object, e As EventArgs)
Dim MyBtn As Button = sender
Session("RowID") = MyBtn.Attributes.Item("PK")
Response.Redirect("~/EditHotel.aspx")
End Sub
Look how clean and simple the above is!!! I find this as easy say as MS-Access coding!!!
Ok, so the grid now looks like this:
Note the button click code.
So, our markup is this for the new page (to edit hte ONE row).
<div style="float:left;width:20%">
<div style="text-align:right">
First Name :<asp:TextBox ID="txtFirstname" runat="server" Width="150"></asp:TextBox> <br />
Last Name :<asp:TextBox ID="txtLastname" runat="server" Width="150"></asp:TextBox> <br />
Hotel Name :<asp:TextBox ID="txtHotel" runat="server" Width="150"></asp:TextBox> <br />
City :<asp:TextBox ID="txtCity" runat="server" Width="150"></asp:TextBox> <br />
Active :<asp:CheckBox ID="Active" runat="server" Width="150"></asp:CheckBox>
</div>
</div>
<div style="clear:both">
<br />
<asp:Button ID="cmdSave" runat="server" Text ="Save " />
<asp:Button ID="cmdCancel" runat="server" Text="Cancel" Style="margin-left:20px" />
</div>
</form>
and it looks like this:
and the load code and save button code for this page?
This:
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
If IsPostBack = False Then
LoadInfo()
End If
End Sub
Sub LoadInfo()
Dim MyTable As DataTable = Session("MyTable")
Dim MyRow As DataRow = MyTable.Rows(Session("RowID"))
txtFirstname.Text = MyRow("FirstName")
txtLastname.Text = MyRow("LastName")
txtCity.Text = MyRow("City")
txtHotel.Text = MyRow("HotelName")
Active.Checked = MyRow("Active")
End Sub
Protected Sub cmdSave_Click(sender As Object, e As EventArgs) Handles cmdSave.Click
Dim MyTable As DataTable = Session("MyTable")
Dim MyRow As DataRow = MyTable.Rows(Session("RowID"))
MyRow("FirstName") = txtFirstname.Text
MyRow("LastName") = txtLastname.Text
MyRow("City") = txtCity.Text
MyRow("HotelName") = txtHotel.Text
MyRow("Active") = Active.Checked
Using cmdSQL As New SqlCommand("SELECT * from tblHotels where ID = 0",
New SqlConnection(My.Settings.TEST3))
Dim da As New SqlDataAdapter(cmdSQL)
Dim cmdUpdate As New SqlCommandBuilder(da)
da.Update(MyTable)
End Using
Response.Redirect("~/MyTours.aspx")
End Sub
Again, look how easy, clean and readable the code is.
Study the above example - you see that you don't need all that parameters code, and you see how little code is in fact required to select a row - jump to page to edit, and then you hit save - update the data and jump back to the grid row page.

GridView not populating on Page_Load but on PostBack or Refresh

In an ASP.NET WebForms application there are just two controls in an aspx page, a DropDownList and a GridView. There is no default selected value of DropDownList on Page_Load. Changing the selection in DropDownList populates GridView accurately.
When the page is requested with a URL parameter such as .../View_Details.aspx?C_ID=123, the selected value in DropDownList changes but GridView does not populate for the first time but refreshing the page shows the records for given URL parameter.
ASPX markup:
<%# Page Title="Data" Language="vb" AutoEventWireup="false" MasterPageFile="~/HomePage.Master" CodeBehind="View_Details.aspx.vb" Inherits="App1.View_Details" %>
<asp:Content ID="Content4" ContentPlaceHolderID="BodyCP" runat="server">
<asp:DropDownList ID="CIDCombo" runat="server" DataSourceID="SqlDSCID" DataTextField="CName" DataValueField="CID" AutoPostBack="true"></asp:DropDownList>
<asp:SqlDataSource ID="SqlDSCID" runat="server" ... ></asp:SqlDataSource>
<asp:GridView ID="gvData" runat="server" AutoGenerateColumns="false">
<Columns>
<asp:BoundField DataField="Fld1" />
<asp:BoundField DataField="Fld2" />
...
</Columns>
</asp:GridView>
</asp:Content>
Code Behind:
Private C_ID As Long
Dim con As SqlConnection = New SqlConnection(ConfigurationManager.Connect...)
Dim cmd As New SqlCommand()
Dim stSqlQry As String = ""
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
C_ID = CLng(Request.QueryString("C_ID"))
If IsPostBack Then
Else
If C_ID > 0 Then
CIDCombo.SelectedValue = C_ID.ToString
LoadGVData(C_ID)
End If
End If
End Sub
Private Sub CIDCombo_SelectedIndexChanged(sender As Object, e As System.EventArgs) Handles CIDCombo.SelectedIndexChanged
If CIDCombo.SelectedIndex >= 0 AndAlso CLng(CIDCombo.SelectedValue) > 0 Then
LoadGVData(CLng(CIDCombo.SelectedValue))
End If
End Sub
Private Sub LoadGVData(ByVal lnCID As Long)
Try
If con.State <> ConnectionState.Open Then con.Open()
Dim da As SqlDataAdapter = New SqlDataAdapter()
stSqlQry = "SELECT Fld1, Fld2 ... WHERE CID = #CID"
da = New SqlDataAdapter()
cmd = New SqlCommand(stSqlQry, con)
cmd.Parameters.AddWithValue("#CID", lnCID)
Dim dtDataTableInc As DataTable = New DataTable("t_Data")
da.SelectCommand = cmd
da.Fill(dtDataTableInc)
'SOME DATA MANIPULATION WITH DATATABLE'
'****************************************************************************'
'DEBUG MODE SHOWS DataTable HAS ROWS BUT DON'T SHOW UP FIRST TIME IN GRIDVIEW'
'****************************************************************************'
gvData.DataSource = dtDataTableInc
gvData.DataBind()
Catch ex As Exception
'EXCEPTION HANDLING
Finally
If con.State <> ConnectionState.Closed Then con.Close()
End Try
End Sub
I see you have AutoEventWireup="false" put it on true.
Just a general note:
When working with DropDownLists and using AutoPostBack=True
make use of an UpdatePanel since the User gets frustrated when he always see a white page flickering :)
if you use an UpdatePanel you use the Onload event to populate your data
and put UpdateMode=Conditional
Good luck and happy coding.

Dynamic controls added to my content page during the preinit event do not maintain viewstate

I've added a few dynamic controls to my content page during the PreInit event, however the Viewstate is not being maintained automatically after postback, despite what is claimed about adding controls during the PreInit event. My drop down list and textbox reset. What am I doing wrong?
Mark Up
<%# Page Title="" Language="VB" MasterPageFile="~/ContentPages/MasterPage.master" AutoEventWireup="false" CodeFile="ElktonOEE.aspx.vb" Inherits="ContentPages_OEE_ElktonOEE" %>
<%# Register Assembly="AjaxControlToolkit" Namespace="AjaxControlToolkit" TagPrefix="asp" %>
<asp:Content ID="Content1" ContentPlaceHolderID="ContentPlaceHolder_Head" Runat="Server">
<link rel="stylesheet" href="http://flexweb/MES/css/UserControls/FlexGridView.css" type="text/css"/>
<link rel="stylesheet" href="http://flexweb/MES/css/LabelTexbox.css" type="text/css"/>
<link rel="stylesheet" href="http://flexweb/MES/css/OEE.css" type="text/css"/>
</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="ContentPlaceHolder_PageContentTitle" Runat="Server">
<div class="PageContentTitle">OEE Report</div>
</asp:Content>
<asp:Content ID="Content4" ContentPlaceHolderID="ContentPlaceHolder_Content" Runat="Server">
<asp:ToolkitScriptManager ID="ToolkitScriptManager1" runat="server">
</asp:ToolkitScriptManager>
<script type="text/javascript">
</script>
<div class="MESContentDiv" id="SqlQueryContentDiv">
<div id="ContentLabelDiv">
<asp:Label ID="PanelLabel" runat="server" CssClass="ContentLabel"></asp:Label>
</div>
<br />
<asp:Panel ID="Panel1" runat="server">
<!-- Controls dynamically generated in the code-behind and inserted here -->
</asp:Panel>
<br />
<br />
<asp:Panel ID="Panel2" runat="server">
<asp:Table ID="Table1" runat="server">
</asp:Table>
</asp:Panel>
</div>
</asp:Content>
Code Behind
Imports ASP 'Allows User Control to be dynamically loaded onto page
Imports System.Data 'Allows namespace access to the DataSet class
Imports System.Web.UI.UserControl
Imports MES_Class_Library
Partial Class ContentPages_OEE_ElktonOEE
Inherits System.Web.UI.Page
Protected Sub Page_PreInit(sender As Object, e As System.EventArgs) Handles Me.PreInit
'Call this to avoid null result when searching for controls on a content page
CommonFlexwebFunctions.PrepareChildControlsDuringPreInit(Page)
'Add Dynamic Controls, Dyanimc Controls Must be given ID in order for the viewstate to work.
LoadSearchPrompt()
End Sub
Protected Sub Page_Load(sender As Object, e As System.EventArgs) Handles Me.Load
Label_Debug.Visible = False
'Prompt for parameters
PanelLabel.Text = "Select OEE Parameters"
'Check for empty Query String
If (Request.QueryString("Type") IsNot Nothing) And (Request.QueryString("Date") IsNot Nothing) Then
Select Case Request.QueryString("Type")
Case "Daily"
LoadDailySummary()
Case "Weekly"
'LoadWeeklySummary()
Case "Monthly"
'LoadMonthlySummary()
End Select
Else
Panel2.Visible = False
End If
End Sub
Private Sub LoadSearchPrompt()
Dim lb1, lb2 As New Label
Dim ddl As New DropDownList
Dim tb As New TextBox
Dim ce As New AjaxControlToolkit.CalendarExtender
Dim validation_groupname As String = "ValidDate"
'Report Label
lb1.CssClass = "LabelName125 Twelve"
lb1.ID = "lbType"
lb1.Text = "Report Type:"
'Report DDL
ddl.CssClass = "ML5"
ddl.ID = "ddlReportType"
ddl.Items.Add("--")
ddl.Items.Add("Daily")
ddl.Items.Add("Weekly")
ddl.Items.Add("Monthly")
'Start Date Label
lb2.CssClass = "LabelName125 Twelve"
lb2.ID = "lbDate"
lb2.Text = "Start Date:"
'Start Date Textbox
tb.CssClass = "TextboxValue125 ML5"
tb.ID = "textboxStartDate"
tb.ValidationGroup = validation_groupname
'Calendar Extender
ce.ID = "ceDate"
ce.TargetControlID = "textboxStartDate"
'Valiation
Dim cv As New CompareValidator
Dim vs As New ValidationSummary
cv.ControlToValidate = "textboxStartDate"
cv.ID = "cv1"
cv.Display = ValidatorDisplay.None
cv.ErrorMessage = "Date must be in the mm/dd/yyyy format."
cv.Operator = ValidationCompareOperator.DataTypeCheck
cv.Type = ValidationDataType.Date
cv.ValidationGroup = validation_groupname
vs.ID = "vs1"
vs.HeaderText = "The data you entered contains an error."
vs.ShowMessageBox = True
vs.ShowSummary = False
vs.ValidationGroup = validation_groupname
'Submit
Dim btn As New Button
btn.CssClass = "Button100 LeftMargin25"
btn.ID = "btnSubmit"
btn.CausesValidation = True
btn.ValidationGroup = validation_groupname
btn.Text = "Submit"
'add handler
AddHandler btn.Click, AddressOf MyBtnClick '' this is the method to call
'Add Controls
Panel1.Controls.Add(lb1)
Panel1.Controls.Add(ddl)
Panel1.Controls.Add(lb2)
Panel1.Controls.Add(tb)
Panel1.Controls.Add(ce)
Panel1.Controls.Add(cv)
Panel1.Controls.Add(vs)
Panel1.Controls.Add(btn)
End Sub
Private Sub MyBtnClick(ByVal sender As Object, ByVal e As EventArgs)
Dim btn As Button = CType(sender, Button) ''Gets the button that fired the method
Dim ReportType As String = ""
Dim StartDate As String = ""
'Access the ddl
Dim ddl As DropDownList = CType(CommonFlexwebFunctions.RecursiveFindControl(Page, "ddlReportType"), DropDownList)
'If the proper ddl was found, set its value
If ddl IsNot Nothing Then
'Set the value
ReportType = ddl.SelectedItem.ToString()
End If
'Access the tb
Dim tb As TextBox = CType(CommonFlexwebFunctions.RecursiveFindControl(Page, "textboxStartDate"), TextBox)
'If the proper ddl was found, set its value
If tb IsNot Nothing Then
'Set the value
StartDate = tb.Text
End If
Response.Redirect("ElktonOEE.aspx?Type=" + ReportType + "&Date=" + StartDate)
End Sub
Private Sub LoadDailySummary()
Panel2.Visible = True
End Sub
End Class
Class Functions
Public Class CommonFlexwebFunctions
Public Shared Function RecursiveFindControl(container As Control, name As String) As Control
If Not (container.ID Is Nothing) AndAlso (container.ID.Equals(name)) Then
Return container
End If
For Each c As Control In container.Controls
Dim ctrl As Control = RecursiveFindControl(c, name)
If Not ctrl Is Nothing Then
Return ctrl
End If
Next
Return Nothing
End Function
Public Shared Sub PrepareChildControlsDuringPreInit(page As Page)
' Walk up the master page chain and tickle the getter on each one
' Run this so you can see the controls on content pages
Dim master As MasterPage = page.Master
While master IsNot Nothing
master = master.Master
End While
End Sub
End Class
The problem might be related to the fact that ToolkitScriptManager by default uses host aspx page url to reference it's controls scripts. Thus page's PreInit event is executed twice.
In order to avoid it - just follow the instruction provided below to instruct ScriptManager to use it's handler instead of the page's URL
http://www.asp.net/ajaxLibrary/AjaxControlToolkitSampleSite/ToolkitScriptManager/ToolkitScriptManager.aspx

Nested repeater with Checkbox list using asp.net

I am trying to do a nested repeater control with check box inside. Basically, what i want is
categorize the checkboxes like,
Group 1
Item 1
Item 2
Group 2
Item 3
Item 4
Group 3
Item 5
Item 6
The problem I am facing is, I getting the error :
Error 1 : 'DataRowView' is not declared. It may be inaccessible due to its protection level.
Error 2 : Name 'DataRowView' is not declared.
ASPX :
<asp:Repeater ID="rp_Groups" runat="server" OnItemDataBound="rp_Groups_ItemDataBound" >
<ItemTemplate>
<ul>
<asp:CheckBox runat="server" ID="chk_Group" Text='<%# Eval("category_type") %>' Value='<%# Eval("service_type_category_id") %>' onclick="OnGroupClick" />
<p class="nested">
<asp:CheckBoxList runat="server" ID="chk_Items" DataValueField="ServiceTypeID" DataTextField="Name"
DataSource='<%# ((DataRowView)Container.DataItem).CreateChildView("FK_esnServiceType_Service_Type_Categorization") %>' ></asp:CheckBoxList>
</p>
</ul>
</ItemTemplate>
</asp:Repeater>
Codebehind:
Public Sub Fill()
Dim dtServiceCategory As DataTable = ServiceTypeModel.GetService_Categories()
Dim dtServiceType As DataTable = ServiceTypeModel.Search("", True)
rp_Groups.DataSource = dtServiceCategory
rp_Groups.DataBind()
Dim ds As New DataSet()
ds.Tables.Add(dtServiceCategory)
ds.Tables.Add(dtServiceType)
Dim relation As New DataRelation("FK_esnServiceType_Service_Type_Categorization", ds.Tables("dtServiceCategory").Columns("service_type_category_id"), ds.Tables("dtServiceType").Columns("CategorizationID"), False)
ds.Relations.Add(relation)
relation.Nested = True
End Sub
Protected Sub rp_Groups_ItemDataBound(ByVal sender As Object, ByVal e As System.Web.UI.WebControls.RepeaterItemEventArgs) Handles rp_Groups.ItemDataBound
Dim chklist As CheckBoxList = DirectCast(e.Item.FindControl("chk_Items"), CheckBoxList)
If chklist IsNot Nothing Then
chklist.DataSource = DirectCast(e.Item.DataItem, DataRowView).CreateChildView("FK_esnServiceType_Service_Type_Categorization")
chklist.DataBind()
End If
End Sub
What am i missing ?
You should check which itemtype is being fired on the ItemDataBound event, this may or may not be the problem, as you don't have a header and footer template, however it is still good practice.
Protected Sub rp_Groups_ItemDataBound(ByVal sender As Object, ByVal e As System.Web.UI.WebControls.RepeaterItemEventArgs) Handles rp_Groups.ItemDataBound
If e.Item.ItemType = ListItemType.Item OrElse e.Item.ItemType = ListItemType.AlternatingItem Then
Dim chklist As CheckBoxList = DirectCast(e.Item.FindControl("chk_Items"), CheckBoxList)
If chklist IsNot Nothing Then
chklist.DataSource = DirectCast(e.Item.DataItem, DataRowView).CreateChildView("FK_esnServiceType_Service_Type_Categorization")
chklist.DataBind()
End If
End If
End Sub
EDIT
I've just noticed that you are adding the relation after you have already binded the repeater's datasource. Try moving the .databind after the relation has been added.
EDIT 2
OK can you try this. Add the datatables to the dataset and set the repeaters datasource to: ds.Tables(0)
Public Sub Fill()
Dim ds As New DataSet()
Dim dtServiceCategory As DataTable = ServiceTypeModel.GetService_Categories()
Dim dtServiceType As DataTable = ServiceTypeModel.Search("", True)
ds.Tables.Add(dtServiceCategory)
ds.Tables.Add(dtServiceType)
Dim relation As New DataRelation("FK_esnServiceType_Service_Type_Categorization", ds.Tables("dtServiceCategory").Columns("service_type_category_id"), ds.Tables("dtServiceType").Columns("CategorizationID"), False)
relation.Nested = True
ds.Relations.Add(relation)
rp_Groups.DataSource = ds.Tables(0)
rp_Groups.DataBind()
End Sub
Ensure that you have imported namespace System.Data
Try to add
<%# Import Namespace="System.Data" %>
to your ASPX file.
As another option you can import namespaces globally, not only in one page:
http://msmvps.com/blogs/simpleman/archive/2006/01/11/80804.aspx

Dynamically add drop down lists and remember them through postbacks

I'm trying to figure out the best way to achieve this:
I need to show a drop down list when the page loads, the default selected value is nothing, or an empty element (such as "-"). When the user selects one value from the list, another drop down list is added below the first one, and so on.
My problem with this is how you make the page to remember the drop down lists created through postbacks and the values they have selected? Do I have to use some kind of array to store them or something?
Thank you very much.
EDIT: I did this example for dynamically add drop down lists and suscribe them to an event handler but the event won't fire.
EDIT 2: Changed the code because I labelled for VB.Net and posted something in C#. I update the code with some trivial improvements, but still won't fire the event :(
Private myDdlArray As New List(Of DropDownList)
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
If (Session("ddl") Is Nothing) Then
Session("ddl") = myDdlArray
End If
If (Session("ddlcount") Is Nothing) Then
Session("ddlcount") = 0
End If
myDdlArray = CType(Session("ddl"), List(Of DropDownList))
Dim myDdl As New DropDownList
myDdl = New DropDownList
For Each myDdl In myDdlArray
panel.Controls.Add(myDdl)
panel.Controls.Add(New LiteralControl("<br/>"))
Next
Session("ddl") = myDdlArray
End Sub
Protected Sub btn_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles btn.Click
myDdlArray = CType(Session("ddl"), List(Of DropDownList))
Dim ddlCount As Integer = CInt(Session("ddlcount")) + 1
Dim newDdl As New DropDownList
newDdl.ID = String.Format("ddlNPKMaterial{0}", ddlCount)
newDdl.AutoPostBack = True
AddHandler newDdl.SelectedIndexChanged, AddressOf cbo_SelectedIndexChanged
newDdl.Items.Add("Uno")
newDdl.Items.Add("Dos")
newDdl.Items.Add("Tres")
myDdlArray.Add(newDdl)
panel.Controls.Clear()
Dim myDdl As New DropDownList
myDdl = New DropDownList
For Each myDdl In myDdlArray
panel.Controls.Add(myDdl)
panel.Controls.Add(New LiteralControl("<br/>"))
Next
Session("ddl") = myDdlArray
Session("ddlcount") = ddlCount + 1
End Sub
Protected Sub btnReset_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles btnReset.Click
Session("ddl") = Nothing
Session("ddlcount") = Nothing
End Sub
Protected Sub btnShow_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles btnShow.Click
' Insert brain here
End Sub
Public Sub cbo_SelectedIndexChanged(ByVal sender As Object, ByVal e As System.EventArgs)
Response.Write(CType(sender, DropDownList).ID)
End Sub
Protected Sub Page_PreRender(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.PreRender
If (Session("ddl") Is Nothing) Then
panel.Controls.Clear()
End If
End Sub
in the aspx I have:
<form id="form1" runat="server">
<div>
<asp:Button ID="btn" runat="server" Text="Add" />
<asp:Button runat="server" ID="btnShow" Text="Tell me" />
<asp:Button runat="server" ID="btnReset" Text="Reset" />
<br />
<asp:Panel runat="server" ID="panel">
<asp:GridView runat="server" ID="grd">
<Columns>
<asp:BoundField HeaderText="Id" DataField="Id" />
</Columns>
</asp:GridView>
</asp:Panel>
</div>
</form>
I'll use OnLoad to recreate the controls, but if you're not storing the # of controls in ViewState (you could put it into Session, a HiddenField, Cookie, etc.) then you can do it in OnInit. Either way should still fire the postback event.
<asp:PlaceHolder id="phDdl" runat="server">
<asp:DropDwonList id="ddl" runat="server"
OnSelectedIndexChanged="ddl_SelectedIndexChanged" />
</asp:PlaceHolder>
<script runat="server">
int DdlIndex {
get {
object o = ViewState["_ddlIndex"];
if (o == null) {
ViewState["_ddlIndex"] = 0;
}
return (int)ViewState["_ddlIndex"];
}
set {
ViewState["_ddlIndex"] = value;
}
}
protected override void OnLoad(EventArgs e) {
base.OnLoad(e);
for (int i = 0; i < DdlIndex; i++) {
AddDropDownList(i);
}
}
void ddl_SelectedIndexChanged(object sender, EventArgs e) {
var ddl = (DropDownList)sender;
if (ddl.SelectedIndex > 0) {
AddDropDownList(DdlIndex++);
}
}
void AddDropDownList(int i) {
var ddl = new DropDownList();
ddl.Id = string.Format("ddl{0}", i);
ddl.SelectedIndexChanged += ddl_SelectedIndexChanged;
// add items
phDdls.Add(ddl);
}
</script>
You can simply have both dropdown lists exist in your asp code with only one visible on first page load. So something like...
<asp:DropDownList ID="mainDDL" Visible="true" runat="server">
<asp:ListItem Text="Elements here" Value="0" />
<asp:ListItem Text="More elements" Value="1" />
</asp:DropDownList>
<asp:DropDownList ID="dynamicDDL" Visible="false" runat="server">
<asp:ListItem Text="This is an element of the dynamic DDL" Value="3" />
</asp:DropDownList>
And then when, say the "More Elements" item selected, switch the dynamicDDL's visibility to true.
Then on each postback, on the Page_Load event, check what the value of mainDDL is. If it is 0, set dynamicDDL to have visible=true
Edit:
Okay, I took a stab at this. There is some headway in this, however, and maybe it will lead us to some clues.
To start off, we DO need an array to store this. We will need a static array of DDLs and a static integer to count our elements. These can be defined as...
Private Shared ddl_arr As DropDownList() = New DropDownList(100) {} 'max, 100 ddls.
Private Shared ddl_count As Integer = 0
Now, we'll need a panel to store our DDLs in. This is simple asp scripting, such as...
<asp:Panel ID="parentPanel" runat="server">
<asp:DropDownList ID="mainDDL" AutoPostBack="true" Visible="true" runat="server">
<asp:ListItem Text="Elements here" Value="0" />
<asp:ListItem Text="More elements" Value="1" />
</asp:DropDownList>
</asp:Panel>
So now, on our page load we will want to load any of our dropdowns that we have saved so far. This can be coded in a way such as..
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
Try
If TypeOf ddl_arr(0) Is DropDownList Then
For Each ddl As DropDownList In ddl_arr
add_ddl(ddl)
Next
End If
Catch ex As Exception ' this is a bad idea, but for brevity..
End Try
End Sub
Our add_ddl method will simply add our new drop down to the parentPanel.
Protected Sub add_ddl(ByVal ddl As DropDownList)
Try
parentPanel.Controls.Add(ddl)
'add any formatting you would like after each ddl here.
Catch ex As Exception
End Try
End Sub
And finally, our method when we change the ddl. This creates a brand new ddl, gives it an id (and whatever properties you may want for it), adds it to the array, increments our counter, and adds it to the page..
Protected Sub mainDDL_SelectedIndexChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles mainDDL.SelectedIndexChanged
Dim newDDL As DropDownList = New DropDownList()
newDDL.ID = "ddlID" & ddl_count 'we will need to store a new ID for each one. So, dynamically generate this.
'continue to add properties to your ddl in this fashion...
ddl_arr(ddl_count) = newDDL
ddl_count = ddl_count + 1
add_ddl(newDDL)
End Sub
This method should definitely check for the end of the array (among other things), but lets keep things simple. This code will only add a new DDL whenever you change the index of the ORIGINAL DDL. You will need to set each newly created DDL to have a method called (performing the above instructions) whenever the selected index changes for all of those newly crafted DDLs.
Hopefully this gets you in the right direction. This was way less organized than I'd hoped, sorry!
Dynamic controls are pretty tricky to create. I would start by reading this.
Create and add the dropdowns during the Page_Init method and generate the names based on the backing data that you're gathering. When getting input from them, either read their returned values off of the page using the FindControl() method or by getting the returned value of the control from the Request.Form[]. FindControl uses the id of the control, and Requst.Name uses the client id - you can get this information using the same function you use to generate the names.
Overall, ASP.NET isn't very good at working with generated controls, but by keeping the data you're working with in a model or datastructure separate from the controls that are being generated it isn't difficult to both create the controls and retrieve the results.

Resources