While going through some of the code, i found there is written like this:
ListViewDataItem dataItem = (ListViewDataItem)(((ImageButton)sender).Parent.Parent);
int noteId = Convert.ToInt32(((HiddenField)dataItem.FindControl("hidNoteId")).Value);
Please explain the meaning of these two line.
There is a control (typed to hidden field) that is being used to track the identity of item within a ListView (http://msdn.microsoft.com/en-us/library/bb398790.aspx)
And it's code that shouldn't be written, IMO. It's inefficient and brittle. Also, the identity of the item is stored in plain text in the source of the page.
This code appears to be getting the identity of a databound item when a button is clicked. A better way would be to simply set the command arguments of the button, like so:
<asp:ImageButton runat="server" CommandArgument="[Binding Expression]" />
In the event handler for the button, the CommandArgument can be retrieved and converted to an Int32. With this methodology, you shouldn't even need that hidden field.
Firstly, both the lines seem a bit crazy in their use of brackets and casting. There's probably a better way to do what they're doing, but without more code it's difficult to offer any suggestions.
Anyway, the first line is going to be in an event handler for an event raised by an ImageButton. The event handler method will have a sender argument; this is being cast to an ImageButton. Then Parent.Parent is called on this ImageButton; this will give the object two levels up in the control heirarchy. The developer is obviously quite sure this is a ListViewDataItem, so it's being cast to one of these. Therefore the variable dataItem now contains an instance of a ListViewDataItem.
In the second line, the FindControl method is being called on dataItem. There is presumably a control under this ListViewDataItem with an ID of "hidNoteId". FindControl returns a Control; but the developer knows this control is actually a HiddenField, so there's a cast. The Value property of this hidden field is then passed into the Convert.ToInt32 method to give an integer - this is then stored in the noteId variable.
So at the end of it all, there's a ListView, in which each data item contains a hidden field that contains the value of some ID. This code is getting the ID.
dataItem is a bound control, which when clicked is being used to represent a placeholder. This is used to identify which line is being executed within in the control.
By looking for a parent's parent of this control, it's giving the code a starting point to navigate to the correct row to extract a value from.
After the row is found, the noteId is being assigned a hidden value and cast into an integer.
When items are bound to a grid/repeater, with a button as the post-back control, a way is needed to identify which row is being executed. All the code is doing above is navigating the control and extracting a value from a set of values within the row.
Related
I'm trying to loop through a gridview and save all the items in it at once. However I have a problem getting the values from the dropdownlist and textbox. I get this error each time:
ArgumentOutOfRangeException was caught. Specified argument was out of the range of valid values.
Here's the code I'm using:
foreach (GridViewRow gvr in gvInvalidOrgs.Rows)
{
try
{
org_code = Convert.ToInt32(gvr.Cells[0].Text);
division = ((DropDownList)gvr.Cells[1].Controls[0]).SelectedValue;
org_description = (((TextBox)gvr.Cells[2].Controls[0]).Text);
}
...
}
Both the textbox and the dropdownlists are dynamically created on rowbound if that matters.
TIA
Without knowing precisely which line the exception is thrown on, it's hard to diagnose with equal precision, but what's likely happening is that you're looking for a control in a cell that doesn't have any controls.
How could this come about? Well, since you are creating the controls in RowDataBound, if you don't bind your grid on every postback (and I'm not recommending that you do) your dynamically-added controls won't be recreated on postback. The posted data will include values for those controls, and ViewState will contain data for those controls, but if you don't recreate those controls on every postback, those controls won't exist when you attempt to access their values.
But you don't have to rebind to recreate the controls. As Mr. Schmelter says in his comment, if you move your creation of the controls from the RowDataBound to the RowCreated event handler, they will be created when the grid rows are re-created on postback.
Maybe someone can help me out. I have created a simple web user control, a date and time picker, to be dropped onto my webform. This all works very nicely, I can set properties, usee the control satisfactorily for ui etc.
When it comes time to "use" the controls selected_date_time property, I just cant get it to persist. Nothing.. I have researched and tried endlessly using viewsate, context and session. Onbiouosly session works, but its dirty, and I am using two copies of this control (start and end time), so session vars really needs to be hacked to maket his work.
Am I missing something? The control gets initialized every time something happens, and obviuosly loses its state information. The ui keeps its state tho, as I can select a date, write that date to a label, and that persists. But when I try to access my properties of the control to retreive the selected combined date and time, (that is already persisting visually), its nothing. I debuggeed and it gets initialized everytime I do any form of post on the page.
Can someone please shed some light on this for me? Its really starting to be an issue now.
Thanks in advance.
Example: (simple components)
UC _ save_method
ViewState("var_time") = "My veiwstate text"
form _read_method
dim str as string = ViewState("var_time")
form sees nothing in the viewstate var.
I have also tried it with normal properties and values, which wasn't working, which is why I moved on to viewsate var's for my properties. Right now, I am just trying to get the viewstate to work even without properties.
Seems my form and the control must have two seperate viewstates? I am a bit of a n00b concerning viewstates.
Thanks
[solution]
You have to explicitly reset your properties on the prerender method of the control. My misunderstanding was that the page and the control share the same single viewstate. Turns out the control and the page that its on, has its own independant viewstates.
So absically, on your property set function in the control, set your value in the viewstate, and upon prerender, take that viewstate value, and set the property get variable =- the value in viewstate.
You can now access the property from your page as if everything was normal and the world is not ending... pheww
Thanks to cnay for directing me.
'Usr control xyz
Protected Sub Page_PreRender(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.PreRender
my_time = ViewState("var_time")
'my_time is the get variable for property date_time
End Sub
'page use
xyz.date_time
Perhaps, you should post the complete control code to get the better answer but speaking in general terms - view-state (or control state) is a typically correct approach to persists control state such as properties. Each control instance get the different view-state bag and hence collisions are easily avoided.
Now, coming to your problem, a typical editable control first restores its state from view-state and then uses the request data to modify the state as needed. For example, a simple text-box control would persists its text value in view-state. On post-back, the text value will be restored from the view-state and then gets overwritten by the value present in the request (looked up by UniqueID property).
Now, for user controls, typically you can use child controls values (or properties) to derive the control value/properties - so these may not use view-state. However, if you add properties/state that is not backup by child controls then you may have to back them up in the view-state. So let's say your user control has two child controls - one for Date and other for time then can combine their values to get the selected date-time value for your control.
I have a list that I need to bind to a List I get from an API. The list looks like this:
struct DataItem { int level; string name; Guid key };
List<DataItem> myList = API.GetList();
ListView1.DataSource = myList;
ListView1.DataBind();
All this works fine for display. However, the table must edit the level value. I am unsure how to make that happen. I have tried event handlers on the listView, but they are never called. I have tried a text box for the level field (with both Bind and Eval) and an event handler OnTextChanged, but the event handler is never called. (I have tried with various combiniations of AutoPostBack and ViewState enabled.)
How can I programatically edit this data structure?
Two way data binding you are trying to implement here won't work like this - List doesn't implement INotifyPropertyChanged (someone correct me if I'm wrong).
You may consider using a plain old DataTable which can be two-way-bound out-of-the-box. If performance is not a highly critical issue, converting your List to a DataTable (and back, depending on what you want to do with the modified data) is simple enough, rather than struggling with custom implementations of list types.
Ok so the obvious answer is, because the flow of a composite control demands my childcontrols to be created at a certain point in time. I got a problem i think other people must have had as well.
My control is a composite "container/collection" control. It will be fed with an object and based on that objects data it will create a number of childcontrols. So my control will render a header (always) and x-number of, say TextBox controls (based on the object it was fed with).
I'm creating my header in CreateChildControls() obviously, but i can't possibly create my TextBoxes there as well, because i don't know if the object (to base the TextBoxes on) has been fed yet? I thought of exposing a property/method to set/fed the object through, but i'm not sure when it will be called.
So what do i do? I mean i can't possibly create the TextBoxes in CreateChildControls() or can I? I mean - when is CreateChildControls() called - i know i can call EnsureChildControls() (which i already do in a property to set the innerText of the header - since i need the header to be created before setting its innerText obviously).
How about this
var c = new MyContainerControl();
c.Header = "fun";
c.TextBoxObject = myTextBoxes;
That would raise an error (or at best not create any TextBox'es) if i put the building of the TextBoxes in CreateChildControls().
Would it be more sane to instead just store the Header in a member variable and thus not having to call EnsureChildControls() in the exposed method/property setting the Header innerText. I just don't like this aproach much, since it would be complicating things by adding extra logic to store temporarely and later having to figure out when to set it (probably in PreRender).
Also i guess i could make some kind of Databound control, ensuring the data be present at the time of calling .DataBind(). I really don't like this either since last i looked at creating databound controls it got very complicated.
This really should be an easy task to solve - I know I'm missing something somewhere...
what you're describing IS a databound control. And yes, it's somewhat complicated, but it's the proper design paradigm for this type of instance.
That said, had you considered utilizing the repeater control rather than trying to roll out your own composite which behaves in the exact same manner? Rather than passing it a random object, pass it a collection or an iList with the number of text areas you're wanting.
I have a asp.net 2.0 web site with numerous asp:DropDownList controls.
The DropDownList control contains the standard info city, state, county etc... info.
In addition to the standard codes the site also has custom codes that the users can configure themselves.
For example a animal dropdown may contain the values Dog, Cat, Fish, ect...
I am popluating the DropDownList from a SQL 2005 table that I created e.g. tblCodes
Everything works great and users are able to add orders using the numerous DropDownList controls to choose items from the list.
The problem occurrs if a user wants to change one of their custom dropdowns. For example a user would like to change the verbage
on a animal type control from Dog to K9. This is where the problem starts.
For all new orders the drop down works fine. When the user retrieved an old order
I get the following error in the C# codebehind
"'DropDownList1' has a SelectedValue which is invalid because it does not exist in the list of items."
What's happening is the old order has a database field value of Dog and the DropDownList no longer has Dog in its list since the user changed it to K9.
Any ideas on a workaround?
Is there a way to make the asp:DropDownList accept items not seeded in its list?
Is there another control I could use?
I solved this exact same problem just two days ago. First, I moved the code that set the SelectedValue to a PreRender handler for the DropDownList. Then, I add logic to first check to see if the value is in the drop down list. If not, I add it.
Here's my code. ddSpecialty is my drop-down list, populated with "specialties" from the database. registration.Specialty is the specialty that the user chose, which may or may not be in the drop down, since that particular specialty may have been deleted since they last chose it.
protected void ddSpecialty_PreRender(object sender, EventArgs e)
{
if (!ddSpecialty.Items.Contains(new ListItem(registration.Specialty)))
ddSpecialty.Items.Add(registration.Specialty);
ddSpecialty.SelectedValue = registration.Specialty;
}
I've become very fond of the following little snippet for setting DropDownList values:
For non-DataBound (eg Items added manually):
ddl.SelectedIndex = ddl.Items.IndexOf(ddl.Items.FindByValue(value));
For DataBound:
ddl.DataBound += (o,e) => ddl.SelectedIndex = ddl.Items.IndexOf(ddl.Items.FindByValue(value));
I sure do wish though that ListControls in general didn't throw errors when you try to set values to somthing that isn't there. At least in Release mode anyways it would have been nice for this to just quietly die.
Your SelectedValue should be a unique id of some sort, that doesn't change. The Text value that gets displayed to the user is something seperate, and can change if necessary without affecting your application, because you associate the id with your Order, not the displayed string value.
I'm not sure it's the same issue, but I had a similar sounding issue with trying to bind a DropDownList that I wanted to contain in a GridView. When I looked around I found a lot of people asking similar questions, but no robust solutions. I did read conflicting reports about whether you could intercept databinding, etc events. I tried most of them but I couldn'f find a way of intercepting or pre-empting the error.
I ended up creating a subclass of the ddl, intercepting the error from there hacking a fix.
Not tidy but it worked for my needs. I put the code up on my blog in case it's of help. link text
Check this:
http://www.codeproject.com/Tips/179184/ASP-dropdownlist-missing-value-error.aspx
Ran into this myself. Oddly, ddl.ClearSelection(); didn't work. Had to use ddl.SelectedValue = null
Also noticed, that this must come AFTER I clear the items from the list ddl.Items.Clear(); which also seems weird. Setting the SelectedValue to null, then clearing the items still threw the error.
Once this is done, re-bind the list and re-select with new value.
Try this:
if (ddl.Items.Contains(new ListItem(selectedFacility)))
ddl.SelectedValue = selectedFacility;
I have made a workaround after having this problem very often. Unfortunate that MS still did not recovered this issue.
Anyway, my workaround is as follows.
1) I bind the data to the ToolTip property of the DropDownList
<asp:DropDownList ID="edtDepartureIDKey" runat="server" CssClass="textbox"
ToolTip='<%# Eval("DepartureIDKey") %>' DataSource="<%# DLL1DataSource() %>" DataTextField="DisplayField" DataValueField="IDKey"
onprerender="edtDepartureIDKey_PreRender">
2) On the prerender event i check the availibilty of the data, and if it is not in the list I simply add it, then set the selectedindex to the data valuei which I saved in ToolTip property
protected void edtDepartureIDKey_PreRender(object sender, EventArgs e)
{
DropDownList ddl = (sender as DropDownList);
if (ddl.Items.FindByValue(ddl.ToolTip) == null)
{
//I am pulling Departure Data through the ID which is saved in ToolTip, and insert it into the 1st row of the DropDownList
TODepartureData v = new TODepartureData(DBSERVER.ConnStrName);
TODeparture d = v.Select(Convert.ToInt32(ddl.ToolTip));
ddl.Items.Insert(0, new ListItem(d.DeptCode, ddl.ToolTip));
}
ddl.Items.FindByValue(ddl.ToolTip).Selected = true;
}