Populating around 10 Gridviews using one data source ASP C# - c#

I need to display data from one table to 10 gridviews based on a filter value (column).
Lets say colours. So pink grid view should only show items that have Pink in colour column.
At the moment I have one Gridview bound (in ASP) to one Datasource. I am updating the datasource in codebehind.
Something like this:
String selectcommand = Select * from table where subject = "Pink"
sqlDatasource1.SelectCommand= (selectcommand);
mygv.Bind();
Obviously it would be a very bad idea to repeat the same code 10 times, one for each subject. Is there a better way of doing what I am after.
Main question is can I use the same datasorce with many gridviews after changing the colour?
Solution One
I bind all gridviews to one datasource and not bother about filtering by colour.
Then in gridview rowdatabound event of each gridview add something like this
if e.Row.RowType = DataControlRowType.DataRow Then
if e.Row.DataItem("colour") = "pink" then e.Row.visible = False
Any other suggestions?

You're right that duplicating the code would not be an ideal choice. However, as mentioned in the comments, you can make use of a repeater that loops over a grouped set of data (So you only query the database once), and then internally binds a GridView template.
The Aspx code could look like the following:
<asp:Repeater ID="repColors" OnItemDataBound="repColors_ItemDataBound" runat="server">
<ItemTemplate>
<asp:GridView ID="gvColor" runat="server" />
</ItemTemplate>
</asp:Repeater>
The Repeater has an OnItemDataBound that will be used to find and bind the GridView inside of its ItemTemplate. This GridView is simple, but could be as complex as you need it to be for your use case.
The aspx.cs page will contain the code you need to load all of your subjects into the repeater and then bind the gridview using the OnItemDataBound event handler.
protected void Page_Init(object sender, EventArgs e)
{
var data = GetColors();
var dataByColors = data.ToLookup(c => c.Subject, StringComparer.OrdinalIgnoreCase);
repColors.DataSource = dataByColors;
repColors.DataBind();
}
protected void repColors_ItemDataBound(object sender, RepeaterItemEventArgs e)
{
if (e.Item.ItemType == ListItemType.Item || e.Item.ItemType == ListItemType.AlternatingItem)
{
var gv = e.Item.FindControl("gvColor") as GridView;
gv.DataSource = e.Item.DataItem;
gv.DataBind();
}
}
public class Colors
{
public string Text { get; set; }
public string Subject { get; set; }
}
private IEnumerable<Colors> GetColors()
{
yield return new Colors { Text = "Color1", Subject = "Blue" };
yield return new Colors { Text = "Color2", Subject = "Pink" };
yield return new Colors { Text = "Color3", Subject = "Blue" };
yield return new Colors { Text = "Color4", Subject = "Red" };
yield return new Colors { Text = "Color5", Subject = "Pink" };
}
Page_Load is used to fetch all of your data from the database. Then we use LINQ to group it by Subject which generates objects that look something like:
{"Blue": {Color1, Color3}}
{"Pink": {Color2, Color5}}
{"Red": {Color4}}
This is then bound to the repeater. The repeater's ItemDataBound event takes each individual data item (Which is a key and a list of colors) and binds it to the GridView that it's able to find in the Repeater template. The ILookup interface will by default enumerate over the list of items that it maintains. This allows it to be passed directly to the GridView.DataSource and we don't need to worry about trying to cast it to a List or anything.

Related

after dynamically adding controls to a gridview, can no longer access controls or gv.selectedvalue

