ASP.Net 4.5 Model Binding Cascading Drop Downs Null Reference (web Forms) - asp.net

I'm retrofitting an older web forms site with Model Binding using asp.net 4.5.
In a DetailsView I have a drop down list that allows selection of a particular 'client' and another that allows selection of a particular 'project' that belongs to that client. So the drop down for project has to be filtered on the client number and if the user changes the client selection, I want to filter the project list by client number.
I couldn't figure out how to get the SelectedIndexChanged method on the client ddl to fire the Select method for the Project, so I concluded the only way to do it was to filter the project ddl by client number in all cases. I am getting an error message when the client selection is made:
NullReferenceException was Unhandled by User Code
which point in my aspx directly to the ddl for Projects.
This is an abbreviated version of the details view, you can see both the clients ddl and the projects ddl (I am operating in Edit mode):
<asp:DetailsView ID="AdministratorDetailsView" runat="server" AutoGenerateRows="False"
DataKeyNames="AdministratorNumber" ItemType="BusinessLogic.Administrator"
Width="99%"
SelectMethod="AdministratorDetailsView_GetItem"
UpdateMethod="AdministratorDetailsView_UpdateItem"
DeleteMethod="AdministratorDetailsView_DeleteItem"
FieldHeaderStyle-Width="30%" EditRowStyle-Width="99%"
InsertRowStyle-Width="70%" RowStyle-Width="99%" CssClass="admin"
AutoGenerateDeleteButton="true" AutoGenerateEditButton="true" >
<Fields>
<asp:TemplateField HeaderText="AdministratorCode" SortExpression="AdministratorCode">
<EditItemTemplate>
<asp:Label ID="AdministratorCode" Text="<%# Item.AdministratorCode%>" runat="server" />
</EditItemTemplate>
<ItemTemplate>
<asp:Label ID="AdministratorCode" Text="<%# Item.AdministratorCode%>" runat="server" />
</ItemTemplate>
</asp:TemplateField>
<asp:TemplateField HeaderText="ClientNumber" SortExpression="ClientNumber">
<EditItemTemplate>
<asp:DropDownList ID="ddClients" runat="server"
AutoPostBack="true"
DataTextField="ClientName" DataValueField="ClientNumber"
ItemType="BusinessLogic.Client"
SelectMethod="ddClients_GetList"
SelectedValue="<%# Item.ClientNumber%>"
OnSelectedIndexChanged="AdministratorDetailsView_ddlClients_SelectedIndexChanged"/>
</EditItemTemplate>
<ItemTemplate>
<asp:DropDownList ID="ddClients" runat="server" Enabled="false"
DataTextField="ClientName" DataValueField="ClientNumber"
ItemType="BusinessLogic.Client"
SelectMethod="ddClients_GetList"
SelectedValue="<%# Item.ClientNumber%>"/>
</ItemTemplate>
</asp:TemplateField>
<asp:TemplateField HeaderText="Projects" >
<EditItemTemplate>
<asp:DropDownList ID="ddProjects" runat="server" AutoPostBack="true"
DataTextField="ProjectName" DataValueField="ProjectNumber"
ItemType="BusinessLogic.Project"
SelectMethod="ddProjects_GetList"
SelectedValue="<%# Item.ProjectNumber%>" />
</EditItemTemplate>
<ItemTemplate>
<asp:DropDownList ID="ddProjects" runat="server" Enabled="false"
DataTextField="ProjectName" DataValueField="ProjectNumber"
ItemType="BusinessLogic.Project"
SelectMethod="ddProjects_GetList"
SelectedValue="<%# Item.ProjectNumber%>"/>
</ItemTemplate>
</asp:TemplateField>
</Fields>
</asp:DetailsView>
There are two SelectMethods, one for each drop down list, and a selectedindexchaneged method for clients:
Public Function ddClients_GetList() As List(Of BusinessLogic.Client)
Dim special As New List(Of Client)
special = CurrentClient.ClientList() 'add whole list
Dim NullClient As New Client()
NullClient.Load(0)
NullClient.ClientName = "<-Not Selected-->"
special.Add(NullClient) 'Had to have a client with 0 in the list since most admins don't have anything but 0 inthis field
Return special
End Function
Public Function ddProjects_GetList(<Control("ddClients")> ByVal ClientNumber As Integer) As List(Of BusinessLogic.Project)
Dim special As New List(Of Project)
If ClientNumber = 0 Then
special = CurrentProject.ProjectList() 'add whole list, refine it if Clients Drop Down selected
Else
special = CurrentProject.ProjectList(ClientNumber) 'add whole list, refine it if Clients Drop Down selected
End If
Dim NullProject As New Project()
NullProject.Load(0)
NullProject.ProjectName = "<-Not Selected-->"
special.Add(NullProject) 'Had to have a Project with 0 in the list since most admins don't have anything but 0 inthis field
Return special
End Function
Protected Sub AdministratorDetailsView_ddlClients_SelectedIndexChanged(sender As Object, e As EventArgs)
Dim ddlProj As DropDownList
ddlProj = AdministratorDetailsView.FindControl("ddProjects")
ddlProj.ItemType = "BusinessLogic.Project"
ddlProj.DataBind()
End Sub
All is well until the user selects a different client which triggers the SelectIndexChanged event and then we get to the DataBind(), where we get the null exception (ddlProj is found).
Need some ideas on how to refresh the projects list based on the new client selection.
How do I force ddProjects to run its SelectMethod again, to avoid the null reference and reload the control?

