In my Page Load Method, I'm filling an HTML Table with Data.
By Default, the Table is empty.
<table runat="server" id="categoriesTable">
</table>
After I create each Row dynamically, I add 4 cells to it, and I give each cell an ID to be used later.
After creating all Cells, I re-traverse this Table so I can add inside each cell a list <li></li>, The Data inside these lists are fetched from a Database, which depends on the ID given to each Cell previously.
When I refresh the page, nothing goes wrong, but on PostBack's (After clicking on some ASP Button, or changing the Selected Index of a DropDownList), the number of cells stays the same, but the list inside each cell is doubled.
Means, if I had this:
Cell1
-Da
-Do
-Di
-Du
Cell2
-Ya
-Yo
Cell3
-Ka
-Ki
I would have this after the PostBack:
Cell1
-Da
-Do
-Di
-Du
-Da
-Do
-Di
-Du
Cell2
-Ya
-Yo
-Ya
-Yo
Cell3
-Ka
-Ki
-Ka
-Ki
Here is the code, note that cCategory is a class that I created, and its methods return a List<cCategory>.
//Load Categories Into the Table
List<cCategory> mainCategoriesList = cCategory.SubCategories(null);
if(mainCategoriesList.Count!=0)
{
//Categories Available
HtmlTableRow Row = new HtmlTableRow();
categoriesTable.Rows.Add(Row);
HtmlTableCell Cell;
int cellCounter = 0;
while(cellCounter<mainCategoriesList.Count)
{
if (cellCounter % 4 == 0 && cellCounter!=0)
{
//Add a New Row
Row = new HtmlTableRow();
categoriesTable.Rows.Add(Row);
}
Cell = new HtmlTableCell();
Cell.InnerHtml = "" + mainCategoriesList.ElementAt(cellCounter).Category()+ "";
Cell.ID = mainCategoriesList.ElementAt(cellCounter).CategoryID();
Row.Cells.Add(Cell);
cellCounter++;
}
//Now we must add the sub categories
String subList = "";
String newContents;
int counter;
List<cCategory> subCategoriesList;
for (int i = 0; i < categoriesTable.Rows.Count; i++)
{
//For each Row, go through each Cell
for (int j = 0; j < categoriesTable.Rows[i].Cells.Count; j++)
{
//For Each CELL, get the subCategories
subCategoriesList = cCategory.SubCategories(categoriesTable.Rows[i].Cells[j].ID);
counter = 0;
while (counter < subCategoriesList.Count)
{
subList = subList + "<li>" + subCategoriesList.ElementAt(counter).Category() + "</li>";
counter++;
}
newContents = "<div class=\"subCategoriesList\"><ul>" + subList + "</ul></div>";
subList = "";
categoriesTable.Rows[i].Cells[j].InnerHtml = categoriesTable.Rows[i].Cells[j].InnerHtml + newContents;
}
}
}
Why is the Cell data getting doubled?
Do you have an IsPostBack() check in your Page_Load method where you are loading the table? I'm guessing not and that the rows are doubling because they are getting posted back via ViewState and you are adding them again via the above code.
I think you should clean up [or remove the data of] the categoriesTable table first, then you do your logic.
Related
I created two HtmlTable(s) in my code-behind whereby each have their own respective HtmlTableRow(s). After I create these, I need to add them to a <table name="teamoptionstable"> element in my markup in a specific order. But while I'm looping to add each HtmlTableRow to my teamoptionstable, the HtmlTable seems to delete the rows as I'm iterating. Here is the code:
HtmlTable gaTable = new HtmlTable();
HtmlTable supportTable = new HtmlTable();
HtmlTableRow gaHeaderRow = new HtmlTableRow();
gaHeaderRow.Cells.Add(new HtmlTableCell {
InnerHtml = "<tr id='gaHeaderLeft'><th colspan='3'>G&A</th></tr>"
});
gaTable.Rows.Add(gaHeaderRow);
HtmlTableRow supportHeaderRow = new HtmlTableRow();
supportHeaderRow.Cells.Add(new HtmlTableCell {
InnerHtml = "<tr id='supportHeaderLeft'><th colspan='3'>Support</th></tr>"
});
supportTable.Rows.Add(supportHeaderRow);
using(DataTable departmentList = Database.ObjectList_Get(Database.ObjectType.Department)) {
foreach(DataRow department in departmentList.Rows) {
int departmentId = Convert.ToInt32(department["department_id"]);
string departmentName = department["name"].ToString();
HtmlTableRow data = new HtmlTableRow();
if (...) {
data.Cells.Add(...);
data.Cells.Add(...);
data.Cells.Add(...);
gaTable.Rows.Add(data);
} else if (...) {
data.Cells.Add(...);
data.Cells.Add(...);
data.Cells.Add(...);
supportTable.Rows.Add(data);
}
}
}
for (int i = 0; i < gaTable.Rows.Count; i++) {
teamoptionstable.Rows.Add(gaTable.Rows[i]);
}
for (int i = 0; i < supportTable.Rows.Count; i++) {
teamoptionstable.Rows.Add(supportTable.Rows[i]);
}
The problem is at the end in my for loop. As it iterates the first time, for some reason, the gaTable.Rows.Count changes, it reduces by one. So I end up adding less rows to teamoptionstable. Here is the markup for the teamoptionstable:
<table class="table table-condensed table-striped" id="teamoptionstable" name="teamoptionstable" runat="server">
<thead>
<tr>
<th>Team</th>
<th>Primary Rule</th>
<th>Secondary Rule</th>
</tr>
</thead>
<tbody>
</tbody>
</table>
This is because HtmlTableRow is a reference type. So when you add the reference to the row to another table, you are therefore removing the reference from the first table. This is similar to pointers in other languages.
Because of this, when you loop from 0 and up, you are only going through half of your rows.
Example. Here is your row collection:
Table 1: [0][1][2][3][4][5]
You take index 0 and add it to your new table. You are now left with this, since you took a reference from your first table. Notice how it shifts. You don't have indexes 1-5, you have indexes 0-4.
Table 1: [0][1][2][3][4]
Table 2: [0]
Then your loop increases more and you eventually reach this point:
Table 1: [0][1][2]
Table 2: [0][1][2]
Now when you get to index 3, there are no more. If you don't care about order, one thing you could do instead is iterate downwards, starting at the row count and working towards 0.
for (int i = gaTable.Rows.Count - 1; i >= 0; i--)
{
teamoptionstable.Rows.Add(gaTable.Rows[i]);
}
This will allow you to take from the end of the first table and account for the shift in indexes. This will obviously leave you with a reversed table, however.
If you do care about order, you'll need to recreate all the rows and cells new, then add them to your second table. Here is a great answer by Daniel Dyson with a method to do just that. I made some modifications to it. This is untested, so be aware of that.
protected HtmlTable CopyTable(HtmlTable copyFromTable)
{
if (copyFromTable != null && copyFromTable.Rows.Count > 0)
{
var copyToTable = new HtmlTable();
HtmlTableRow copyRow;
HtmlTableCell copyCell;
for (int i = 0; i < copyFromTable.Rows.Count - 1; i++)
{
copyRow = new HtmlTableRow();
for (int j = 0; j < copyFromTable.Rows[i].Cells.Count - 1; j++)
{
copyCell = new HtmlTableCell();
copyCell.InnerHtml = copyFromTable.Rows[i].Cells[j].InnerHtml;
copyRow.Cells.Add(copyCell);
}
copyToTable.Rows.Add(copyRow);
}
return copyToTable;
}
return null;
}
I have a data table in a data grid view. I want to put an listbox/dropdown menu in a specific column and row. I have tried this but it doesn't work --
var column = new DataGridViewComboBoxColumn();
RunTimeCreatedDataGridView[1, 1].Value = RunTimeCreatedDataGridView.Columns.Add(column);
Here is how I populate the table--
public DataTable createGridForForm(int rows, int columns)
{
// Create the output table.
DataTable table = new DataTable();
for (int i = 1; i <= columns; i++)
{
table.Columns.Add("column " + i.ToString());
}
for (int i = 1; i < rows; i++)
{
DataRow dr = table.NewRow();
// populate data row with values here
ListBox test = new ListBox();
myTabPage.Controls.Add(test);
table.Rows.Add(dr);
}
return table;
}
And here is how I create the datagridview.
private void createGridInForm(int rows, int columns)
{
DataGridView RunTimeCreatedDataGridView = new DataGridView();
RunTimeCreatedDataGridView.DataSource = createGridForForm(rows, columns);
//DataGridViewColumn ID_Column = RunTimeCreatedDataGridView.Columns[0];
//ID_Column.Width = 200;
int positionForTable = getLocationForTable();
RunTimeCreatedDataGridView.BackgroundColor = Color.WhiteSmoke;
RunTimeCreatedDataGridView.Size = new Size(995, 200);
RunTimeCreatedDataGridView.Location = new Point(5, positionForTable);
myTabPage.Controls.Add(RunTimeCreatedDataGridView);
}
You were right on track with your first code block... the problem was you were trying to add a column to a cell reference. If you want to add a column where all rows have a drop down, then do exactly what you were doing, but simply add the column instead of specifying a cell value, like this:
var column = new DataGridViewComboBoxColumn();
RunTimeCreatedDataGridView.Columns.Add(column);
Then, specify the datasource as you normally would for a combobox.
Alternatively, if you want a specific cell to have a different combobox, or only a single cell in the column to have one, you can create a separate one as shown here or here.
Edit: To add a combobox to a specific cell in the DataGridView, you would do something like this:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
DataGridView dgv = new DataGridView();
// add some columns and rows
for (int i = 0; i < 10; i++)
{
DataGridViewCell c = new DataGridViewHeaderCell();
dgv.Columns.Add("Column" + i, Convert.ToString(i));
}
for (int i = 0; i < 10; i++)
{
dgv.Rows.Add(new DataGridViewRow());
}
//create a new DataGridViewComboBoxCell and give it a datasource
var DGVComboBox = new DataGridViewComboBoxCell();
DGVComboBox.DataSource = new List<string> {"one", "two", "three"};
DGVComboBox.Value = "one"; // set default value of the combobox
// add it to cell[4,4] of the DataGridView
dgv[4, 4] = DGVComboBox;
// add the DataGridView to the form
this.Controls.Add(dgv);
}
}
I have a htmltable. Each of the cell of the table is identified by an ID, for example 0001 and so on. The table has not a fixed dimension, but dynamic, so there can be 20 or more cells depending on how much values are stored in the db. I would like to change the backgorund color of the textbox of a specific cell. But don't know how to access the cell.
I know this syntax:
// the whole background becomes green
myTable.BgColor = "#008000";
// I see no changes
myTable.Rows[x].Column[y].BgColor = "#008000";
// I need a syntax like this
myTable.Cell(Id_cell).BgColor = "#008000";
Try this:
.aspx:
<asp:Table ID="table" runat="server" />
C# Code:
TableRow row = new TableRow();
TableCell cell = new TableCell();
cell.Text = "Testing";
cell.BackColor = System.Drawing.Color.Red;
row.Cells.Add(cell);
table.Rows.Add(row);
table.Rows[0].Cells[0].BackColor = System.Drawing.Color.Pink;
You can set the background colour of an individual cell like this:
myTable.Rows[0].Cells[1].BgColor = "#553322";
Where 0 is the row number you want, in this case the first row and 1 is the cell number you want in this case the 2nd cell as indexes are based from 0. This has to be done before the table is rendered e.g. on page load.
You could generalise this to a method to set the cell colour for an id like so:
private void SetColorByCellId(HtmlTable table, string id, string color)
{
for (int i = 0; i < table.Rows.Count; i++)
{
for (int j = 0; j < table.Rows[i].Cells.Count; j++)
{
if (table.Rows[i].Cells[j].ID == id)
{
table.Rows[i].Cells[j].BgColor = color;
}
}
}
}
Then you would call it like this:
SetColorByCellId(myTable, "0001", "#553322");
I am writing a web site in Visual Studio, something like an on-line library. I have a GridView on the first page that presents all of the books available from the data source and some other properties also contained in the data source. The GridView contains check boxes and the user can choose which books he wants to order by checking a box. My question is how can I use the data in the selected rows, the list of books with their properties and show that list on another page, so that the user is able to know which items he has selected?
I tried with a for loop on the FirstPage:
for (int i = 0; i < GridView1.Rows.Count; i++)
{
int bookID = (int)GridView1.DataKeys[i][0];
CheckBox cb = (CheckBox)GridView1.Rows[i].FindControl("CheckBox");
if (cb.Checked)
{
purchaseProductList.Add(bookID);
Response.Redirect("SecondPage.aspx?bookID" + i + "=" + bookID);
}
}
and then on the SecondPage:
for (int i = 0; i < 10; i++)
{
if (Request.QueryString["bookID" + i] != null)
{
DataRow row;
row = dtBooks.NewRow();
row["ID"] = Request.QueryString["bookID" + i];
dtBooks.Rows.Add(row);
}
}
GridView1.DataSource = dtBooks;
GridView1.DataBind();
but it didn't work. Any help? Thank you in advance.
you can use session to keep selected ids
List<string> ids = new List<string>();
for (int i = 0; i < GridView1.Rows.Count; i++)
{
int bookID = (int)GridView1.DataKeys[i][0];
CheckBox cb = (CheckBox)GridView1.Rows[i].FindControl("CheckBox");
if (cb.Checked)
{
purchaseProductList.Add(bookID);
ids.Add(bookID);
}
}
Session["Ids"] = ids;
Response.Redirect("SecondPage.aspx");
from second page you can access session and load those ids to grid
var list = (List<string>)Session["Ids"];
foreach (string id in list)
{
DataRow row;
row = dtBooks.NewRow();
row["ID"] = Request.QueryString["bookID" + id];
dtBooks.Rows.Add(row);
}
GridView1.DataSource = dtBooks;
GridView1.DataBind();
I am having a gridview that will have 2 rows with some data as follows
101 111111111 1111111111009270754A094101// My first row data
9000002 1000000020222222236000000000000000000000012000000000000000000000000000000000000000//My 2nd row data
I will add some values such that it should be inserted between these 2 rows and that 2nd row already exists should be move to the last row of the datagrid view. Like that if i add n number of values the values should be inserted in between those 2 and that row which was already exists should be moved to the last row any idea please
I have editted this with tested code:
public Form1()
{
InitializeComponent();
//This can be removed before utilizing
dgv.Rows.Add("1", "1", "1");
dgv.Rows.Add("1", "1", "1");
dgv.Rows.Add("bob", "bob", "bob");
dgv.Rows.Add("1", "1", "1");
dgv.Rows.Add("1", "1", "1");
dgv.Rows.Add("1", "1", "1");
//This can be removed before utilizing
int oldrow = 2;
dgv.Rows.Add(itemArray(dgv.Rows[oldrow]));
dgv.Rows.RemoveAt(oldrow);
/*
DataGridViewRow oldRow = dataGridView1.Rows.Add(itemarray(dataGridView1.Rows[1])); dataGridView1.Rows.Remove(oldRow)
*/
}
object[] itemArray(DataGridViewRow Row)
{
int a = Row.DataGridView.ColumnCount - 1;
object[] mOut = new object[a+1];
for (int x = 0;x <= a ; x++)
{
mOut[x] = Row.Cells[x].Value;
}
return mOut;
}
I apologize for all of the additional testing.
This was what i written but does not work for me. What i need is if my array starts with 5 i would like to remove the 2nd row which was already exists in gridview and would like to append it after the particular row added
if (line.StartsWith("5"))
{
int oldRow = 1;
dataGridView1.Rows.Add(itemarray(dataGridView1.Rows[1]));
dataGridView1.Rows.RemoveAt(oldRow);
dataGridView1.Rows.Add("BatchHeader", line);
m_flag = true;
StringBuilder sb = new StringBuilder();
objfileentry.createFileEntry(Append.FileName, out sb);
if (m_flag)
dataGridView1.Rows.Add("FileControl", sb.ToString());
line = string.Empty;
}
The given function by you
private object[] itemarray(DataGridViewRow Row)
{
int a = Row.DataGridView.ColumnCount - 1;
object[] mOut = new object[a + 1];
for (int x = 0; x <= a; x++)
{
mOut[x] = Row.Cells[x].Value;
}
return mOut;
}
If you are binding data in this way,
myGridView.DataSource = GetDataSource();
You can't add rows programmatically. If not, you can use:
DataGridViewRow newRow = new DataGridViewRow();
//set row data here
myGridView.Rows.Insert(newIndex, newRow); //use Insert instead of Add/AddCopy
Can you use ListView instead of datagrid?