I'm adding some fields to a gridview dynamically in the gv.DataBinding event. I'm handling the selecting, paging and sorting in C#. Everything renders properly on screen and I can see the data is loaded into the gridview.
<asp:GridView ID="gvPulledBills" runat="server" AutoGenerateColumns="false"
OnDataBinding="gvPulledBills_DataBinding" OnRowDataBound="gvPulledBills_RowDataBound"
OnSelectedIndexChanged="gvPulledBills_SelectedIndexChanged"
AllowSorting="true" OnSorting="gvPulledBills_Sorting"
AllowPaging="true" PageSize="30" OnPageIndexChanging="gvPulledBills_PageIndexChanging"
DataKeyNames="Id" SkinID="gridviewGray">
In the gv.SelectedIndexChanged event, I need to retrieve the Id of the row selected. Id is stored in a HiddenField and the gv.DataKeyNames value is set to ID so I have two ways to retrieve it.
gv.SelectedValue works fine after the initial render. However, when selecting a row after paging/sorting, the gv.SelectedValue returns null. It behaves as if nothing was selected at all, even though my selected row markup is working correctly. Any suggestions on what I need to do to ensure the datakey is retrievable when binding columns dynamically?
Alternatively, I've tried accessing the hidden field directly rather than depending on the gv.SelectedValue...
protected void gvPulledBills_SelectedIndexChanged(object sender, EventArgs e)
{
GridView gv = (GridView)sender;
//var key = (int)gv.SelectedValue;
var index = gv.SelectedIndex - (gv.PageIndex * gv.PageSize);
var row = gv.Rows[index];
var hiddenField = (HiddenField)row.FindControl("hdnId");
var key = int.Parse(hiddenField.Value);
...
}
but the controls collections are empty in every cell, even for explicitly declared fields, even though there is data on the screen.
Explicit declaration
<Columns>
<asp:TemplateField>
<ItemTemplate>
<asp:HiddenField ID="hdnId" runat="server" Value='<%# Eval("Id") %>' />
</ItemTemplate>
</asp:TemplateField>
</Columns>
Dynamic declaration
private TemplateField CreateTemplateField()
{
var controls = new List<Control>
{
{ CreateHiddenIdField() }
};
var tf = GridViewTools.CreateTemplateField(string.Empty, string.Empty, controls);
tf.ItemStyle.CssClass = "noRightBorder";
tf.HeaderStyle.CssClass = "noBorder";
return tf;
}
private static Control CreateHiddenIdField()
{
var hdn = new HiddenField();
hdn.ID = "hdnId";
hdn.Value = "'<%# Eval(\"Id\") %>'";
return hdn;
}
//in static class GridViewTools
public static TemplateField CreateTemplateField(string headerText, string sortExpression, List<Control> controls)
{
TemplateField tf = new TemplateField();
tf.HeaderText = headerText;
tf.SortExpression = sortExpression;
tf.ItemTemplate = new GridViewTemplate(DataControlRowType.DataRow, controls);
return tf;
}
//... snippet that adds column to grid
var addColumns = new List<DataControlField>();
addColumns.Add(CreateTemplateField());
// add other columns
foreach (var column in addColumns)
{
gv.Columns.Add(column);
}
//...
Does anyone have any suggestions on how to get the SelectedValue populated when using code behind paging/sorting with dynamic columns? Or any suggestions on how to ensure the dynamically created field controls are populated and accessible in code behind?
I have many more columns added, mostly bound fields. I'm doing this to customize the column set based on drop-down fields elsewhere on the page. It's around 30 columns total and I would prefer not to add them all to the gridview, rather than just setting visibility on them all.
UPDATE SOLUTION
The final solution for this was:
var gv = (GridView)sender;
var rowIndex = gv.SelectedIndex - (gv.PageIndex * gv.PageSize);
var key = (int)gv.DataKeys[rowIndex]["Id"];
DisplayWorkDetail(key);
SelectedRow and SelectedValue are null at this point. Only SelectedIndex is populated. By converting the SelectedIndex to a RowIndex, I was able to retrieve the DataKey directly from the row.
I chose to use sender to make the code generic so I can easily extract it to a method for use with other GridViews.
I don't think you can use sender here.
but, while the Rowcommand has NOT changed the selected index, in that index changed event, it HAS changed. So, you can directly use/reference the control
eg:
GridViewRow myGv As GridViewRow = gvPulledBills.SelectedRow
and then:
myGv.RowIndex (get/use row index).
But, you already have the row, so:
hiddenField myField = (HiddenField)myGv.FindControl("hdnId");
int MyKey = int.Parse(myField.Value);
You can also I suppose use the Datakeys collection and not even have that hidden field.
So, you could do this:
GridViewRow myGv As GridViewRow = gvPulledBills.SelectedRow
int RowPK = (int)(gvPulledBills.DataKeys[myGv.RowIndex]["ID"]);