Turns out this issue is not unique to ModelBinding, but is a General WebForms Issue.
You cannot have cascading dropdowns within another data control where they depend on each other unless you add a bit of code.
In my case, I was trying to bind both dropdowns to the underlying detailsview, and at the same time setting the selected values. Doesn't work because of timing. It seems right until you try to change the selection on the parent dropdown, and then you get null exception on the second because it is not yet loaded. It's all timing. If you try to work around it with saved values, it gets too messy.
My solution is, Bind another item to the underlying details view (you can make it hidden if you wish). Then add some code to keep it in sync with the actual value. DO NOT Bind SelectedValue on the secondary drop down , this is what causes the problem. Manage Selected Value in code using the added field.
<asp:DetailsView ID="AdministratorDetailsView" runat="server" AutoGenerateRows="False"
DataKeyNames="AdministratorNumber" ItemType="BusinessLogic.Administrator"
Width="99%" SelectMethod="AdministratorDetailsView_GetItem" UpdateMethod="AdministratorDetailsView_UpdateItem" DeleteMethod="AdministratorDetailsView_DeleteItem"
FieldHeaderStyle-Width="30%" EditRowStyle-Width="99%" InsertRowStyle-Width="70%" RowStyle-Width="99%" CssClass="admin" AutoGenerateDeleteButton="true" AutoGenerateEditButton="true"
>
<Fields>
<asp:TemplateField HeaderText="Client Name" SortExpression="ClientName">
<EditItemTemplate><asp:DropDownList ID="ddClients" runat="server" AutoPostBack="true" DataTextField="ClientName" DataValueField="ClientNumber" ItemType="BusinessLogic.Client" SelectMethod="ddClients_GetList" SelectedValue="<%# Item.ClientNumber%>" OnDataBound="ddClients_DataBound" OnSelectedIndexChanged="AdministratorDetailsView_ddlClients_SelectedIndexChanged" /> </EditItemTemplate>
<ItemTemplate><asp:DropDownList ID="ddClients" runat="server" Enabled="false" DataTextField="ClientName" DataValueField="ClientNumber" ItemType="BusinessLogic.Client" SelectMethod="ddClients_GetList" SelectedValue="<%# Item.ClientNumber%>"/></ItemTemplate>
</asp:TemplateField>
<asp:TemplateField HeaderText ="Project Number" >
<EditItemTemplate><asp:Label ID="ProjectNumberLabel" runat="server" Text="<%# BindItem.ProjectNumber%>"></asp:Label></EditItemTemplate>
</asp:TemplateField>
<asp:TemplateField HeaderText="Project Name" SortExpression="ProjectNumber">
<EditItemTemplate><asp:DropDownList ID="ddProjects" runat="server" AutoPostBack="true" DataTextField="ProjectName" DataValueField="ProjectNumber" ItemType="BusinessLogic.Project" SelectMethod="ddProjects_GetList" OnDataBound="ddProjects_DataBound" OnSelectedIndexChanged="ddProjects_SelectedIndexChanged" />
</EditItemTemplate>
<ItemTemplate><asp:DropDownList ID="ddProjects" runat="server" Enabled="false" DataTextField="ProjectName" DataValueField="ProjectNumber" ItemType="BusinessLogic.Project" SelectMethod="ddProjects_GetList" SelectedValue="<%# Item.ProjectNumber%>" />
</ItemTemplate>
</asp:TemplateField>
</asp:DetailsView>
And in the code behind, not how we set and get the value of the bound projectnumber:
Protected Sub AdministratorDetailsView_ddlClients_SelectedIndexChanged(sender As Object, e As EventArgs)
''If this selection changes we need to change some flags and perhaps theProject ddl
''This causes null exception for Project ddl, can't figure out how to get it to reload. Tried DataBind(), same effect as having a ValueProvider in the parameters to the Select
Dim ProjectNumberLabel As Label = AdministratorDetailsView.FindControl("ProjectNumberLabel")
ProjectNumberLabel.Text = "0" 'Synchronize to no selection
Dim ddProjects As New DropDownList
ddProjects = AdministratorDetailsView.FindControl("ddProjects") 'Have to use Find Control because ddl is buried in DetailsView
ddProjects.SelectedValue = 0 'Cause it to read Select an Item
ddProjects.DataBind() 'Rebinding it causes the SelectMethod to run.
End Sub
Protected Sub ddProjects_DataBound(sender As Object, e As EventArgs)
'Because of the problem with cascading references, we had to remove the SelectValue declaration from ddProjects.
'So we must make sure that once theddProjects is databound, it points to the selected value from the data
Dim ddlProjects As DropDownList
ddlProjects = AdministratorDetailsView.FindControl("ddProjects")
Dim ProjectNumberLabel As Label = AdministratorDetailsView.FindControl("ProjectNumberLabel")
ddlProjects.SelectedValue = CInt(ProjectNumberLabel.Text)
End Sub
Hope someone else sees this as useful. I couldn't find it anywhere.

