I'm having an awful time with my GridView. I've been going over my code with a fine-tooth comb and can't find the problem.
I have an ASP.net GridView with seven columns; the data comes from SQL. Two columns hold javascript that the user can click on to remove or modify an entry in the database. The other five fields contain names, phone numbers, and e-mail addresses of people in a database.
Four of my fields are ASP.net TemplateFields.
When the page loads, everything is displayed correctly. However, if a user clicks a JavaScript link, clicks to sort the GridView by any field, or does any other action that causes a postback, the resulting page may not display the contents of my TemplateFields; they are all blank.
Just so you can see what I'm working with, I have a "name" column which is defined like this:
<asp:TemplateField HeaderText="Name" SortExpression="name" ItemStyle-Width="150">
<ItemTemplate>
<%#(string)Eval("surname") + ", " + (string)Eval("fname") %>
</ItemTemplate>
</asp:TemplateField>
If I step thru the code in the debugger, I see that in the case that the template fields display correctly, the debugger stops on the line that displays the name. In the case that the fields are all blank, it jumps over the code to display the contents of those fields; and for no discrenible reason what-so-ever.
The only thing that seems to cause the problem is that there is a single enum value that is set from the query-string (which tells the page what data to load). When the enum value is set to 1, I never see this error, but it seems that I always see it when the enum value is set to 2.
However, the enum is never ever referenced anywhere in my gridview code.
In any event, the GridView.DataSource = x is always called and GridView.DataBind() is always called. I've tried stepping thru the code and checking that the data from the database is correct. I've tried debugging on the code block where DataBind is called from and also the GridViewRow_DataBound Event handler.
I've seen other posts elesewhere about trouble with TemplateField, but none of them seem to address what's going on here.
Any ideas?
I figured it out part of the answer. I found some code that read like:
if (!userIsAllowedToSeePrivatePhoneNumbers)
removePrivatePhoneNumber(gridView);
***
protected void removePrivatePhoneNumber(GridView gridView)
{
DataControlField privatePhoneNumberColumn = null;
foreach (DataControlField column in gridView.Columns)
{
if (column.HeaderText.ToUpper() == "PRIVATE PHONE")
privatePhoneNumberColumn = column;
}
gridView.Columns.Remove(privatePhoneNumberColumn);
}
I discovered that if I set the visibility of the column to false instead of removing the column, all of my data displays correctly. I do not understand why this is, but it is a satisfactory solution to make the program work for now.
protected void removePrivatePhoneNumber(GridView gridView)
{
DataControlField privatePhoneNumberColumn = null;
foreach (DataControlField column in gridView.Columns)
{
if (column.HeaderText.ToUpper() == "PRIVATE PHONE")
privatePhoneNumberColumn = column;
}
privatePhoneNumber.Visible = false;
}
Related
Consider the following .aspx page:
Title:
<asp:TextBox runat="server" ID="TitleTB"></asp:TextBox>
Details:
<asp:TextBox runat="server" ID="DetailsTB"></asp:TextBox>
<asp:Button runat="server" ID="Btn" Text="Submit" OnClick="Btn_click"/>
Note that I minimized the code to be legitimate so a lot of lines are missing (irrelevant lines, <br/> for example).
In the C# file, I usually post the details to the database (inserting them), but if I have a certain field in my query string (to_edit, per se) I need to update the, already existing, record.
Obviously, this task is overall simple. The thing is, that when that field is included, I initially (in the Page_Load event) set the Title and the Details fields to the values already found in the record (so the user won't have to input from zero, he'd have the ones he already entered to edit).
A debug would show, though, that when I post these back to the database (UPDATE query, which looks a bit like this UPDATE Table SET Title = #Title, Details = #Details WHERE ID=#ID, where I checked #ID - which is completely valid. #Title corresponds to TitleTB.Text and #Details to DetailsTB.Text, both added with SqlCommand.AddWithValue()) that DetailsTB.Text and TitleTB.Text are, for some reason, the same as I assigned them in the Page_Load although I deleted the whole text box content in my browser and refilled it with a different string.
Here are chosen parts of my Code Behind:
//A part of my Page_Load()
//reader is an SqlDataReader, the connection is valid, the values here are valid, and the output is as planned.
TitleTB.Text = (string)reader["Title"];
DetailsTB.Text = (string)reader["Details"];
And up to now, everything seems fine.
//Relevant parts of Btn_click()
cmd.Connection = conn; //valid connection
cmd.CommandText = "UPDATE QuestionsTable SET Title = #Title, Details = #Details WHERE ID=#ID";
cmd.CommandType = CommandType.Text;
cmd.Parameters.AddWithValue("#Title", TitleTB.Text);
cmd.Parameters.AddWithValue("#Details", DetailsTB.Text);
cmd.Parameters.AddWithValue("#ID", Request.QueryString["to_edit"]);
conn.Open();
int affected = cmd.ExecuteNonQuery(); //affected is now 1, as expected.
conn.Close();
//There's a redirection over here.
But in the code shown above, TitleTB.Text and DetailsTB.Text are the same, it's not that the update query doesn't work. It's that, for some reason, the text boxes values won't change even when I clearly change them on my browser.
Any idea what could happen?
EDIT:
One remark is that when I use the OnClientClick event to alert the values (and then returning true, it was only for a test) - the values are identical to what I typed (not to the default value).
One of the possible reasons for such behavior is that you forgot to check if Page_Load is caused by a post back and you overwrite text both values in both get and post.
Just try to add an obvious condition:
//A part of my Page_Load()
//reader is an SqlDataReader, the connection is valid, the values here
if ( !this.IsPostback )
{
TitleTB.Text = (string)reader["Title"];
DetailsTB.Text = (string)reader["Details"];
}
Note that control values are stored in the viewstate and thus there is no need to update them during consecutive post backs.
Wrap your Page_Load event code in If(!IsPostBack){ }. The postback property of the button is refreshing the page with default value.
The code should be as follows -
If(!IsPostBack) {
//A part of my Page_Load()
//reader is an SqlDataReader, the connection is valid, the values here are valid, and the output is as planned.
TitleTB.Text = (string)reader["Title"];
DetailsTB.Text = (string)reader["Details"];
}
A good read may be helpful - http://www.codeproject.com/Articles/811684/Understanding-The-Complete-Story-of-Postback-in-AS
Suddenly my application has some problems when getting the hiddenfield values on server side.
My Code that was running before this sudden event;
<asp:HiddenField ID="hfColumnName" runat="server" />
in the code behind I use to get and set its values like;
if(hfColumnName.Value == SortDirection.Ascending.ToString())
{
//have logic to perform.
hfColumnName.Value = SortDirection.Ascending.ToString();
}
else(hfColumnName.Value == SortDirection.Descending.ToString())
{
//have logic to perform.
hfColumnName.Value = SortDirection.Descending.ToString();
}
When next time this code runs, the hidden field value contains the concatenated values of its old and new one.
I faced the same issue. The root cause was html/asp code some improper tags. Please make sure the tag is proper.
http://forums.asp.net/p/1448466/3304601.aspx#3304601
I have encountered a problem and I didn't manage to find any soultions yet. Let me simplify things a bit.
I have 2 forms, the first contains an ASP ListBox with multi select mode enabled. I submit the form and in the other form I use just for testing purposes this snippet of code:
protected void Page_Load(object sender, EventArgs e)
{
foreach (string formKey in Request.Form.AllKeys)
{
if (formKey != null)
{
if (formKey.Equals("ctl00$MainContent$ListBox1"))
Label1.Text = Request.Form[formKey];
}
}
}
The problems is that the values that come from the listbox (the values that i selected in the previous form) are separated by "," for ex. "test1,test2,test3". How can i change this separator to "$" for example? I need to change it because the actual values may contain "," and i don't manualy feed them to the listbox.
I can't use any other mode of transfering this values between the form because the entire application uses this model. The values that i get are then sent to a workflow where there will be manipulated and in the workflow i need to know where each listbox item starts and ends so it must be an unique separator.
Any help is apreciated! Thank you very much
Thank you MatteKarla but unfortunately this does not solve my problem. Yes, this is a good way of transfering the values from one form to another.
However i must use the method I described above with Request form keys because the listbox is one of many others "parameters" that are generated at runtime and have their values sent to a workflow method that takes this values. And i can't afford to change that in my application.
My problem is that coma (",") separator is used by default with a multiselect listbox.
I thought that there maybe is a method to change that separator from coma to another char because the coma can also be included in the value itself and this will create confusion.
As i said if i select three values test1, test2 and test3, the result with my method will be a string looking like "test1,test2,test3". However a "test1$test2$test3" would be much better.
But I'm affraid that changing this default separator is not possbile. I must think at a method to overcome this problem like replacing before feeding the listbox all the intended coma from the values with some other char not to create confusion. But this is not a great way of doing it.
On your first page/form (First.aspx.cs) create a public property with the listbox:
public ListBox PostedListBox { get { return ListBox1; } }
Set the postback-url for the button to Second.aspx
Second page in the aspx-file after the #Page-directive add:
<%# PreviousPageType VirtualPath="~/First.aspx" %>
Then in Form_Load on Second.aspx.cs you can extract the values:
if (PreviousPage != null)
{
ListBox postedListbox = PreviousPage.PostedListBox;
foreach (var index in postedListbox.GetSelectedIndices())
{
var itemText = postedListbox.Items[index].Text;
}
}
Or you could just try to locate the control by using:
if (PreviousPage != null)
{
var control = PreviousPage.FindControl("ListBox1") as ListBox;
}
Third Edit:
You could use GetValues:
Request.Form.GetValues("ctl00$MainContent$ListBox1");
returns a string array containing each of the selected items.
Today i faced a pretty weird problem, made of a stored procedure, an autocompleteextender and event handling. If anyone can suggest a concise title for the following saga, please do!
First i'll try to introduce the players (some parts are a bit abridged) and then the scenarios. Environment is VS2008, C#, SQL Server 2005 and ASP.NET.
DB table:
TABLE (
SomeNbr INT,
SomeAbbr VARCHAR(10),
Name VARCHAR(30)
)
StoredProcedure:
Reads the table above and returns something like this:
DECLARE #Results TABLE
(
SomeNbr INT,
--Composite CHAR(50), -- voodoo magic starts here...
Composite VARCHAR(50)
)
Composite-field of the return table is made of SomeNbr, SomeAbbr and Name fields. They are appended together and separated by commas (f.ex. "number, abbreviation, name").
Stored Procedure is used to retrieve a set of rows from db, and these are put into a typed datatable. This datatable is then stored in a session variable. DataTable's XSD-Schema has datatype String for the "Composite"-field.
!Important! For some rows in the db, "Name"-field contains the actual value and rpadding (made of spaces) up to the maximum length. Other just contain the name with no rpadding.
User interface (ASPX-page with codebehind):
I have extended textbox-control to include an AutoCompleteExtender from AJAX Control Toolkit. Control is declared as follows:
<ajx:TextControl ID="txtControl"
runat="server"
AutoPostBack="true"
UseAutoComplete="true"
ServiceMethod="GetItems"
MinimumPrefixLength="1" />
There are some additional basic textboxes on the ui.
CodeBehind:
AutoCompleteExtender uses the following webmethod:
[System.Web.Services.WebMethod]
public static string[] GetItems(string prefixText, int count)
{
try
{
return Utility.GetMatchingStrings(_DataTableInSession, prefixText);
}
catch (Exception ex)
{
throw;
}
}
GetMatchingStrings(...)-method compares preFixText against all datarows in _DataTableInSession, and returns an array of matching strings. Comparison is done with the datarows' "Composite"-fields.
In codebehind, the event handler gets set:
extendedTextControl.Control.TextChanged += PopulateTextControls;
, and:
private void PopulateTextControls(object sender, EventArgs e)
{
// Read Text-property from extended textcontrol
string str = extendedTextControl.Text;
// Split string by commas and take parts into an array
string[] arrStr = SplitString(",", str);
// Set the values in array to Text-properties of TextBoxes
SetValuesToTextBoxes(arrStr, _TextBoxes);
}
Scenarios
Everything seems to be fine, the extended TextControl is working as it is supposed to and generates the selectable list below the textbox.
Scenario 1:
Now, when user selects an item from autocompletion list the following things happen:
Selected string is set to Text-property of the extended textcontrol
textcontrol's autopostback happens
on postback, an eventhandler is registered for textcontrol's TextChanged-event
TextChanged-event is fired and other textboxes get filled with data as expected
For this scenario to happen, the "Composite"-field in the typed datarow has to be something like this:
"10", "10, ABBR, Some Name Goes Here____..._" (<- spaces replaced with underscores)
If the datarow is more like this:
"10", "10, ABBR, Some Name Goes Here"
then we'll go to...
Scenario 2:
The above is a sort of happy day -scenario :-)
If the matching datarow is made from a row in db where "Name"-field is not padded with spaces up to the maximum length (VARCHAR(50)), then the TextChanged-event does not get fired.
I spent a good few hours trying to figure out everything above. First i was quite confused that why the **** selecting some of the items in autocompletion list do work, and some don't.
When i realized to look at the datatable's contents, i saw that some rows had the padding and others did not. After that i tried to ltrim(rtrim()) everything in the SP's return table, and none of the items worked anymore.
After that i tried to set the SP's return value to CHAR(50) instead of VARCHAR(50), and that fixed it.
Question:
What is going on in my application? Why doesn't the registered event fire? It's like TextChanged would only work for string of certain length (max length of the VARCHAR(50)), but how would that be possible?
Problem is solved, but i have no idea why. Just another day at the office, i suppose :-)
I'll be happy to provide additional data and clarifications!
Edit 1: Added note about spaces and underscores within the datarow.
After that i tried to set the SP's return value to CHAR(50) instead of VARCHAR(50), and that fixed it.
I didn't understand the whole scenario but I would look at the CHAR(50) vs. VARCHAR(50) issue. I also do not know what "WebOlkiUtility.GetMatchingStrings" does.
declare #text char(50)
select #text = 'test'
select #text
declare #text2 varchar(50)
select #text2 = 'test2'
select #text2
-- output --
CHAR(50): 'test '
VARCHAR(59): 'test2'
CHAR(50) returns always 50 Character!
BTW: Why do you attach the event this way?
if (GetPostBackingControlId() == extendedTextControl.ID)
{
extendedTextControl.Control.TextChanged += PopulateTextControls;
}
You could attach it always. In your ASPX file or in the Page_Init Method.
i've populated a dropdownlist control with different text properties but each text properties had THE SAME value (text property was A, value properties is blah,text property was B, value properties is blahblah, etc... )
ASP.net only checks value properties on postback and because ALL values were the same (for
testing reason) this little annoying behavior happened. Is there a work around? does this mean you can't never have the value to be the same?
Sounds like you are working on the wrong event. Try SelectedIndexChanged.
Ensure you also have the AutoPostBack property set to True.
Resolved
OK, so I got digging on this since I was curious :)
There is a "problem" when databinding with non-unique values.
So, firstly, I publicly apologise for saying otherwise.
To replicate:
ASPX
<asp:DropDownList ID="myDDL" runat="server" AutoPostBack="True">
</asp:DropDownList>
<asp:Label ID="lblSelItem" runat="server"Text="Currently Selected Item: 0"></asp:Label>
<asp:Label ID="lblSelVal" runat="server" Text="Currently Selected Value: X"></asp:Label>
Code-Behind
List<string> MyData()
{
List<string> rtn = new List<string>();
rtn.Add("I am the same value!");
rtn.Add("I am the same value!");
rtn.Add("I am the same value!");
rtn.Add("I am the same value!2");
return rtn;
}
protected void Page_Init()
{
if (!Page.IsPostBack)
{
// Load the Data for the DDL.
myDDL.DataSource = MyData();
myDDL.DataBind();
}
}
protected void Page_Load(object sender, EventArgs e)
{
// Display the Currently Selected Item/Value.
lblSelItem.Text = "Currently Selected Item: " + myDDL.SelectedIndex.ToString();
lblSelVal.Text = "Currently Selected Value: " + myDDL.SelectedValue;
}
Run, changing the values in the DropDownList. Note that a PostBack does not occur.
When looking at the Source, I realised that we need to explicitly set the "value" attribute for the <option> elements generated by the server control, which lead me to do something like:
New Code-Behind
Dictionary<string, string> MyTwoColData()
{
Dictionary<string, string> rtn = new Dictionary<string, string>();
rtn.Add("1", "I am the same value!");
rtn.Add("2", "I am the same value!");
rtn.Add("3", "I am the same value!");
return rtn;
}
protected void Page_Init()
{
if (!Page.IsPostBack)
{
// Load the Data for the DDL.
Dictionary<string, string> data = MyTwoColData();
foreach (KeyValuePair<string, string> pair in MyTwoColData())
{
myDDL.Items.Add(new ListItem(pair.Value, pair.Key));
}
myDDL.DataBind();
}
}
This explcitly sets the values to the "1", "2", "3" etc making them unique, while still displaying the correct data within the list.
Obviously, you can change this to work with single-column lists but just running through a for loop and using the value of i or something.
As to good workarounds with DataSets, not sure.
Realistically, would we present a list of options with the exact same values to the user?
I personally think not, which is probably why this "problem" hasn't been addressed :)
Enjoy!
PS:
Oh, I should also add, if you want to use the text value in the "fix" then change it to SelectedItem rather than SelectedValue.
ASP.NET can't distinguish between different items with the same values in the dropdown because when the browser sends the HTTP POST, it sends just the selected value.
ASP.NET will find the FIRST item in the dropdown with a value that matches.
You need to ensure that each item in the dropdown has a distinct value. You could do this by adding a key to each value. In other words, instead of having "blah" for each value, you'd use "blah-1", "blah-2", etc.
The problem is that if the selected index doesn't change the postback won't fire. In the case where the user makes the same selection, the selected index does not change.
Sorry that this doesn't answer the question, but it does explain the behavior as far as I know.
The SelectedIndexChanged won't even trigger because all the listitem value in the dropdownlist control are the same. I did some googling. It seem like this is the common problem. I haven't found any work around yet.
You could use values like this:
1:2
2:2
3:2
Where the second number is the "real" value. Then your event should fire and you can parse out the "real" value in your code behind.
Why do you have a drop down where all of the values are the same? Or is just that some of them are the same?
If you think back to pre ASP.Net days then the only thing that is send with a form submit from a <SELECT> is the VALUE of the <OPTION>. ASP.Net then effectively works out which item is selected by looking up this value in the list of data items.
You will also notice that if you have two items with the same value but different labels that if you do trigger a postback the next time the form loads the first one will be displayed, even if you have the second one selected before you performed the postback.
If you take a step back for a moment and consider your original data source - how would you identify which text value was selected if all you have is the Value? How would you select that value from a database, or from a list? How would you update that row in the database? If you try it you will find that .Net throw an Exception because it cannot uniquely identify the row.
Therefore you need to add a unique key to your data.