asp.net dropdownlist does not remember choice

I'm currently running into a head-scratching bug in my ASP.NET Windows Form App. I have two DropDownList, implemented them in the same way, yet they behave differently.
The problem:
When selecting an item in the DropDownList "GroepSelect", the page refreshes (as intended), but resets it's SelectedIndex to the first item.
When selecting an item in the DropDownList "VakSelect", the page refreshes, but also remembers it's SelectedIndex value.
It's doing this behavior consistently, yet I am unable to discover what I do wrong.
My Code:
In my HTML code, I have two DropDownList Controls.
<div>
<asp:DropDownList runat="server" ID="GroepSelect" AutoPostBack="true" AppendDataBoundItems="true" />
<asp:DropDownList runat="server" ID="VakSelect" AutoPostBack="true" AppendDataBoundItems="true" />
</div>
I'm populating the controls in my C# code:
protected void Page_Load(object sender, EventArgs e) {
Database db = new Database();
if (!IsPostBack) {
GroepSelect.DataSource = GenereerDummyGroepen(); // returns a List<ListItem>
GroepSelect.DataTextField = "Text";
GroepSelect.DataValueField = "Value";
GroepSelect.DataBind();
GroepSelect.SelectedValue = "1";
VakSelect.DataSource = db.GetVakken(); // returns a List<Vak>
VakSelect.DataTextField = "Omschrijving";
VakSelect.DataValueField = "Id";
VakSelect.DataBind();
VakSelect.SelectedValue = "1";
}
// Use the SelectedValue to determine which data to get out of the database
Medewerkers = db.GetMedewerkers(int.Parse(GroepSelect.SelectedValue));
Opdracht = db.GetOpdrachten(int.Parse(VakSelect.SelectedValue)).First();
Resultaten = db.GetResultaten(Opdracht.Id, int.Parse(GroepSelect.SelectedValue));
GenereerTabel();
}
As requested, my code for GenereerDummyGroepen() is the following:
private List<ListItem> GenereerDummyGroepen() {
return new List<ListItem>()
{
new ListItem("Groep 1", "1"),
new ListItem("Groep 2", "1")
};
}
Why I implemented it this way?
I try to populate a custom-made pivot table based on the content of Medewerkers, Opdracht and Resultaten. The content of those lists, depends on the selected item in the DropDownList control. The expected behavior of those controls is, that on the moment those are changed, the table should re-populate. The strategy I follow here, is that a page-postback is being processed, and using the AppendDataBoundItems=true remembers the DropDownList contents so that on the newly refreshed page I can generate the table.
My Question
I'm looking for the answer for: why is there a consistent different behavior? Is it the fact that the ListItem class differs in behavior from my custom class Vak?
Here I guess issue is with you function
GenereerDummyGroepen();
Please put your code here. In your code there is value field might have same data for all listItem. Because of that it is changing default to firstIndex as all values are same.

ASP.NET gridview - how to add dynamically populated dropdown to dynamically bound gridview

