Why are components of my custom control not initiated? - c#

I'm making a GenericTable as a custom implementation of GridView that will display the values of any list of objects that's inserted.
To use the control on an aspx page it needs to be a UserControl, so the GridView is included as a component in the GenericTable:
<%# Control Language="C#" AutoEventWireup="true" CodeBehind="GenericTable.ascx.cs" Inherits="CASH.WebApplication.Controls.GenericTable" %>
<div style="width: 100%; overflow: scroll">
<asp:GridView ID="grid" runat="server"></asp:GridView>
</div>
This works fine for the first use of my control, it's added on the aspx page. It seems that doing that adds some sort of magic that initiates the control components.
When the user clicks on an item that has properties of it's own, the GenericTable should insert a row below the current row and spawn a new GenericTable that will show said properties. table is the DataTable that I use to set the GridView contents:
var data = table.NewRow();
var child = new GenericTable();
data[0] = child;
table.Rows.InsertAt(data, row);
grid.DataSource = table;
grid.DataBind(); // The extra row is displayed now, initialize components in the aspx code?
child.MakeTable(); // Throws exception because it's `grid` property is null.
When I try to activate the newly made GenericTable, after this code, it's grid is null.
Is there a way to initialize the same magic that happens when this control is located in the aspx code?
Update: Maybe the problem lies in how the table is stored between postbacks, currently I'm using the session, maybe there's a better way to remember user input?
The whole GenericTable code:
using Project.DomainModel.Models;
using System;
using System.Collections.Generic;
using System.Data;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Web.UI;
using System.Web.UI.WebControls;
namespace CASH.WebApplication.Controls
{
public partial class GenericTable : UserControl
{
private PropertyInfo[] properties;
//private GridView gridView;
private DataTable table = new DataTable();
private Dictionary<int, int> ingedrukt = new Dictionary<int, int>();
protected void Page_Init(object sender, EventArgs e)
{
grid.RowCommand += WeergaveDossiers_RowCommand;
}
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
for (int i = 0; i < grid.Rows.Count; i++)
{
grid.Rows[i].Cells[0].ColumnSpan = 0;
}
}
else
{
properties = (PropertyInfo[])Session["properties"];
table = (DataTable)Session["table"];
ingedrukt = (Dictionary<int, int>)Session["ingedrukt"];
foreach (var knop in ingedrukt)
{
DetailRijToevoegen(knop.Key, knop.Value);
}
}
grid.DataBind();
}
protected void SaveInSession()
{
Session["properties"] = properties;
Session["table"] = table;
Session["ingedrukt"] = ingedrukt;
}
protected void WeergaveDossiers_RowCommand(object sender, GridViewCommandEventArgs e)
{
int row = int.Parse((string)e.CommandArgument) + 1;
int col = GetKolomIndex(e.CommandName) + 1;
if (ingedrukt.ContainsKey(row))
{
if (ingedrukt[row] != col)
{
//DetailRijVerwijderen(row);
//ingedrukt.Remove(row);
//ingedrukt[row] = col;
}
}
else
{
ingedrukt[row] = col;
}
//DetailRijToevoegen(row, col);
SaveInSession();
}
protected void DetailRijToevoegen(int row, int col)
{
var data = table.NewRow();
var child = new GenericTable();
child.grid = new GridView();
data[0] = child;
table.Rows.InsertAt(data, row);
grid.DataSource = table;
grid.DataBind();
var cells = grid.Rows[row].Cells;
// Only keep the first cell
while (cells.Count > 1)
{
cells.RemoveAt(1);
}
child.MaakTable(new List<object>() { table.Rows[row][col] });
grid.Columns[0].Visible = true;
grid.Rows[row].Cells[0].ColumnSpan = table.Columns.Count;
}
protected void DetailRijVerwijderen(int row)
{
}
protected int GetKolomIndex(string naam)
{
for (int i = 0; i < properties.Length; i++)
{
if (properties[i].Name == naam)
{
return i;
}
}
throw new InvalidDataException("Kolom naam " + naam + " niet bekend");
}
public void MaakTable(IEnumerable<object> data)
{
properties = data.First().GetType().GetProperties().Where(p => p.CanRead).ToArray();
grid.AutoGenerateColumns = false;
var details = new BoundField();
details.DataField = "Details";
grid.Columns.Add(details);
table.Columns.Add(new DataColumn("Details", typeof(object)));
foreach (var veld in properties)
{
table.Columns.Add(new DataColumn(veld.Name, (veld.Name == "Id" ? typeof(object) : veld.PropertyType)));
grid.Columns.Add(MaakKolom(veld));
}
foreach (var entry in data)
{
var row = table.NewRow();
int col = 0;
foreach (var veld in properties)
{
row[++col] = veld.GetValue(entry);
}
table.Rows.Add(row);
}
grid.DataSource = table;
SaveInSession();
}
protected DataControlField MaakKolom(PropertyInfo veld)
{
DataControlField field;
if (typeof(Entity).IsAssignableFrom(veld.PropertyType))
{
field = new ButtonField();
((ButtonField)field).DataTextField = veld.Name;
((ButtonField)field).ButtonType = ButtonType.Button;
((ButtonField)field).CommandName = veld.Name;
}
else if (veld.PropertyType == typeof(bool))
{
field = new CheckBoxField();
((CheckBoxField)field).DataField = veld.Name;
}
else if (veld.PropertyType.IsEnum)
{
field = new TemplateField();
//((TemplateField)field).ItemTemplate = (ITemplate)new Label()
//{
// Text = "#DataBinder.Eval(\"" + veld.Name + "\")",
//};
}
else if (veld.PropertyType == typeof(DateTime))
{
field = new TemplateField();
//field.DatePicker = true;
}
else
{
field = new BoundField();
((BoundField)field).DataField = veld.Name;
}
field.HeaderText = veld.Name;
return field;
}
protected void OnRowDataBound(object sender, GridViewRowEventArgs e)
{
if (e.Row.RowType == DataControlRowType.DataRow)
{
}
}
}
}

