DropDownList annoyance: same value won't trigger event - asp.net

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.

Related

ASP Multiselect listbox separator

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.

What code do I type in next page after passing gridview button clicked rowindex

I pass the row index value into next page when gridview button clicked using this code
if(e.CommandName=="select")
{
int Id = int.Parse(e.CommandArgument.ToString());
//Label1.Text = Id.ToString();
Response.Redirect("~/manclothes1.aspx?Id=" + e.CommandArgument.ToString());
}
but i don't know what code i write in next page to display row data
please can anyone help me
In ASP.NET you don't actually pass code to a new page. Instead you modify the controls on the current page and the ASP.NET framework re-renders the page for you. Instead of the Response.Redirect line you want something like
DataGrid1.EditItemIndex = Id;
In the page_load of manclothes1.aspx you can try
protected void Page_Load(object sender, EventArgs e)
{
if (Request.QueryString["Id"] != null)
{
var id = Request.QueryString["Id"];
// do something with id variable
...
}
}
You'll probably want to reference whatever code on the current page populates the gridview with data in the first place. Essentially, where the code on the current page gets many rows to populate a gridview, the code on the manclothes1.aspx page will get one row. If it's data from a database, the query will likely be very much the same but with an additional WHERE clause to filter by (I'm assuming) an ID value, which is probably a primary key (or the primary key, if we're talking about only one table).
To put this into context, what the call to Response.Redirect() is doing is telling the client (browser) to issue an entirely new request (a GET request, that is) for an entirely new resource (manclothes.aspx with a query string parameter). So understand that "the next page" knows nothing of the gridview on the current page. Nor should it, really. The request should be handled entirely separate from the current page.

Why does my GridView intermittently not display my .net TemplateFields

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;
}

ASP.NET AutoCompleteExtender-enabled TextBox's TextChanged-event doesn't fire when selecting certain kind of item from AutoCompleteExtender (whew...)

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.

How do I count checked checkboxes across all pages of a gridview using jquery?

I want to instantly update a status line indicating the number of checked checkboxes across all pages of an asp.net gridview. Right now I am only ably to count the number of checkboxes that are checked on the current gridview page.
Here is my code:
$(document).ready(initAll);
function initAll(){
countChecked();
$(".activeBoxes").click(countChecked);
}
function countChecked() {
var n = $(".activeBoxes input:checked").length;
$("#checkboxStatus").text(n + (n == 1 ? " vehicle is" : " vehicles are") + " selected on this page. ");
if( n == 0){
$(".activateButton").hide();
$("#checkboxStatus").hide();
}else{
$("#checkboxStatus").show();
$(".activateButton").show();
}
}
Keep a hidden text field on your page and everytime you check a box, call a javascript method that will write the 'id' of the checkbox to the hidden field. Each time you postback your page, serialise the hidden field's value to the session in your desired objects structure (be it objects, hash table, array etc).
Upon rendering the page, each checkbox can check the session object structure (that you have created before) and determine if the state of the checkbox was last checked or not.
You could use JQuery to loop through all checkboxes on the page and increment a counter if the checkbox is checked.
You can track the total selection in viewstate (or something similar) on page change. I did something similar tracking the selected row ID's in an array. In my case I had to re-check the items when they returned to the page. Additionally if you allow sorting the selection may move across pages.
Edit: Sorry this doesn't actually your Jquery question, but maybe it will help...
What you're missing is removing the ID. When checking rows and tempid is not checked make sure it is not in saveids.
Do you know that Google is your friend?
Selecting CheckBoxes Inside GridView Using JQuery
The WML Video
And without JQuery but for more perfectionist behavior (like the image below), try this link
alt text http://www.gridviewguy.com/ArticleImages/GridViewCheckBoxTwistAni.gif
I am using a viewstate to keep track of all checked items across all pages and rechecking them upon returning to the page.
I will have to add my viewstate value to the page total and somehow subtract overlapping totals. Since my jquery does not include an id, this will be tricky.
protected ArrayList savedIds;
if (ViewState["SavedIds"] == null) savedIds = new ArrayList();
else savedIds = (ArrayList)ViewState["SavedIds"];
List<int> activateList = new List<int>();
foreach (GridViewRow tt in GridView2.Rows)
{
CheckBox cb = (CheckBox)tt.FindControl("ActivateItem");
HiddenField id = (HiddenField)tt.FindControl("IdField");
if (cb.Checked)
{
int tempId = 0;
string tempId2 = id.Value.ToString();
int.TryParse(tempId2, out tempId);
activateList.Add(tempId);
}
}
foreach (int activateId in activateList)
{
if (!savedIds.Contains(activateId.ToString())) savedIds.Add(activateId.ToString());
}
ViewState["SavedIds"] = savedIds;

Resources