Manually insert items into DDL after data binding - asp.net

I have a dropdownlist, which dynamically populate data from SQL Server and i wanna manually insert two items on top of the DDL after data binding. So, the DDL would has data something like this:
Select Branch (manually insert)
ALL (manually insert)
AIR
AMP
ABG
...
I tried to achieve it by using code below:
ddlBranch.Items.Insert(0, "Select Branch")
ddlBranch.Items(0).Value = CMM.sExcVal1
ddlBranch.Items.Insert(1, "ALL")
ddlBranch.Items(1).Value = "ALL"
but it comes out giving me the data like this:
Select Branch (manually insert)
ALL (manually insert)
('AIR' branch should be here but it's gone)
AMP
ABG
...
After manually insert the 'ALL' item into the DDL, the 'AIR' is gone which is already replaced by the 'ALL'. How can i remain all the data from server and at the same time i can manually insert two items?

It is much simpler than you think. All you need to do is change the html code as below:
<asp:DropDownList ID="dropdownlist1" runat="server" AppendDataBoundItems="true">
<asp:ListItem>Select Branch</asp:ListItem>
<asp:ListItem>ALL</asp:ListItem>
</asp:DropDownList>
The important thing is the AppendDataBoundItems="true" bit, which will append the remaining items that will be brought back from the SQL Server as normal (however you did it) after the two entries you manually typed in.
Hope that helps.
Ash (UK).

Just skip the automatic databinding.
if (!IsPostBack)
{
list.Items.Clear();
list.Items.Add(new ListItem("Select branch", ""));
list.Items.Add(new ListItem("ALL", "*");
// ----- Just bind your items here from the DB.
foreach (var item in yourCollection)
{
ListItem li = new ListItem(item.Text, item.Value);
list.Items.Add(li);
}
}

Alternatively, if you need this placeholder item in a lot of places in your app - you might want to create a Component that inherits from DropDownList. Then, override the DataBind method -
public override void DataBind()
{
base.DataBind();
ListItem li = new ListItem { Value = String.Empty, Text = "[Select an item ...]" };
this.Items.Insert(0, li);
}

Related

How to get access to DropDownList control if I add them from code

I am trying to access a DropDownList within a div after I add them from the code-behind.
If I add DropDownList to my .aspx page like this:
<div id="FiltersDiv" runat="server">
<asp:DropDownList ID="DropDownListMenu" runat="server"
DataTextField="name" DataValueField="id"
AutoPostBack="true"
onselectedindexchanged="DropDownListMenu_SelectedIndexChanged" >
</asp:DropDownList>
I can get access in any event
protected void AddButton_Click(object sender, EventArgs e)
{
var dl = addProductLocationFiltersDiv.Controls.OfType<DropDownList>();
}
but not if I add from code
var ddl = new DropDownList();
ddl.DataSource = flist;
ddl.DataTextField = "name";
ddl.DataValueField = "id";
ddl.DataBind();
FiltersDiv.Controls.Add(ddl);
I need to add several DropDownList’s and I do not now how many will be. So, I need to looping thru.
Did I miss some attribute or how I need to do it?
When you add a control via code, you access it through the variable name.
If you need access to several of these, consider putting then in a List<DropDownList> (or List<Control> if you have different types of controls).
If you need to share these across methods in your code behind, use a field instead of a local variable.
Note, that due to how the ASP.NET page lifecycle works, you will probably need to recreate these on every postback, otherwise you will not be able to access them. The best location to recreate such controls dynamically is in the OnInit handler.
foreach(DropDownList ddl in FiltersDiv.Controls.OfType<DropDownList>())
{
Trace.Write(ddl.SelectedValue);
}
This is off the top of my head, so you may need to cast the FiltersDiv to a drop down list - As said, you'll need to make sure the controls have been recreated as soon as possible on the page before the control data is written or read.
If you try this too soon, or havn't created the controls you'll get no results.

Cascading dropdown in ASP.Net is not working fine

Hi - I know this sounds simple, but I am having trouble with some functionality to be achieved. Please refer the attached screen shot of the situation. This is what I am doing:
On page load I am loading the first drop down of Product / Service
BAsed on the selection of the Product dropdown I am populating the second dropdown "Category". I am making the database call to make this happen.
Based on the selection of the Category I am populating the third drop down of the price. I am making database call to get the information for this drop down.
Based on the selection of third (Price) dropdown, I am hiding / showing the panels.
Each dropdown is doing a post back to make the call to the datbase to get the details for the subsequent dropdown.
Everything seems to be working during first run, but the problem comes when I try a combination of the drop down. Following are the problems I am having:
Panel once rendered doesn't go away if I select different option from Dropdown1 and 2.
After subsequent run, the values are added up after each post back. For example if I have a product "Toy Car" in the product / service dropdown, it is now appearing twice and so on.
If the user selects the default option "Select Product", "Select Category" etc. The entire hierarchy should be reset. For example if the previous selection in the Product dropdown was "Toy Car", and now I select the default option "Select Product". It should reset "Category", Price and Panel visibility.
Please let me know how this can be done.
Thanks for the help.
On three DropDownLists
1 You set AutoPostBack="true"
2 You define OnSelectIndexChanged
<asp:dropdownlist id="ddl1" runa="server" Autopostback="true" onselectindexChanged="ddl1_selectindexChanged"/>
<asp:dropdownlist id="ddl2" runa="server" Autopostback="true" onselectindexChanged="ddl2_selectindexChanged"/>
<asp:dropdownlist id="ddl3" runa="server" Autopostback="true" onselectindexChanged="ddl3_selectindexChanged"/>
You must define page_load with this strategy
If(! IsPostBack)
{
//Bind dropdownlist
}
On each selectindexChanged, get value and execute selection in database, and bind your dropdownlist.
void ddl1_selectindexChanged(Object sender, EventArgs e)
{
//Get Selected Values
//For examlpe you can use SelectedValue property
var value1 = ddl1.SelectedValue;
//Request your database
//And bind your dropdownlist of sub categories
...
}
void ddl2_selectindexChanged(Object sender, EventArgs e) 
{
...
}
void ddl3_selectindexChanged(Object sender, EventArgs e)
{
..;
}
Do following things:
1) Second and Third dropdown should be disabled until user select the first drop down then enable second dropdown when user selects second dropdown then enable the third drop down.
2) On post back, Clear the Items in the DropDownList and them bind them with the datasource again. Simply create a function for each dropdown and call it on PostBack after clearing DropDownList by calling Clear method of dropdownlist.
3) To reset the entire hierarchy, Set the SeletecedIndex of all the dropdowns to Zero or -1;