I'm not exactly sure what you're trying to acheive, I just know that what you're doing is fundamentally wrong (well, in terms of the ASP.NET view of the world...).
You're adding your control to the input data, rather than adding it as a sub-control of the grid.
You're not instantiating the GridView control in the consructor of your control.
Session is absolutely the wrong place to store lots of page-related data.
I'll start with the last point first: If this is data that needs to persist between visits to the site, then you must put it into a database. If this is data that only exists from the time someone logs in, to the time they logout, then yes, Session may be the right place for it. Otherwise, if it's specific to the page and should be discarded when the user visits a different one, then you should either reload it from the database every time or maybe store it in the ViewState.
Next, are all the objects of the same type / do they have the same fields? If so, then the default behaviour (controlled explicitly by AutoGenerateColumns will do the job for you, no extra work required:
<asp:GridView runat="server"
ID="grid"
AutoGenerateColumns="true" />
If the do not have the same columns, then they should be in seperate grids; a GridView is a way of creating an HTTP <table> element. A table element should only contain related data; you wouldn't use a single table for displaying prices of fish and colours of cars. It follows from this, that if you have different tables, with unrelated data, then you should have different data sources... a more simple solution that means you don't need to implement the control you're trying to implement.
Finally, for completeness, when you define a control, you're just creating a class. If you want to be able to instantiate a control in the way you are trying, then you need to make sure that all its data members are a instantiated in the constructor or that any references are guarded by null-reference checks:
if (grid != null)
{
// Do stuff with grid
}

Try using the Static datatable option and update the datatable first and rebind it to the grid without loosing data.

I'm not seeing how your gridview "grid" is connected to your "child" custom control. It won't have the grid property if your don't have them linked together?
You could be doing this behind the scenes in something in your custom control but that definately needs to be done.
My logic goes: Something you think is defined is null. Did you define it?

I believe what you need to achieve can simply be achieved by creating a class inheriting from the grid view. That way you will get all the features for Grid View and your additional properties without worrying about the implementation details for custom control.
If you still think you need to create a custom control then, post the code for your custom control that will help understand the issue.
If you do not wish to post the code for some reason then visit this link where I answered a question related custom control only. I think you would be able to find a solution to your problem there.
Hope this helps.

Related

Empty Datagridview cell with bound datasourse

I have a DataGridView with "Type" and "Value" columns. The user selects a data type, and then enters a value compatible with that type, some types (e.g. "Text") accept any string. Other types (e.g. "Yes/No") are restricted to a list of possible values. For each row, I set the cell in the "Value" column to be either a Textbox for freeform types, or a combobox for list types. The DataGridView is bound to a DataTable.
The problem comes in if the user enters a value for one type, but then switches the row to a different type for which the current value is not allowed. No matter what I try, I cannot clear the Value cell. Then when I assign a combobox to the cell, I get a DataError, because the current value of the cell is incompatible. How can I clear the value before changing the cell from a textbox to a combobox?
public enum DataType
{
Text,
YesNo
}
public class IndexedItem
{
public string Name { get; set; }
public int ID {get; set; }
public string strID
{
get { return ID.ToString(); }
}
//constructors & other methods;
}
public static DataTable ParameterTable;
public List<IndexedItem> YesNoList;
In the form constructor (dgvInputs is the DataGridView):
ParameterTable = new DataTable("ParameterTable");
ParameterTable.Columns.Add("Type", typeof(DataType));
ParameterTable.Columns.Add("Value", typeof(string));
YesNoList = new List<IndexedItem>();
YesNoList.Add(new IndexedItem("Yes", 1));
YesNoList.Add(new IndexedItem("No", 0));
var D = (DataGridViewComboBoxColumn)dgvInputs.Columns[0];
D.ValueMember = "Value";
D.DisplayMember = "Display";
D.DataSource = new DataType[] {
DataType.Text,
DataType.YesNo
}.Select(x => new { Display = x.ToString(), Value = (int)x }).ToList();
BindingSource ParamSource = new BindingSource();
ParamSource.DataSource = ParameterTable;
dgvInputs.AutoGenerateColumns = false;
dgvInputs.DataSource = ParamSource;
dgvInputs.Columns[0].DataPropertyName = "Type";
dgvInputs.Columns[1].DataPropertyName = "Value";
And Events:
private void dgvInputs_CurrentCellDirtyStateChanged(object sender, EventArgs e) {
if (dgvInputs.IsCurrentCellDirty) {
dgvInputs.CommitEdit(DataGridViewDataErrorContexts.Commit);
}
}
private void dgvInputs_CellValueChanged(object sender, DataGridViewCellEventArgs e) {
if (e.RowIndex >= 0 && e.ColumnIndex == 0) {
var cb = (DataGridViewComboBoxCell)dgvInputs[0, e.RowIndex];
if (cb.Value != null && cb.Value != DBNull.Value) {
DataType Type = (DataType)cb.Value;
dgvInputs[1, e.RowIndex].Value = string.Empty;
dgvInputs.CommitEdit(DataGridViewDataErrorContexts.Commit);
switch (Type) {
case DataType.YesNo:
dgvInputs[1, e.RowIndex].Dispose();
var newBox = new DataGridViewComboBoxCell();
newBox.DisplayMember = "Name";
newBox.ValueMember = "strID";
newBox.DataSource = YesNoList;
dgvInputs[1, e.RowIndex] = newBox;
break;
default:
dgvInputs[1, e.RowIndex] = new DataGridViewTextBoxCell();
break;
}
}
}
}
If you have it set to "text" and enter something arbitrary, then switch to "YesNo", it gives an error "System.ArgumentException: DataGridViewComboBoxCell value is not valid.", that will reappear any time the cursor is over the cell. Changing it back to a text row causes the original value to reappear.
I am assuming that the problem is that the value is saved in ParameterTable, but I can't get it to propagate my clearing of the original value to ParameterTable. I've tried null and DBNull.Value instead of string.Empty, but neither one made any difference. I added the "CommitEdit" line in hopes of getting it to make the change, but that made no difference either.
Edit:
As it turns out, the problem was this code that I had in the cell change event:
string Default = dgvInputs[4, e.RowIndex].Value as string;
// code switching out text box and combo box above
try
{
dgvInputs[4, e.RowIndex].Value = Default;
} catch (Exception e2) {
MessageBox.Show(e2.GetType().ToString());
}
The idea had been to preserve the value if possible, and I had the messagebox to show me the specific exception I needed to catch, as I was not sure. But apparently this assignment does not immediately induce the exception. That only occurs later, apparently during some other event I am not handling.
It is obvious in hindsight that I should have included this code in the sample. I have no idea now how I overlooked it. My apologies to everybody I led on a wild goose chase by leaving out the critical information. I do appreciate all of your assistance.
You problem is not with clearing the value but with the YesNoList.
The grid compbobox tries to find the value for the current record and there is no empty neither null value in your YesNoList.
You will even get an error if you try to add a new record and first set the DataType without setting the Value.
You can solve this by either adding an empty item to your YesNoList or by setting a default value to the existing record when switching DataType.
Edit:
I know below does not specifically answer your question as it states, but the example may help you out. Consider having two controllers in one cell.
Original
I am not sure if this will help you or not, but I tried to make a very basic program that you discussed. A dataset is created with 2 entries. The first column is the DataType, the second is of Value. If the DataType Text is chosen, the Value cell turns into a Textbox. If the DataType Yes/No is chosen, it hides the Textbox and shows a DropDownList. The idea is to hide one component when not needed.
Default.aspx.cs
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
namespace DataGridViewBounds
{
public partial class _Default : Page
{
public enum DataType
{
Text,
YesNo
}
public class IndexedItem
{
public string Name
{ get; set; }
public int ID
{ get; set; }
public string strID
{ get { return ID.ToString(); } }
}
protected void Page_Load (object sender, EventArgs e)
{
if (!IsPostBack)
{
Bind();
}
for (int i = 0; i < dg.Items.Count; ++i)
{
bool ShowText = ((DropDownList)dg.Items[i].Cells[0].Controls[1]).SelectedValue.Equals("text");
((DropDownList)dg.Items[i].Cells[1].Controls[1]).Visible = !ShowText;
((TextBox)dg.Items[i].Cells[1].Controls[3]).Visible = ShowText;
}
}
private void Bind ()
{
DataTable ParameterTable = new DataTable("ParameterTable");
ParameterTable.Columns.Add("", typeof(string));
ParameterTable.Columns.Add("Type", typeof(DataType));
ParameterTable.Columns.Add("Value", typeof(string));
List<ListItem> YesNoList = new List<ListItem>(); // Should be ListItem, not IndexedItem
YesNoList.Add(new ListItem("Yes", "1"));
YesNoList.Add(new ListItem("No", "0"));
DataRow row = ParameterTable.NewRow();
row["Type"] = DataType.Text;
row["Value"] = "Some text";
DataRow row2 = ParameterTable.NewRow();
ParameterTable.Rows.Add(row);
row2["Type"] = DataType.YesNo;
row2["Value"] = "false";
ParameterTable.Rows.Add(row2);
dg.DataSource = ParameterTable;
dg.DataBind();
dg.ShowHeader = true;
dg.Visible = true;
for (int i = 0; i < dg.Items.Count; ++i)
{ // Showing 2 ways to bind the DropDownList items
((DropDownList)dg.Items[i].Cells[0].Controls[1]).Items.Add(new ListItem("Text", "text"));
((DropDownList)dg.Items[i].Cells[0].Controls[1]).Items.Add(new ListItem("Yes/No", "bool"));
((DropDownList)dg.Items[i].Cells[1].Controls[1]).DataSource = YesNoList;
((DropDownList)dg.Items[i].Cells[1].Controls[1]).DataBind();
}
}
}
}
And the Default.aspx page
<%# Page Title="Home Page" Language="C#" MasterPageFile="~/Site.Master" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="DataGridViewBounds._Default" %>
<asp:Content ID="BodyContent" ContentPlaceHolderID="MainContent" runat="server">
<asp:DataGrid ID="dg" runat="server" AutoGenerateColumns="false">
<Columns>
<asp:TemplateColumn HeaderText="Type">
<ItemTemplate>
<asp:DropDownList runat="server" ID="ddListType" AutoPostBack="true"></asp:DropDownList>
<asp:Label id="TypeLabel" runat="server" Visible="false"></asp:Label>
</ItemTemplate>
</asp:TemplateColumn>
<asp:TemplateColumn HeaderText="Value">
<ItemTemplate>
<asp:DropDownList runat="server" ID="ddListValue" AutoPostBack="true" Visible="false"></asp:DropDownList>
<asp:TextBox id="ValueLabel" runat="server" Visible="false"></asp:TextBox>
</ItemTemplate>
</asp:TemplateColumn>
</Columns>
</asp:DataGrid>
</asp:Content>
This is the best I could do at the moment without seeing more of the code, but may you can use it. One suggestion, dgvInputs_CurrentCellDirtyStateChanged appears to commit code. I am assuming this is SQL code. You may want to wait until committing until a final 'Submit' button or 'Accept Changes' button has been pressed so that you don't have to call SQL so much, but also if there is an error that occurs between the start of the first SQL call and the last. If an interruption occurs between the two, you may not necessarily want to commit.

Retain table and checked checkboxes after postback

In my webpage, I have Calendar, Table and button.
After selecting date, it will fire the databind() method of table. There are checkboxes with autopostback =true. Once checked, the Table disappears. I have no idea on how to retain the table with the checked checkboxes after post back.
protected void Page_Load(object sender, EventArgs e)
{
if (Request.QueryString.Get("Id") != null)
{
if (!IsPostBack)
{
Calendar1.Visible = false;
}
}
}
protected void Calendar1_SelectionChanged(object sender, EventArgs e)
{
Label1.Text = Calendar1.SelectedDate.ToShortDateString();
//Set datasource = (cal.selectedDate), the invoking override
// DataBind() method to create table
}
Calendar1.Visible = false;
}
I've tried to databind the table again else (IsPostBack) but i wasn't able to achieve my goals, instead, it created another table on top of the existing table
This is the method to create Table with checkboxes
public override void DataBind()
{
TableRow myTableRow = default(TableRow);
TableCell myTableCell = default(TableCell);
if (source != null && !(mDate == DateTime.MinValue))
{
for (int i = 0; i <= 23; i++)
{
foreach (DataRow row in source.Tables["Object"].Rows)
{
myTableCell = new TableCell();
CheckBox cb = new CheckBox();
cb.AutoPostBack = true;
cb.Attributes.Add("id", row["objid"].ToString());
cb.InputAttributes.Add("rowID", mDate.Date.AddHours(i).ToString());
myTableCell.Controls.Add(cb);
myTableCell.HorizontalAlign = HorizontalAlign.Center;
myTableRow.Cells.Add(myTableCell);
TimeSheetTable.Rows.Add(myTableRow);
}
}
}
else
{
throw new ArgumentException(" Invalid Date.");
}
}
Dynamically generated tables need to be regenerated on every postback. For subsequent postbacks, viewstate will be reloaded, but you have to recreate the table, cells, and controls in the same exact fashion, otherwise web forms complains about it. You need to do this during Init I believe; if checkbox checked status changed, the web forms framework will update the Checked property after load, so that will be taken care of.
I usually use a repeater or listview control as dynamic controls can be painful and the ListView is pretty flexible. Databinding takes care of rebuilding the control tree for you.