I looked around for answer about my problem already, but I gig not find anything conclusive. I Want to to the following:
I have an asp.net form with a GridView which is not bound to a data source which hence does not have pre-defined columns. I populate the gridview with data from an SQL Server dynamically:
gvComponentLocks.DataSource = getComponentsAndLocks(worksPermitID);
//Note getComponentsAndLocks encapsulates the database query and returns a DataTable
gvComponentLocks.DataBind();
Now I want to have a DropDownList in one specific column of the GridView. This DropDownList shall be populated with values dynamically (here I think the ...Item.Add is the appropriate approach).
My biggest problem is how to create the DropDownLists in the cells without being able to define them statically as asp:TemplateField in the web page's markup?
Another way to answer my question would be how to populate a statically defined GridView (with statically defined DropDownList control) dynamically with data from a data source - without the need to bind the GridView statically to a DataSource.
You can create the DropDownList dynamically in the RowDataBound event of the GridView.
protected void gvComponentLocks_RowDataBound(object sender, GridViewRowEventArgs e)
{
//check if the row is a datarow
if (e.Row.RowType == DataControlRowType.DataRow)
{
//create a new dropdownlsit
DropDownList ddl = new DropDownList();
//bind the source and define the values
ddl.DataSource = getComponentsAndLocks(worksPermitID);
ddl.DataTextField = "columnA";
ddl.DataValueField = "columnB";
ddl.DataBind();
//add the dropdownlist to the gridview in column 1
e.Row.Cells[1].Controls.Add(ddl);
}
}
The only thing you also have to do is to place the DataBBinding of the GridView outside the IsPostBack check. Otherwise you'll lose the DropDowns after a PostBack.
Assuming GridView (with statically defined DropDownList control)
<asp:DropDownList ID="DropDownList1" runat="server">
</asp:DropDownList>
In side
protected void gvComponentLocks_RowDataBound(object sender, GridViewRowEventArgs e)
if (e.Row.RowType == DataControlRowType.DataRow)
{
if ((e.Row.RowState & DataControlRowState.Edit) > 0)
{
do something like...
DropDownList DropDownList1 = (DropDownList)e.Row.FindControl("DropDownList1");
DropDownList1.DataSource=SomeDropDownList1ItemCollection
DropDownList1.Bind();
Alternatively return from a ListItemCollection, preferably...
gvComponentLocks.Items.AddRange(LoadList().Cast<ListItem>().ToArray());
where
public ListItemCollection LoadList()
{
ListItemCollection Items = new ListItemCollection();
Items.Add(new ListItem("Choose from list...", ""));
Items.Add(new ListItem("Text","Value")
Dynamic DropDownList:
Put a PlaceHolder Control in the Template in the Grid. Create a DropDownList control with appropriate ids in the code behind and add to the PlaceHolder control.
DropDownList DropDownList1= new DropDownList();
DropDownList1.ID=...
etc
YourPlaceHolderControl.Controls.Add(DropDownList1)
You will have to build back this dynamic DropDownList on postback and repopulate it.

Getting subcontrol in Repeater

I am using ASP.NET and C# on .NET 4 to develop a simple app. I have a repeater with an item template containing a few controls; one of them is a label that must be set depending on a complex calculation. I am using the OnItemDataBound event to compute the text and set the label's text in the code behind, like this:
protected void repRunResults_ItemDataBound(object sender, RepeaterItemEventArgs e)
{
//capture current context.
Repeater repRunResults = (Repeater)sender;
Label laMessage = (Label)repRunResults.Controls[0].FindControl("laMessage");
DSScatterData.RunResultsRow rRunResults = (DSScatterData.RunResultsRow)((DataRowView)(e.Item.DataItem)).Row;
//show message if needed.
int iTotal = this.GetTotal(m_eStatus, rRunResults.MaxIterations, rRunResults.TargetLimit);
if(iTotal == 100)
{
laMessage.Text = "The computed total is 100.";
}
else
{
laMessage.Text = "The computed total is NOT 100.";
}
}
The data source for my repeater contains a few rows, so I would expect that each impression of the repeater would call the event handler and show the message according to the data in the associated row. However, I only get one message, which appears on the first repeater impression but matches the data for the last row in the data source.
It seems like every time the ItemDataBound event fires, the controls that my code captures are the same ones, so that I overwrite the message on every impression of the repeater. I have stepped through the code and this is what it is apparently happening.
Any idea why? And how to fix it?
Note. My repeater is nested inside another repeater. I don't think this should be relevant, but it might be.
You are grabbing the first one. You need to use the item that is being passed in like so:
protected void repRunResults_ItemDataBound(object sender, RepeaterItemEventArgs e)
{
//capture current context.
Repeater repRunResults = (Repeater)sender;
Label laMessage = e.Item.FindControl("laMessage"); //<-- Used e.Item here
DSScatterData.RunResultsRow rRunResults = (DSScatterData.RunResultsRow)((DataRowView)(e.Item.DataItem)).Row;
//show message if needed.
int iTotal = this.GetTotal(m_eStatus, rRunResults.MaxIterations, rRunResults.TargetLimit);
if(iTotal == 100)
{
laMessage.Text = "The computed total is 100.";
}
else
{
laMessage.Text = "The computed total is NOT 100.";
}
}

Dropdownlist first value display "choose"

I want that my dropdownlist display first value: "-choose car-"
I succeed at this way:
protected void ddl1_DataBound(object sender, EventArgs e)
{
Convert.ToInt32(ddl1.SelectedValue);
ddl1.Items.Insert(0, new ListItem("-Choose car-", "-Choose car-" ));
}
and that's ok,the "-choose-" is in the first place but the problem now is that if I have values,for example,the dropdownlist show like that:
-Choose car-
Subaro
Fiat
Honda
The first value that display when I'm enter to the site is the Subaro,and to see the -choose car- the user need to open the dropdownlist and then he will see the -choose car- at the first place.How can I do that from the start,from the page load - the -choose car- will display at the ddl from the page load.Where I wrong at the code ?
I tried the itemlist with AppendDataBoundItems = "true" but I got an error, and when I succeed,the problem is the same like I said before.
You were on the right track with using the AppendDataBoundItems property, it should be set to true if you're databinding the list.
Your markup should look like this
<asp:DropDownList runat="server" ID="ddl1" AppendDataBoundItems="true">
<asp:ListItem Text="-Choose car-" />
</asp:DropDownList>
and your code behind probably already looks something like this
ddl1.DataSource = [your datasource goes here];
ddl1.DataBind();
This will place the Choose car text as the first option in the drop-down list and append the rest of the options below it.
Now for the more interesting part of why you were seeing the behavior you were seeing (first item not being selected). If you look at the implementation of SelectedIndex using a tool like JustDecompile (not affiliated with Telerik, just happen to use their tool) you'll see code that looks like this:
public int SelectedIndex
{
get
{
int num = 0;
num++;
while (num < this.Items.Count)
{
if (this.Items[num].Selected)
{
return num;
}
}
return -1;
}
set
{
// stuff you don't care about
this.ClearSelection();
if (value >= 0)
{
this.Items[value].Selected = true;
}
// more stuff you don't care about
}
}
As you can see, the index isn't stored anywhere, it's computed every time based on which item has the Selected property set to true. When you set the SelectedIndex to 0 in the markup and databind your datasource, it will select the 0th item in that list, in your case Subaro. When you insert a new item at the beginning of the list, Subaro is still marked as the selected item, which is why when the page loads, you see that selected and not Choose car. If you want to mark Choose car as the selected item using code, you will have to do it after you data databind your dropdown. Please note, this is just an implementation detail of how DropdownList works. It could change in future version of ASP.NET so do not write code that relies on it working this way.
Make sure that you bind the data source and insert you "-choose care-" item first before selected he first item
make sure when you insert your 1st item "-Choose car-" you make it once not on each PostBack. Check if not IsPostBack to add the 1st item.
EDIT:
Example:
protected void Page_Load(object sender, EventArgs e)
{
if(!IsPostBack)
{
ddl1.Items.Insert(0, new ListItem("-Choose car-", "-Choose car-" ));
}
ddl1.SelectedIndex = 0;
}
You should do ddl1.Items.Insert(0, new ListItem("-Choose car-", "-Choose car-")); first, and than ddl1.SelectedIndex = 0
private void FillCar()
{
DataTable dt = GetCar();
ddl1.Items.Clear();
ddl1.DataSource = dt;
ddl1.DataTextField = "carName"; // field name in the database
ddl1.DataValueField = "CarNum"; // field name in the database
ddl1.DataBind();
ListItem li = new ListItem();
li.Text = "--Choose car--";
li.Value = "-1";
ddl1.Items.Insert(0, li);
ddl1.SelectedIndex = 0;
}
I use method like this and call it in the page load in if (!IsPostBack){}.

Categories