Great posting, as I faced a similar conundrum and it made me think. I have done this before outside of the model-binding approach, and the solution is essentially the same, as described. I used a ListView, and added the method:
OnItemDataBound="lvWhatever_ItemDataBound"
to fire an event from which to go and get the relevant ID from another bound control in the list view data item:
if (e.Item.ItemType == ListViewItemType.DataItem)
{
// get the ID from from another control in this item record: lblItemID
ListViewDataItem lvdi = (ListViewDataItem)e.Item;
if (lvdi != null)
{
Label iID = (Label)lvdi.FindControl("lblItemID");
if (iID != null)
where lblItemID.Text contains the relevant ID. Then from that I can query to get the relevant list, and manually bind it to the DDL. Brilliant - thanks very much.

Related

How to bind SelectedValue of DropdownList to DataSoruce when drop down is populated programmatically?

I have a GridView. When I go into edit mode on a row, I want one of the columns to be a dropdown. My first draft included this:
<asp:TemplateField HeaderText="Type">
<ItemTemplate>
<%# Eval("type_name") %>
</ItemTemplate>
<EditItemTemplate>
<asp:DropDownList ID="ctlItemType" runat="server" datasourceid="dsItemTypes"
DataTextField="name" DataValueField="id"
selectedvalue='<%# Bind("item_type_id") %>' />
</EditItemTemplate>
</asp:TemplateField>
I should clarify that my data source included "type_name" as the name of the type and "item_type_id" as the ID corresponding to that name. That worked great. Cool.
But I need to restrict the values appearing in the drop down depending on other data about the record. So okay, fine, I removed the datasourceid, and instead populated the dropdown with code:
Private Sub ItemsGridView_RowDataBound(sender As Object, e As GridViewRowEventArgs)
Handles ItemsGridView.RowDataBound
If e.Row.RowState = DataControlRowState.Edit Then
Dim ctlItemType As DropDownList = e.Row.FindControl("ctlItemType")
Dim parent_id = ItemsGridView.DataKeys(e.Row.RowIndex).Values(1)
ctlItemType.DataSource = ItemTypeActions.List(parent_id)
ctlItemType.DataBind()
End If
End Sub
That blows up with a "SelectedValue not found" error, I presume because it's trying to set the selected value before I populate the values.
So I tried setting the SelectedValue in code also. After the DataBind above I added:
ctlItemType.SelectedValue = DataBinder.Eval(e.Row.DataItem, "item_type_id")
That displays fine when I go into edit mode, but when I click "Update", it blows up, because item_type_id doesn't get included in the list of parameters passed to the update function. Because it's not Bind'ed, it's just set with an Eval.
So how do I make this work? Is there a way to bind a control to a datasource with code? I could find anything like DataBinder.Eval that is equivalent to <%# bind("whatever") %>. Or ... what?

Filter gridview with OnChecked Event

I have a gridview loaded from a database with a checkbox in the first column. What I want is that when clicking on any row the grid will be filtered to show only the rows in which the value of the second cell is the same as the one I clicked on (having the option to select more rows with that value). Removing the checks he would reload all the information again. I tried doing it using the OnChecked event, but due to AutoPostBack it filters but loses the value of the checkbox checked. What is the best way to do what I want? I hope I have been explicit. Thank you
asp:GridView ID="gridview" runat="server" AutoGenerateColumns="false" DataKeyNames="Id">
<Columns>
<asp:TemplateField>
<ItemTemplate>
<asp:CheckBox ID="chkRow" OnCheckedChanged="chkRow_CheckedChanged" AutoPostBack="true" runat="server" />
</ItemTemplate>
</asp:TemplateField>
<asp:BoundField DataField="Document" ItemStyle-Width="10%" HeaderText="Documento" />
<asp:BoundField DataField="Entidad" ItemStyle-Width="55%" HeaderText="Entidad" />
</Columns>
For Each row As GridViewRow In gvEncomendasPendentes.Rows
If row.RowType = DataControlRowType.DataRow Then
Dim chkRow As CheckBox = TryCast(row.Cells(0).FindControl("chkRow"), CheckBox)
If chkRow.Checked Then
'Bind with filter
End If
End If
Next
Does the table display the entire dataset at all times? If so, I'd check into client-side filtering (JavaScript). Not having the round trip to the server/database would make your page a lot more responsive.
But assuming that's not the case, and to actually answer the posted question, here's how I'd approach it in the VB.Net Code. Try changing the Change Event like this:
Protected Sub chkRow_CheckedChanged (sender as object, e As EventArgs)
Dim chk As CheckBox = DirectCast(sender, CheckBox)
dim grv As GridRowView = DirectCast(chk.NamingContainer, GridRowView)
dim hdn AS HiddenField = grv.FindControl("hdnFieldWithValue")
If chk.Checked then
AddValueToFilerList(hdn.Value)
Else
RemoveValueFromFilterList(hdn.Value)
End If
BindGridView()
End Sub
Your way does work, but that has a Log(N) time on processing, as it needs to look at every row in the grid. The above way will KNOW which checkbox was checked, and deal with it appropriately.
And this small change to the markup:
<asp:hiddenField runat='server' id='hdnFilterList' />
<asp:GridView ID="gridview" runat="server" AutoGenerateColumns="false" DataKeyNames="Id">
<Columns>
<asp:TemplateField>
<ItemTemplate>
<asp:CheckBox ID="chkRow" OnCheckedChanged="chkRow_CheckedChanged" AutoPostBack="true" runat="server" checked='<%# CheckValueInFilterList(Eval("Entidad")) %>' />
<asp:hiddenField id="hdnFieldWithValue" runat="server" value='<%# Eval("Entidad")' />
</ItemTemplate>
</asp:TemplateField>
<asp:BoundField DataField="Document" ItemStyle-Width="10%" HeaderText="Documento" />
<asp:BoundField DataField="Entidad" ItemStyle-Width="55%" HeaderText="Entidad" />
</Columns>
You'll also a function something like this:
Protected Function CheckValueInFilterList(input as string) as boolean
Return MyListOfFilterItems.Contains(input)
End Function
The purpose of that would be to see if the input is contained in your list of filters you're applying to your query and returning true/false depending on that.
Which is directly related to why you're not retaining a check in the checkboxes between postbacks. Since you're rebinding the grid, the existing form state is thrown away in favor of the values from the bind. Since your markup lacked a setter for the checkbox's checked state in the databind, it always resorted to false.
I also added a hidden field to the Markup which would contain the filter list as some type of serialized string (JSON, XML, etc). I'd read it from that field on the Page Load where IsPostBack is True, and always write the list to it in the PreRender event. The point is to just have that passed back and forth through the posted form state so that you can remember them between postbacks.
Maintaining that list of filters in a control outside of the gridview as described does add a bit of overhead in the data that is sent to/from the server on roundtrips. But I wouldn't expect that to a large amount, and would expect a preformance increase due to not having to iterate through the entire gridview item list on every postback.

Populating a variable with DetailsView textbox data value

When an ASP.Net DetailsView data is first displayed, can you tell me how to populate the variable shown in this coding?
I already tried this in the code-behind file but was shown an error that:
Object reference not set to an instance of an object.
This is the coding:
Protected Sub DetailsViewDetails_DataBound(sender As Object, e As EventArgs) Handles DetailsViewDetails.DataBound
Dim txtOriginalRegistrationFee As TextBox
If DetailsViewDetails.CurrentMode = DetailsViewMode.Edit Then
txtOriginalRegistrationFee = FindControl("TextBoxRegistrationFee")
If String.IsNullOrEmpty(txtOriginalRegistrationFee.Text) = False Then
MsgBox(txtOriginalRegistrationFee)
End If
End If
End Sub
This is from the aspx file:
<asp:TemplateField HeaderText="RegistrationFee" SortExpression="RegistrationFee">
<EditItemTemplate>
<asp:TextBox ID="TextBoxRegistrationFee" runat="server" Text='<%# Eval("RegistrationFee") %>'></asp:TextBox>
</EditItemTemplate>
<InsertItemTemplate>
<asp:TextBox ID="TextBoxRegistrationFee" runat="server" Text='<%# Bind("RegistrationFee") %>'></asp:TextBox>
</InsertItemTemplate>
<ItemTemplate>
<asp:Label ID="LabelRegistrationFee" runat="server" Text='<%# Bind("RegistrationFee", "{0:c}") %>'></asp:Label>
</ItemTemplate>
<ItemStyle ForeColor="Blue" />
</asp:TemplateField>
* Update *
I tried using this coding updated based on your help but still get the "Object reference not set to an instance of an object." error when clicking the edit button of the DetailsView.
The FindControl function needs a control to search otherwise it will be searching the page (or content) level controls.
Try
txtOriginalRegistrationFee = DetailsViewDetails.FindControl("TextBoxRegistrationFee")
By the way your line with the MsgBox function will not work either. MsgBox is for windows forms and will not work on the web. You must use javascript for that type of functionality. Plus, that function takes a string, not a control.
I don't work in VB so bear with me.... The template is only rendered if your details view is in insert mode.
Dim txtOriginalRegistrationFee As TextBox
If DetailsViewDetails.CurrentMode = DetailsViewMode.Insert Then
txtOriginalRegistrationFee = FindControl("TextBoxRegistrationFee")
If String.IsNullOrEmpty(txtOriginalRegistrationFee.Text) = False Then
MsgBox(txtOriginalRegistrationFee)
End If
End If

Dropdownlist results in gridview

I'm having some trouble getting the results of the drop-downs in a gridview, and haven't found any helpful articles for VB code.
What I'm trying to do is build a site for tests. So far I have the gridview built w/the extra column for a drop-down list where the true/false answer will be selected. When the test is completed, there is a submit button. All is well except I need to be able to get the results of each drop-down list on post-back to a variable or array (each test contains 10 questions) so the results can be checked/graded. When the results are determined I would like to display a label in it's place and change the text value accordingly (correct or incorrect), so I'll need to be able to enumerate these as well (i.e. label1.text="Correct!", label2.text="Incorrect!", etc...).
Code so far.
<asp:GridView ID="GridView1" runat="server" AutoGenerateColumns="False"
DataSourceID="SqlDataSource1" BackColor="White" BorderColor="#3366CC"
BorderStyle="None" BorderWidth="1px" CellPadding="1">
<RowStyle BackColor="White" ForeColor="#003399" />
<Columns>
<asp:BoundField DataField="Question" HeaderText="Question"
SortExpression="Question" />
<asp:TemplateField HeaderText="Answer">
<ItemTemplate>
<% If Not IsPostBack Then%>
<asp:DropDownList ID="DropDownList1" runat="server" DataSourceID="SqlAnswer"
DataTextField="torf" DataValueField="torf">
</asp:DropDownList>
<%Else%>
<asp:Label ID="Label1" runat="server" Text=""></asp:Label>
<%End If%>
</ItemTemplate>
</asp:TemplateField>
</Columns>
<FooterStyle BackColor="#99CCCC" ForeColor="#003399" />
<PagerStyle BackColor="#99CCCC" ForeColor="#003399" HorizontalAlign="Left" />
<SelectedRowStyle BackColor="#009999" Font-Bold="True" ForeColor="#CCFF99" />
<HeaderStyle BackColor="#003399" Font-Bold="True" ForeColor="#CCCCFF" />
</asp:GridView>
The preceding code loads 10 rows, 2 columns (column1:Question | column2-[DropDownLists]:Answer). I'm a freshman when it comes to development, so if you need additional info let me know.
Here is how I handled it:
created a page level private variable to hold our list of correct/incorrect values
Private Answers as New List(Of Boolean) 'creates a strongly typed list of t/f values
in Page.Load
if IsPostBack then
'iterate through each row
for each gridRow As GridViewRow in GridView1.Rows
'get the selected value for this answer
Dim answer as string = CType(gridRow.FindControl("DropDownList1"),DropDownList).SelectedValue
'add this answer to the list
Answers.Add(IsCorrectAnswer(answer))
next
end if
the IsCorrectAnswer function determines whether or not the answer given for this question is correct and returns a Boolean value. You would need to write this function to suit your needs.
in Button1.Click handler
'rowCounter will act as an index to the answers
Dim rowCounter as Integer = 0
For Each gridRow as GridViewRow in GridView1.Rows
'grid_RowDataBound handles the binding of single row
grid_RowDataBound(gridRow, rowCounter)
rowCounter+=1
Next
finally
Private Sub grid_RowDataBound(gridRow as GridViewRow, rowCounter as Integer)
'make the dropdown invisible
CType(gridRow.FindControl("DropDownList1"),DropDownList).Visible = False
'because we'll be acting on two properties of the label, I shove it in a variable first for ease of access
Dim label As Label = CType(gridRow.FindControl("Label1"),Label)
'set the label to visible
label.Visible = True
'set the text
label.Text = Iif(Answers(rowCounter),"Correct", "Incorrect")
End Sub
Someone probably has a cleaner solution than this, but I do know this works, at least as far as I understand the issue you are facing.
Good luck
What about binding the Visible attribute to Page.IsPostBack (note this is in C# since I'm not familiar with the syntax for VB.NET... I'm sure something similar would work):
<ItemTemplate>
<asp:DropDownList ID="DropDownList1" runat="server" DataSourceID="SqlAnswer"
DataTextField="torf" DataValueField="torf" Visible='<%# !Page.IsPostBack %/>></asp:DropDownList>
<asp:Label ID="Label1" runat="server" Text="" Visible='<%# Page.IsPostBack %/></asp:Label>
</ItemTemplate>
check the RowDataBound event of the gridview object. It takes two parameters:
(byval sender as Object, byval e as GridViewRowEventArgs). With it, you can set the value of the label in each row to 'Correct' or 'Incorrect' on the postback.
see the example at
http://msdn.microsoft.com/en-us/library/system.web.ui.webcontrols.gridview.rowdatabound.aspx
for more information
as for getting the answers into a variable, here are a couple of options
1) check viewstate. It has some overhead, so be cautious with it.
2) save the data to a Session object

How do I data bind a drop down list in a gridview from a database table using VB?

So in this gridview, there is a column for status and I want to have a drop down list with Pass, Pending, Fail appear when the edit button is clicked. These values are already in a table, so I need to somehow bind from this table to each ddl for every row.
Here is the column from the gridview. As you can see, I would like to just have a label showing when not in edit mode, and a ddl when the edit button is pressed
<asp:TemplateField HeaderText="During Production Status" SortExpression="DuringProductionStatus">
<EditItemTemplate>
<asp:DropDownList ID="ddlStatus" runat="server" datavaluefield="Name"
datatextfield="Name" DataSource="*What goes here?*"> />
</EditItemTemplate>
<ItemTemplate>
<asp:Label ID="lblStatus" runat="server"
Text='I don't understand how to get this from the ddl' />
</ItemTemplate>
</asp:TemplateField>
For clarity's sake, my table is named Status, and the database is named DirectImport
There's a few steps to go through here - none of them are particularly difficult, but they can be a bit fiddly (IMHO). The good news is once you've got this working once, it gets easier to do it again!
I'm assuming you've got a <asp:*DataSource> control on your page - my preference is for an ObjectDataSource but I don't think it matters, I think a SqlDataSource works equally well. I've never tried doing this with GridView.DataSource = MyDataSet in code-behind, so I don't know whether that would work or not, but my assumption is that it wouldn't as you wouldn't get the proper two-way binding that you want. This data source feeds your grid with your main data. The key point here is that your database query must return both the Status text field and the Status id.
So your gridview will now look something like:
<asp:objectdatasource runat="server" id="MainDataSource" ... />
<asp:gridview runat="server" id="MyGridView" DataSourceID="MainDataSource">
<Columns>
<asp:TemplateField HeaderText="During Production Status" SortExpression="DuringProductionStatus">
<ItemTemplate>
<asp:Label ID="lblStatus" runat="server"
Text="<%# Bind('Status') %>" />
</ItemTemplate>
</asp:TemplateField>
</Columns>
</asp:gridview>
The Text="<%# Bind('Status') %>" is the bit you're missing to get the status text into the grid.
Now add a second data source into your markup that reads in the set of values from the Status table.
<asp:objectdatasource runat="server" id="StatusObjectDataSource" ... />
And add the EditItemTemplate into the GridView, which is bound to the Status DataSource.
<EditItemTemplate>
<asp:DropDownList ID="ddlStatus" runat="server" datavaluefield="StatusID"
datatextfield="Status" DataSourceID="StatusObjectDataSource"
SelectedValue="<%# Bind('StatusId') %>" />
</EditItemTemplate>
The SelectedValue="<%# Bind('StatusId') %>" is what connects up the two datasets so that when you flip a row into Edit mode, the dropdownlist has the correct item already selected, and when you then save it you've got the Status ID to put into your database.
And you're done.
I have used the RowDataBound event. Here is a small code snippet. HTH
you would have an ItemTemplate in your aspx/ascx
<asp:TemplateField HeaderText="Column Headings">
<ItemTemplate>
<asp:DropDownList ID="ddlName" runat="server" Width="150"></asp:DropDownList>
</ItemTemplate>
</asp:TemplateField>
and in your code behind, you will have
protected void grdDataMap_RowDataBound(object sender, GridViewRowEventArgs e)
{
if (e.Row.RowType == DataControlRowType.DataRow)
{
DropDownList ddl = (DropDownList)e.Row.FindControl("ddlName");
ddl.DataSource = someList;//the source of your dropdown
ddl.DataBind();
}
}
so when you bind your grid with grdDataMap.Databind (assuming your grid id is grdDataMap), row databound event will be called for each row (including header/footer, and thats the reason you check RowType)
so you can probably decide what controls/columns to hide/show/bind inside this row databound event
In the winforms world I pull my objects from the DB into a List(Of Whatever) and use the list as the datasource.
This also lets me add extra "convenience" fields in the object so that I can populate it with stuff from other tables.
I don't know asp.net at all so if you can do something similar, it might help.
A really quick solution is to create a custom web control for the status dropdown. The control will always contain the same data. When it renders you populate the datasource. When it gets added to the gridview, the data will be in the drop down. Hope that helps!

Resources