GridView change headers text

Complexity is that grid should have AutoGenerateColumns = true. I cannot use TemplateField etc, so no aspx, only .cs file manipulations are allowed. Requirements:
Grid should be autogenerated
Sorting and paging are allowed
I dunno when Asp.Net fills a header row, but he do it deep inside, because when PreRender, RowDataBound etc rise, header row still be empty. If I rename it, it works, but in this case Asp.Net renders it as plain text. Okay, i'm hardcoding postback url and retrying, but no success, this code
private void FillHeaders()
{
const string linkText = #"{1}";
bool a = true;
if (a)
for (int i = 0; i < LanitAudit.Properties.Length; i++)
{
AuditGridView.HeaderRow.Cells[i].Text = string.Format(linkText, LanitAudit.Properties[i].Key, LanitAudit.Properties[i].Value);
}
}
}
rises an exception:
Invalid postback or callback argument. Event validation is enabled using in configuration or <%# Page EnableEventValidation="true" %> in a page. For security purposes, this feature verifies that arguments to postback or callback events originate from the server control that originally rendered them. If the data is valid and expected, use the ClientScriptManager.RegisterForEventValidation method in order to register the postback or callback data for validation.
I still hope that i can not use client-side JS.
Tnx for answers, but probably my question is malformed. Yes, I can replace header text, BUT after it I cannot sort gridview (see 2nd requirement). Screen is below. As u see, I cannot click New Header Text, it's plain text. But if I try to use __doPostBack myself, i get an error you can see above.
Try this, below is changing for 1st column og grid on RowDataBound event of grid.
protected void gv_RowDataBound(object sender, GridViewRowEventArgs e)
{
if (e.Row.RowType == System.Web.UI.WebControls.DataControlRowType.Header)
{
e.Row.Cells[0].Text = "test";
}
}
See changes below, it may be what you are trying to do.
protected void gv_RowDataBound(object sender, GridViewRowEventArgs e)
{
if (e.Row.RowType == System.Web.UI.WebControls.DataControlRowType.Header)
{
//e.Row.Cells[0].Text = "test";
LinkButton lnk1 = e.Row.Cells[0].Controls[0] as LinkButton;
lnk1.Text = "test";
}
}
If you wait until the GridView is databound, you can just access the HeaderRow property to make whatever changes you need:
protected void AuditGridView_DataBound(object sender, EventArgs e)
{
AuditGridView.HeaderRow.Cells[0].Text = "New Header Text";
}
You have to wait until at least the DataBound event, because the content of the GridView hasn't been finalized before that point. See this note from the HeaderRow docs on MSDN:
Any modification to the HeaderRow property must be performed after the
GridView control has been rendered; otherwise, the GridView control
will overwrite any changes.
Note: gmd's answer also works, since he's waiting until at least the HeaderRow is rendered before making changes to it
If this is breaking the link, just change the part of the text that you need to change by parsing the values. It's kind of tedious, but if that's what you're stuck doing, this is how to do it:
protected void AuditGridView_DataBound(object sender, EventArgs e)
{
string newLinkText = "New Header Text";
string headerText = AuditGridView.HeaderRow.Cells[0].Text;
string linkText = ExtractLinkTextFromHeader(headerText);
AuditGridView.HeaderRow.Cells[0].Text = headerText.Replace(linkText, newLinkText);
}
private string ExtractLinkTextFromHeader(string headerText)
{
int linkStartIndex = headerText.IndexOf('<');
int linkEndIndex = headerText.IndexOf('>', linkStartIndex);
return headerText.Substring(linkStartIndex, linkEndIndex - linkStartIndex);
}
I found that the single way to change HeaderRow is manipulate Column.HeaderText property (using GridView.Columns collection). GridView.HeaderRow is useless at all. This is why i decided to abandon column autogeneration. So - why not write it myself? This code worked for me:
public override void DataBind()
{
if (AuditGridView.Columns.Count == 0)
foreach (var pair in LAudit.Properties)
{
AuditGridView.Columns.Add(new BoundField
{
DataField = pair.Key,
HeaderText = pair.Value,
SortExpression = pair.Key
});
}
base.DataBind();
}
we disabling AutoGeneratedColumns, and generating them ourselves. LAudit Properties is just array of KeyValuePair (i'm using it instead of Dictionary, because order is important). My realisation is:
static LAudit()
{
var keys = typeof (LAudit).GetProperties(BindingFlags.Public | BindingFlags.Instance).Select(x => x.Name).ToList();
string[] values =
{
"Prop1", "Prop2", "Prop3", //...
};
Properties = new KeyValuePair<string, string>[keys.Count];
for (int i = 0; i < Properties.Length; i++)
{
Properties[i] = new KeyValuePair<string, string>(keys[i], values[i]);
}
}
public static readonly KeyValuePair<string, string>[] Properties;
It's naive, perhaps, should use LINQ join or something else, but principe still be the same.
Hope it will be helpful.

For a dropdownlist, SelectedIndex always returns the value 0

I have a dropdown in my webpage, which always returns the value 0 as the selected index no matter whichever item the user selects. I have populated the dropdown using a DB query. And I am populating in on Page_Load method in my page. The code shown below does the specified work: int danceid;
protected void Page_Load(Object sender, EventArgs e)
{
if (!IsPostBack)
{
PopulateDanceDropDown();
}
}
private void PopulateDanceDropDown()
{
DataTable dt = new DataTable();DataRow row = null;
dt.Columns.Add("Did", Type.GetType("System.Int32"));
dt.Columns.Add("DName", Type.GetType("System.String"));
var dancer_dance = (from dd in context.DANCER_AND_DANCE
where dd.UserId == dancerId
select new
{
Value = dd.DanceId,
Text = dd.DanceName
}).ToList();
foreach (var dndd in dancer_dance)
{
row = dt.NewRow();
row["Did"] = dndd.Value;
row["DName"] = dndd.Text;
dt.Rows.Add(row); dances.DataSource = dt;
dances.DataTextField = dt.Columns[1].ToString();
if (!IsPostBack)
{
dances.DataBind();
}
}
protected void changeIndex(object o, EventArgs e)
{
danceid = dances.SelectedIndex;
}
protected void dropthedance(object o, EventArgs e)
{
int danceIDFromDropDown = danceid;
var dancer_dance = from dd in context.DANCER_AND_DANCE
where dd.DanceId == danceIDFromDropDown
select dd;
foreach (var dndd in dancer_dance)
{
context.DANCER_AND_DANCE.DeleteOnSubmit(dndd);
}
try
{
context.SubmitChanges();
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
}
The line int danceIDFromDropDown = danceid; in the method dropthedance always has the value 0. Pleaseeeeeeeeeeeeee help someone
Are you sure that you want to be using the index as the ID? Typically, you're going to want to set the actual ID from your database as the DataValueField and then you can grab the value that way.
But I also noticed that you grab the index and place it into a variable on the indexchanged event and then you try to use that value in a different method. I'm assuming that danceid is an attribute somewhere not shown here. At any rate, the value isn't persisting through postbacks. Instead of trying to store it in a variable like you would on a desktop application, try adding EnableViewState="True" to your dropdown control. Then get that index on your submit handler directly. Or if you really want to store it in a variable, then try persisting the value of that variable by storing it in the session or caching it, then pull from that cache/session variable when it comes time to actually use the value.
But again, it might be better practice to place the danceid in the listitem object itself. Just the though of basing IDs on item indexes makes me shudder, especially when you populating the list from a database, because what happens when you add a new item to that list in the library and then try to sort them by name... then your indices become useless.
Replace
int danceIDFromDropDown = danceid;
with
int danceIDFromDropDown = dances.SelectedIndex;
It may work.

How can I use the selected rows in GridView as a source for a GridView in another page?

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:
protected void Page_Load(object sender, EventArgs e)
{
List<int> ids = new List<int>();
if (!IsPostBack)
{
}
else
{
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)
{
ids.Add(bookID);
}
}
Session["Ids"] = ids;
Response.Redirect("SecondPage.aspx");
}
}
and on the SecondPage:
protected void Page_Load(object sender, EventArgs e)
{
DataTable dtBooks = new DataTable("Books");
dtBooks.Columns.Add(new DataColumn("ID", typeof(int)));
if (!IsPostBack)
{
var list = (List<int>)Session["Ids"];
foreach (int id in list)
{
if (Request.QueryString["bookID" + id] != null)
{
DataRow row;
row = dtBooks.NewRow();
row["ID"] = Request.QueryString["bookID" + id];
dtBooks.Rows.Add(row);
}
}
GridView1.DataSource = dtBooks;
GridView1.DataBind();
}
else
{
}
}
but I get no GridView table on the second page. I would be very grateful if anyone notices my mistake and points it out. Hope you can help me.
This is a common issue when setting session variables before a redirect. I think you can work around it by using the overloaded Response.Redirect method:
Response.Redirect("...", false); // false = don't stop execution
See here for more details:
Session variables lost after Response.Redirect
Another option is to store the IDs in a hidden field, and access them with Page.PreviousPage, like this:
HiddenField hidden = (HiddenField)Page.PreviousPage.FindControl("MyHiddenField");
string values = hidden.Value;
Lastly, depending on what the page is doing, you might want to use Server.Transfer here. There are drawbacks to this approach, but there are situations where it's applicable.
In your second page, you are checking for a query string variable before adding a row to dtBooks:
if (Request.QueryString["bookID" + id] != null)
However, you are not passing any query strings when you redirect:
Response.Redirect("SecondPage.aspx");
At a guess, I would think that you originally tried using the query string to pass the IDs, before changing to the session and you haven't updated all of your code.
I am somewhat concerned about your first page code, though. You do realize that you will redirect to the second page whenever a post back occurs? That means that no matter what buttons / controls you have on the first page, if they post back for any reason, you will redirect to the second page.
EDIT AFTER COMMENTS
If you aren't using the query string, then don't use the query string:
foreach (int id in list)
{
DataRow row;
row = dtBooks.NewRow();
row["ID"] = id;
dtBooks.Rows.Add(row);
}

Categories

Resources