Show header of a empty Gridview for Datasource is List<Data>

I am using a Gridview with datasource is a List. How can I show the header if the List is null or for a empty gridview?
if you are using fx4.0 then Set ShowHeaderWhenEmpty to true in Grid view
please look at below code
asp:GridView ID="gvPreview" runat="server" ShowHeaderWhenEmpty="True"
See A more elegant solution to display GridView header and footer when the data source is empty.
You can use HeaderTemplate property to setup the head programatically or use ListView instead if you are using .NET 3.5.
or
You can even try below
//if data exists
if (dtSource.Rows.Count != 0)
{
grdView.DataSource = dtSource;
grdView.DataBind();
}
else
{
//Other wise add a emtpy "New Row" to the datatable and then hide it after binding.
dtFunding.Rows.Add(dtSource.NewRow());
grdView.DataSource = dtSource;
grdView.DataBind();
grdView.Rows[0].Visible = false;
}

ASP.NET RadioButton messing with the name (groupname)

I got a templated control (a repeater) listing some text and other markup. Each item has a radiobutton associated with it, making it possible for the user to select ONE of the items created by the repeater.
The repeater writes the radiobutton setting its id and name generated with the default ASP.NET naming convention making each radiobutton a full 'group'. This means all radiobuttons are independent on each other, which again unfortunately means I can select all radiobuttons at the same time. The radiobutton has the clever attribute 'groupname' used to set a common name, so they get grouped together and thus should be dependant (so I can only select one at a time). The problem is - this doesn't work - the repeater makes sure the id and thus the name (which controls the grouping) are different.
Since I use a repeater (could have been a listview or any other templated databound control) I can't use the RadioButtonList. So where does that leave me?
I know I've had this problem before and solved it. I know almost every ASP.NET programmer must have had it too, so why can't I google and find a solid solution to the problem? I came across solutions to enforce the grouping by JavaScript (ugly!) or even to handle the radiobuttons as non-server controls, forcing me to do a Request.Form[name] to read the status. I also tried experimenting with overriding the name attribute on the PreRender event - unfortunately the owning page and masterpage again overrides this name to reflect the full id/name, so I end up with the same wrong result.
If you have no better solution than what I posted, you are still very welcome to post your thoughts - at least I'll know that my friend 'jack' is right about how messed up ASP.NET is sometimes ;)
ASP.NET Tip: Using RadioButton Controls in a Repeater
This is the code for the JavaScript function:
function SetUniqueRadioButton(nameregex, current)
{
re = new RegExp(nameregex);
for(i = 0; i < document.forms[0].elements.length; i++)
{
elm = document.forms[0].elements[i]
if (elm.type == 'radio')
{
if (re.test(elm.name))
{
elm.checked = false;
}
}
}
current.checked = true;
}
The code is linked to the Repeater through the ItemDataBound event. For it to work properly, you need to know the name of the Repeater control, as well as the GroupName you're assigning to the RadioButtons. In this case, I'm using rptPortfolios as the name of the Repeater, and Portfolios as the group name:
protected void rptPortfolios_ItemDataBound(object sender,
RepeaterItemEventArgs e)
{
if (e.Item.ItemType != ListItemType.Item && e.Item.ItemType
!= ListItemType.AlternatingItem)
return;
RadioButton rdo = (RadioButton)e.Item.FindControl("rdoSelected");
string script =
"SetUniqueRadioButton('rptPortfolios.*Portfolios',this)";
rdo.Attributes.Add("onclick", script);
}
REF: http://www.codeguru.com/csharp/csharp/cs_controls/custom/article.php/c12371/
Google-fu: asp.net radiobutton repeater problem
Indeed an unfortunate consequence of the id mangling. My take would be creating a - or picking one of the many available - custom control that adds support for same name on the client.
Vladimir Smirnov has already created a great custom control that resolves this issue. We have been using the GroupRadioButton in our projects and it has been working perfectly with radio buttons created inside of a repeater and others outside the repeater all being a part of the same group.
I use jQuery script:
<script type="text/javascript">
function norm_radio_name() {
$("[type=radio]").each(function (i) {
var name = $(this).attr("name");
var splitted = name.split("$");
$(this).attr("name", splitted[splitted.length - 1]);
});
};
$(document).ready(function () {
norm_radio_name();
});
// for UpdatePannel
var prm = Sys.WebForms.PageRequestManager.getInstance();
prm.add_endRequest(function () {
norm_radio_name();
});
</script>
I know this is an old post, but here's what I ended up doing with a listview. My listview is bound in VB codebehind, so I'm not sure if this will work well with a repeater, but I imagine it could be similar.
What I did was handle the OnCheckChanged event of the radiobuttons with a function that unselected any other radio buttons. Then I looked for the selected radio button when I navigated away from the page.
This solution avoids JavaScript and jQuery, and ignores the GroupName issue completely. It's not ideal, but it functions as (I) expected. I hope it's helpful for others.
Markup:
<asp:ListView ID="lvw" runat="server">
<LayoutTemplate>`
<table>
<th>Radio</th>
<tr id="itemPlaceholder"></tr>
</table>
</LayoutTemplate>
<ItemTemplate>
<tr>
<td><asp:RadioButton ID="rdbSelect" runat="server" AutoPostBack="true"
OnCheckedChanged="rdbSelect_Changed"/></td>
</tr>
</ItemTemplate>
</asp:ListView>
Code:
Protected Sub rdbSelect_Changed(ByVal sender As Object, ByVal e As System.EventArgs)
Dim rb1 As RadioButton = CType(sender, RadioButton)
For Each row As ListViewItem In lvw.Items
Dim rb As RadioButton = row.FindControl("rdbSelect")
If rb IsNot Nothing AndAlso rb.Checked Then
rb.Checked = False
End If
Next
rb1.Checked = True
End Sub
And then when the Submit button is clicked:
Protected Sub btnSubmit_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles btnSubmit.Click
For Each row As ListViewItem In lvw.Items
Dim rb As RadioButton = row.FindControl("rdbSelect")
Dim lbl As Label
If rb IsNot Nothing AndAlso rb.Checked = True Then
lbl = row.FindControl("StudentID")
Session("StudentID") = lbl.Text
End If
Next
Response.Redirect("~/TransferStudent.aspx")
End Sub
This might be a little better..
I have a usercontrol which is essentially a set of radiobuttons inside a repeater, each instance of the usercontrol has a public property called FilterTitle, which is unique per instance.
add these two properties to your radiobutton replacing FilterTitle with your own public property name
onclick='<%# "$(\"input[name$=btngroup_" + FilterTitle + "]\").removeAttr(\"checked\"); $(this).attr(\"checked\",\"checked\");" %>' GroupName='<%# "btngroup_" + FilterTitle %>'
more simply..
onclick="$('input[name$=btngroup1]').removeAttr('checked'); $(this).attr('checked','checked');" GroupName="btngroup1"
Here's a pure Javascript solution for the sake of completeness.
Just add this onclick attribute to your RadioButton element(replace GroupName with your RadioButton's GroupName):
<asp:RadioButton ... GroupName="GroupName" onclick="SelectRadioButton('GroupName$',this)" ... />
And include this Javascript in your page:
<script type="text/javascript">
function SelectRadioButton(regexPattern, selectedRadioButton)
{
regex = new RegExp(regexPattern);
for (i = 0; i < document.forms[0].elements.length; i++)
{
element = document.forms[0].elements[i];
if (element.type == 'radio' && regex.test(element.name))
{
element.checked = false;
}
}
selectedRadioButton.checked = true;
}
</script>
Create a Custom Control and override UniqueID to the listview UniqueID + GroupName
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
namespace MYCONTROLS
{
[ToolboxData("<{0}:MYRADIOBUTTON runat=server></{0}:MYRADIOBUTTON >")]
public class MYRADIOBUTTON : RadioButton
{
public override string UniqueID
{
get
{
string uid = base.UniqueID;
//Fix groupname in lisview
if (this.Parent is ListViewDataItem && GroupName != "")
{
uid = this.Parent.UniqueID;
uid = uid.Remove(uid.LastIndexOf('$'));
uid += "$" + GroupName;
}
return uid;
}
}
}
}
It’s a custom control that inherits from RadioButton (public class MYRADIOBUTTON : RadioButton).
If you do nothing in the class you get a normal RadioButton. Overriding the UniqueId you can change the logic for how the name attribute is rendered.
To still keep the name unique to other controls on the page (outside the listview) you can get the UniqueId from the ListView and add GroupName to that and add it to the RadioButton.
This fix the problem with Grouping RadioButtons on different rows in a listview. You may want to add some more logic with a property to turn this feature on/off so it behaves like a normal RadioButton.
I know this question is bit old, but I think it might help somebody, therefore posting my solution to this issue.
This issue has 2 parts:
To prevent selection of more than one radio button at a time.
To know which radio button was clicked in server-side code.
I had the similar issue with Radio button in Repeater. I found partial solution here:
Simple fix for Radio Button controls in an ASP.NET Repeater using jQuery
Please read the above article to get the understanding of the issue. I referred this article and it was good help. As mentioned above this issue has two parts, first is to prevent selection of more than one radio button at a time. Second, to know which radio button was clicked in server-side code. The solution posted in above article worked for me only for the first part. However code written there as well as updates to solution posted there did not work for me. So I had to modify it a bit to get it working. Here is my solution.
I wanted to create poll with Vote button. Name of my radio button (ID) is PollGroupValue with Groupname set to PollGroup in a repeater. Remember Groupname attribute in ASP.net is rendered as name attribute in generated html. My code is as follows:
<script type="text/javascript">
/* Step-01: */
$(document).ready(function () {
/* Step-02: */
$("[name*='PollGroup']").each(function () {
$(this).attr('ci-name', $(this).attr('name'));
});
/* Step-03: */
$("[name*='PollGroup']").attr("name", $("[name*='PollGroup']").attr("name"));
$('#Poll1_BtnVote').click(function (e) {
/* Step - 04: */
if ($("[name*='PollGroup']").filter(':checked').length == 0) {
alert('Please select an option.');
e.preventDefault();
}
/* Step - 05: */
$("[name*='PollGroup']").each(function () {
$(this).attr('name', $(this).attr('ci-name'));
});
});
});
Step 1:
Whenever a radio button is used in repeater, its groupname gets changed, since asp.net changes it so as to make it unique. Therefore each radio button gets different groupname (name attribute in client-side generated markup). Due to this, user is able to select all of the options at the same time. This issue is resolved by using jquery code as explained by subsequent comments.
Step 2:
This block of code creates a new custome attribute called ci-name and copies original value of name attribute into this new custom attribute. This process repeats for every radio button in poll. This step would help us in later step.
Step 3:
This block of code sets the value of name attributes of all radio buttons in poll to the value of name attribute of first radio button. This step prevents user from selecting more than one option at a time.
Step 4:
This code inside event handler of vote button click event, checks whether user has checked at least one option. If he hasn't, an error message is shown.
Step 5:
This code inside event handler of vote button click event, sets value of name attribute of all radio buttons to their original values. This is achieved by copying value from custom attribute ci-name. This allows asp.net server side code to know which button was actually clicked.
I was also baffled by this bug and decided to just drop the repeater in favor of dynamically building a table with the controls inside. In your user control or on your page, simply add the following elements:
<asp:Table ID="theTable" runat="server">
<asp:TableHeaderRow runat="server">
<asp:TableHeaderCell runat="server" Text="Column 1"/>
<asp:TableHeaderCell runat="server" Text="Column 2"/>
<asp:TableHeaderCell runat="server" Text="Column 3"/>
</asp:TableHeaderRow>
</asp:Table>
Then add the data rows in the code behind with radio buttons and other required controls. You can of course do the same with other elements like the DIV:
<div runat="server" ID=theDiv">
</div>
But let us still hope for the ASP.NET team to get around to fixing this unfortunate issue with repeaters and list views. I still like the repeater control and use it whenever possible.
This is a pure server side approach using reflection. The RadioButton control uses the UniqueGroupName property to determine the group name. The group name is cached inside the _uniqueGroupName field. By setting this field using reflection, we can override the default group name and use a group name that is the same across all radio buttons in a repeater. Please note this code must be run in the 'PreRender' event of the 'RadioButton' control to ensure the new group name is persisted across post backs.
protected void rbOption_PreRender(object sender, EventArgs e)
{
// Get the radio button.
RadioButton rbOption = (RadioButton) sender;
// Set the group name.
var groupNameField = typeof(RadioButton).GetField("_uniqueGroupName", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
groupNameField.SetValue(rbOption, "MyGroupName");
// Set the radio button to checked if it was selected at the last post back.
var groupValue = Request.Form["MyGroupName"];
if(rbOption.Attributes["value"] == groupValue)
{
rbOption.Checked = true;
}
}
RadioButton source code: http://reflector.webtropy.com/default.aspx/Net/Net/3#5#50727#3053/DEVDIV/depot/DevDiv/releases/whidbey/netfxsp/ndp/fx/src/xsp/System/Web/UI/WebControls/RadioButton#cs/2/RadioButton#cs
This may not be the ideal solution for everyone, but I did the following using jQuery.
<asp:RadioButton ID="rbtnButton1" groupName="Group1" runat="server" />
<asp:RadioButton ID="rbtnButton2" groupName="Group1" runat="server" />
etc...
Then include the following code in your master page. (or all your pages)
$(function() {
//This is a workaround for Microsoft's GroupName bug.
//Set the radio button's groupName attribute to make it work.
$('span[groupName] > input').click(function() {
var element = this;
var span = $(element).parent();
if (element.checked) {
var groupName = $(span).attr('groupName');
var inputs = $('span[groupName=' + groupName + '] > input')
inputs.each(function() {
if (element != this)
this.checked = false;
});
}
});
});
A custom control/override to work around the HtmlInputControl.RenderAttributes() method by ignoring the RenderedNameAttribute property:
/// <summary>
/// HACK: For Microsoft&apos;s bug whereby they mash-up value of the "name" attribute.
/// </summary>
public class NameFixHtmlInputRadioButton : HtmlInputRadioButton
{
protected override void RenderAttributes(HtmlTextWriter writer)
{
// BUG: Usage of 'HtmlInputControl.RenderedNameAttribute'
// writer.WriteAttribute("name", this.RenderedNameAttribute);
writer.WriteAttribute(#"name", Attributes[#"Name"]);
Attributes.Remove(#"name");
var flag = false;
var type = Type;
if (! string.IsNullOrEmpty(type))
{
writer.WriteAttribute(#"type", type);
Attributes.Remove(#"type");
flag = true;
}
base.RenderAttributes(writer);
if (flag && DesignMode)
{
Attributes.Add(#"type", type);
}
writer.Write(#" /");
}
}
I used same Technic to uncheck other radio with jquery please find below code
<asp:RadioButton ID="rbSelectShiptoShop" runat="server" onchange="UnCheckRadio(this);" CssClass="radioShop" />
and script below
function UnCheckRadio(obj) {
$('.radioShop input[type="radio"]').attr('checked', false);
$(obj).children().attr('checked', true);
}

Adding dynamic columns to an ASP.NET Gridview

I'm having a problem dynamically adding columns to a GridView. I need to change the layout -- i.e. the included columns -- based on the value in a DropDownList. When the user changes the selection in this list, I need to remove all but the first column and dynamically add additional columns based on the selection.
I have only one column defined in my markup -- column 0, a template column, in which I declare a Select link and another application specific LinkButton. That column needs to always be there. When the ListBoxSelection is made, I remove all but the first column and then re-add the desired columns (in this sample, I've simplified it to always add a "Title" column). Here is a portion of the code:
RemoveVariableColumnsFromGrid();
BoundField b = new BoundField();
b.DataField = "Title";
this.gvPrimaryListView.Columns.Add(b);
this.gvPrimaryListView.DataBind();
private void RemoveVariableColumnsFromGrid() {
int ColCount = this.gvPrimaryListView.Columns.Count;
//Leave column 0 -- our select and view template column
while (ColCount > 1) {
this.gvPrimaryListView.Columns.RemoveAt(ColCount - 1);
--ColCount;
}
}
The first time this code runs through, I see both the static column and the dynamically added "Title" column. However, the next time a selection is made, the first column is generated empty (nothing in it). I see the title column, and I see the first column to its left -- but there's nothing generated within it. In the debugger, I can see that gvPrimaryListView does indeed still have two columns and the first one (index 0) is indeed a template column. In fact, the column even retains it's width which is set as 165px in the markup below (for debugging purposes).
Any ideas?
<asp:GridView ID="gvPrimaryListView" runat="server" Width="100%" AutoGenerateColumns="false"
DataKeyNames="Document_ID" EnableViewState="true" DataSourceID="odsPrimaryDataSource"
AllowPaging="true" AllowSorting="true" PageSize="10" OnPageIndexChanging="activeListView_PageIndexChanging"
AutoGenerateSelectButton="False" OnSelectedIndexChanged="activeListView_SelectedIndexChanged"
Visible="true" OnRowDataBound="CtlDocList_RowDataBound" Font-Size="8pt" Font-Names="Helvetica">
<Columns>
<asp:TemplateField ShowHeader="false">
<ItemTemplate>
<asp:LinkButton EnableTheming="false" ID="CtlSelectDocRowBtn" runat="server" Text="Select"
CommandName="Select" CssClass="gridbutton" OnClick="RowSelectBtn_Click" />
<asp:ImageButton EnableTheming="false" ID="DocViewBtn" runat="server" ImageUrl="../../images/ViewDoc3.png"
CssClass="gridbutton" CommandName="Select" OnClick="DocViewBtn_Click" />
</ItemTemplate>
<ItemStyle Width="165px" />
</asp:TemplateField>
</Columns>
<EmptyDataTemplate>
<asp:Label ID="Label6" runat="server" Text="No rows found." SkinID="LabelHeader"></asp:Label>
</EmptyDataTemplate>
</asp:GridView>
Just some additional information.
It has nothing to do with the fact that it is the first column but everything to do with the fact that it is a TemplateField. If I put a normal column to the left (in the markup) and shift the TemplateField column to the right, the first column renders fine, and the (now second) TemplateField column disappears.
Another strange thing -- the problem doesn't happen the first postback -- OR THE SECOND -- but it starts on the third postback and then continues for subsequent postbacks. I'm stumped.
I recently conquered silmilar issues with dynamic columns in gridviews, perhaps this will help.
First turn the viewstate off
Second add the columns programatically in a function fired in the oninit event
Lastly I used the following helper class to cause the checkboxes to instantiate when the RowDataBound event kicked off. Yes some of it is hard coded.
Heck here is all the code. Have at it :) Warrenty as is, blah blah blah...
Finally since I am just getting my feet wet DotNet any tips would be appreciated [IE don't rip me too much :) ]. And yes 'borrowed' the initial code from the web somewhere, sorry I cant remember off the top of my head :(
-- Fire this off in protected override void OnInit
private void GridViewProject_AddColumns()
{
DataSet dsDataSet = new DataSet();
TemplateField templateField = null;
try
{
StoredProcedure sp = new StoredProcedure("ExpenseReportItemType_GetList", "INTRANETWEBDB", Context.User.Identity.Name);
dsDataSet = sp.GetDataSet();
if (sp.RC != 0 && sp.RC != 3000)
{
labelMessage.Text = sp.ErrorMessage;
}
int iIndex = 0;
int iCount = dsDataSet.Tables[0].Rows.Count;
string strCategoryID = "";
string strCategoryName = "";
iStaticColumnCount = GridViewProject.Columns.Count;
// Insert all columns immediatly to the left of the LAST column
while (iIndex < iCount)
{
strCategoryName = dsDataSet.Tables[0].Rows[iIndex]["CategoryName"].ToString();
strCategoryID = dsDataSet.Tables[0].Rows[iIndex]["CategoryID"].ToString();
templateField = new TemplateField();
templateField.HeaderTemplate = new GridViewTemplateExternal(DataControlRowType.Header, strCategoryName, strCategoryID);
templateField.ItemTemplate = new GridViewTemplateExternal(DataControlRowType.DataRow, strCategoryName, strCategoryID);
templateField.FooterTemplate = new GridViewTemplateExternal(DataControlRowType.Footer, strCategoryName, strCategoryID);
// Have to decriment iStaticColumnCount to insert dynamic columns BEFORE the edit row
GridViewProject.Columns.Insert((iIndex + (iStaticColumnCount-1)), templateField);
iIndex++;
}
iFinalColumnCount = GridViewProject.Columns.Count;
iERPEditColumnIndex = (iFinalColumnCount - 1); // iIndex is zero based, Count is not
}
catch (Exception exception)
{
labelMessage.Text = exception.Message;
}
}
-- Helper Class
public class GridViewTemplateExternal : System.Web.UI.ITemplate
{
#region Fields
public DataControlRowType DataRowType;
private string strCategoryID;
private string strColumnName;
#endregion
#region Constructor
public GridViewTemplateExternal(DataControlRowType type, string ColumnName, string CategoryID)
{
DataRowType = type; // Header, DataRow,
strColumnName = ColumnName; // Header name
strCategoryID = CategoryID;
}
#endregion
#region Methods
public void InstantiateIn(System.Web.UI.Control container)
{
switch (DataRowType)
{
case DataControlRowType.Header:
// build the header for this column
Label labelHeader = new Label();
labelHeader.Text = "<b>" + strColumnName + "</b>";
// All CheckBoxes "Look Up" to the header row for this information
labelHeader.Attributes["ERICategoryID"] = strCategoryID;
labelHeader.Style["writing-mode"] = "tb-rl";
labelHeader.Style["filter"] = "flipv fliph";
container.Controls.Add(labelHeader);
break;
case DataControlRowType.DataRow:
CheckBox checkboxAllowedRow = new CheckBox();
checkboxAllowedRow.Enabled = false;
checkboxAllowedRow.DataBinding += new EventHandler(this.CheckBox_DataBinding);
container.Controls.Add(checkboxAllowedRow);
break;
case DataControlRowType.Footer:
// No data handling for the footer addition row
CheckBox checkboxAllowedFooter = new CheckBox();
container.Controls.Add(checkboxAllowedFooter);
break;
default:
break;
}
}
public void CheckBox_DataBinding(Object sender, EventArgs e)
{
CheckBox checkboxAllowed = (CheckBox)sender;// get the control that raised this event
GridViewRow row = (GridViewRow)checkboxAllowed.NamingContainer;// get the containing row
string RawValue = DataBinder.Eval(row.DataItem, strColumnName).ToString();
if (RawValue.ToUpper() == "TRUE")
{
checkboxAllowed.Checked = true;
}
else
{
checkboxAllowed.Checked = false;
}
}
#endregion
}
best solution for add dynamic column to grid view (ASP) placed on code project by below address :
please check it out :
http://www.codeproject.com/Articles/13461/how-to-create-columns-dynamically-in-a-grid-view
diningphilanderer.myopenid.com has a similar approach to what I would recommend.
The problem is that you have to rebind the grid each time a postback occurs and consequently you have to rebuild the columns. I like to have a method called BindGrid() that first clears the Columns GridView1.Columns.Clear(); then adds them programatically, then sets the datasource and calls databind. Make sure you have viewstate disabled for the grid and you have autogeneratecolumns = false;
I found this earlier today: TemplateField in a GridView doesn't have its ViewState restored when BoundFields are inserted.
Looks like a bug that Microsoft doesn't plan on fixing, so you'll have to try one of the solutions above. I'm having the same problem -- I have some DataBoundFields and some TemplateFields, and after a postback, the TemplateField based columns lose their controls and data.
I have written a short article on the similar topic that deal with dynamically populating GridView column based on the columns selected by the user in the CheckBoxList control. Hope this will help to those looking for simple demonstration How to generate GridView columns dynamically based on user selection?.
void Page_PreRenderComplete(object sender, EventArgs e)
{
// TemplateField reorder bug: if there is a TemplateField based column (or derived therefrom), GridView may blank out
// the column (plus possibly others) during any postback, if the user has moved it from its original markup position.
// This is probably a viewstate bug, as it happens only if a TemplateField based column has been moved. The workaround is
// to force a databind before each response. See https://connect.microsoft.com/VisualStudio/feedback/details/104994/templatefield-in-a-gridview-doesnt-have-its-viewstate-restored-when-boundfields-are-inserted
//
// This problem is also happening for grid views inside a TabPanel, even if the TemplateField based columns have not
// been moved. Also do a databind in that case.
//
// We also force a databind right after the user has submitted the column chooser dialog.
// (This is because the user could have moved TemplateField based column(s) but ColChooserHasMovedTemplateFields()
// returns false -- ie when the user has moved all TemplateField based columns back to their original positions.
if ((!_DataBindingDone && (ColChooserHasMovedTemplateFields() || _InTabPanel)) || _ColChooserPanelSubmitted || _ColChooserPanelCancelled)
DataBind();
// There is a problem with the GridView in case of custom paging (which is true here) that if we are on the last page,
// and we delete all row(s) of that page, GridView is not aware of the deletion during the subsequent data binding,
// will ask the ODS for the last page of data, and will display a blank. By PreRenderComplete, it will somehow have
// realized that its PageIndex, PageCount, etc. are too big and updated them properly, but this is too late
// as the data binding has already occurred with oudated page variables. So, if we were on the last page just before
// the last data binding (_LastPageIndex == _LastPageCount - 1) and PageIndex was decremented after the data binding,
// we know this scenario has happened and we redo the data binding. See http://scottonwriting.net/sowblog/archive/2006/05/30/163173.aspx
// for a discussion of the problem when the GridView uses the ODS to delete data. The discussion also applies when we
// delete data directly through ClassBuilder objects.
if (_LastPageIndex == _LastPageCount - 1 && PageIndex < _LastPageIndex)
DataBind();
if (EnableColChooser)
{
if (!_IsColChooserApplied)
ApplyColChooser(null, false, false);
else
{
// The purpose of calling ApplyColChooser() here is to order the column headers properly. The GridView
// at this point will have reverted the column headers to their original order regardless of ViewState,
// so we need to apply our own ordering. (This is not true of data cells, so we don't have to apply
// ordering to them, as reflected by the parameters of the call.)
// If we have already processed column reordering upon the column chooser panel being submitted,
// don't repeat the operation.
if (!_ColChooserPanelSubmitted)
ApplyColChooser(null, false, true);
}
}
}
I found this little nugget in the documentation, under the DataControlFieldCollection Class.
If you are using the GridView or DetailsView control, the DataControlField objects that are automatically created (for example, when the AutoGenerateColumns property is true) are not stored in the publicly accessible fields collection. You can only access and manipulate DataControlField objects that are not automatically generated.
I guess the answer is to do all of your column manipulation in code, and then your approach should work fine.
Instead of dynamically adding columns, could you define them at the outset and hide/show them as needed (either with Visible="false" or setting the CssClass of the control/header/footer to a class with "display: none;")? I use this method in some of my code, including template columns, with no issues.
Sorry, Decker. I missed a few key points obviously.. :)
If this is still a problem for you, I wonder if it makes a difference what you have in your item template? If you just put some text in there, then refresh the page a few times, does the text appear on the first load, then not on the second?
Also, when the problem arises is there any html markup at all in the cells or are they completely empty?

Resources