FormView datasource lost on button click in another formview - c#

I want to preface this by saying that I am a beginner to asp.net, especially when it comes to working with the FormView controls. I have searched long and hard and have spent hours debugging this issue.
I have 3 FormViews on one aspx page. Each FormView has its own EditItemTemplate and PagerTemplate with DefaultMode="Edit". I am not using a SqlDataSource, but instead databinding programatically on the PageLoad when if(!Page.IsPostBack) and also calling the databinding method when the PageIndexChanging method is called. The pager template contains a 'Back' and a 'Next' button set with CommandArgument="Prev" and CommandArgument="Next", respectively, and both set with CommandName="Page".
The paging works great on the first FormView. When I hit the back or next button, it pages (i.e. re-binds) appropriately. During the 1st FormView's paging event, I also successfully call the binding methods for the 2nd and 3rd FormView since I want them to bind data that is specific to the page selected in the 1st FormView.
But, when I page back to the first page of the 1st FormView (i.e. PageIndex = 0), and then try to page forward in the 2nd FormView, the datakey for the 1st FormView is null. In fact, the formview1.DataSource is null for the 1st FormView when I try to click a navigation button on the 2nd FormView.
Then, here's where I thought it was weird, ... if I click back a second time on the 1st FormView, THEN the formview1.DataSource is fine, and I can then navigate in the 2nd FormView.
All viewstates for the formviews and the buttons are set to true.
I have tried calling formview1.DataBind() inside formview2's paging event before any paging occurs but no success there. I have also tried setting properties: UseSubmitBehavior="False" and CausesValidation="False" on the paging buttons. Admittedly, I did this without really understanding the behavior but rather implemented after seeing it suggested in solutions for other somewhat related problems.
The templates are rather long since there are many fields in each. But the FormView tags look like this:
<asp:FormView ID="fvHeader" runat="server" DataKeyNames="ObjectID" DefaultMode="Edit" AllowPaging="True" OnModeChanging="fvHeader_ModeChanging" OnPageIndexChanging= "fvHeader_PageIndexChanging">
<EditItemTemplate> ..... </EditItemTemplate> </asp:FormView>
PagerTemplates:
<PagerSettings Mode="NextPrevious" />
<PagerTemplate>
<span class="labels">Page: <%#fvHeader.PageIndex+1%> of <%#fvHeader.PageCount %></span>
<asp:Button ID="btnBack" runat="server" CommandArgument="Prev" CommandName="Page" CssClass="btnHdr" Text="<< Back" />
<asp:Button ID="btnNext" runat="server" CommandArgument="Next" CommandName="Page" CssClass="btnHdr" Text="Next >>" /> </PagerTemplate>
Note that 'fvHeader' is what I'm calling 'formview1' for simplicity in my question.
Back/Next buttons C# code and databinding the 1st formview:
protected void fvHeader_PageIndexChanging(object sender, FormViewPageEventArgs e)
{
fvHeader.PageIndex = e.NewPageIndex;
bindFV_Initial();
//rebind fvSub1 (2nd formview) to get the 1st obs of the newly selected header record
fvSub1.ChangeMode(FormViewMode.Edit);
fvSub1.PageIndex = 0;
bindSub1_Initial();
//rebind 2nd subform
fvSub2.ChangeMode(FormViewMode.Edit);
fvSub2.PageIndex = 0;
bindSub2_Initial();
}
private void bindFV_Initial()
{
if (conn.State == ConnectionState.Open)
{
conn.Close();
}
conn.Open();
if (dtEOS == null || dtEOS.Rows.Count == 0)
{
sqlda = new SqlDataAdapter("USE dbWEF SELECT * FROM tblHeader WHERE [UserID] = '" + Session["User"] + "' AND [ProjectName] = '" + Session["Project"] + "'", conn);
sqlda.Fill(dtEOS);
}
fvHeader.DataSource = dtEOS;
fvHeader.DataBind();
conn.Close();
if (dtEOS.Rows.Count > 0)
{
fillDD_fvHeader(); //Fill dropdowns and databind ddls
}
}
}
Below is the error message I receive when I attempt to navigate the 2nd formview.
Error in: bindSub1_Initial.
System.ArgumentOutOfRangeException: Index was out of range. Must be non-negative and less than the size of the collection.
Parameter name: index
at System.Collections.ArrayList.get_Item(Int32 index)
at System.Collections.Specialized.OrderedDictionary.get_Item(Int32 index)
at System.Web.UI.WebControls.DataKey.get_Item(Int32 index)
at RenewableEnergyDataEntry.Forms.EagleObservationSurvey.bindSub1_Initial()
During debugging, I find that this error occurs right when the 2nd formview is being binded because the datatable is empty which is a result of it needing the datakey from formview1 to pull the correct data, which again, is null because formview1's datasource goes to null. But clicking formview's back button one more time, refills and KEEPS the datasource, thereby allowing formview2 to fill and navigate perfectly. So strange...

I suggest you simplify you code.
more than likely you are creating conflicts with the view state.
We can't see where and how your dataSource: (dtEOS) is being defined.
But I am going to guess that it's being reset conditionally on a call back.
This is a common problem in ASP.net (conflict with view sate and /or databinding).
Try using a grid instead of a formview and it will handle the paging much more gracefully without needing a postback and data rebind.

Related

ASP:Dropdown List producing duplicate value on button click while data binding

I have a asp:dropdown defined as below:
<asp:Label ID="DropdownLabel1" runat="server">State Name</asp:Label>
<asp:DropDownList ID="statename" runat="server" AppendDataBoundItems="true" Width="600px" style= "top:100%;" Enabled ="false">
<asp:ListItem Text="" Value="" />
</asp:DropDownList>
I have a button function which is defined as below:
<asp:Button ID="Savestate" runat="server" Text=" Save "
OnClick="SaveState_Click"></asp:Button>
By clicking this button, it will invoke the function SaveState_Click from server side (.cs file)
I am trying to bind values to the dropdown list from a dictionary which is getStateName. The keys of getStateName holds all the state names and hence using that to bind the value.
Populate function:
statename.DataSource = getStateName.Keys;
statename.DataBind();
statename.SelectedIndex = statename.Items.IndexOf(statename.Items.FindByText(GetKeyFromValue(getStateName[1].Id)));
The above populate dropdown function works perfectly and binds all the key values to the dropdown list and also selects correct index value as I expect.
But when I click the button again, button function calls the populate function again and the databind causes the dropdown value to repeat. How many ever times, the button is clicked the dropdown value repeats that many times.
When I click the button, values in the dropdown list gets replicated. Initially there are 10 values, when I click the button again, the values gets repeated and it has 20 values now (each value gets populated twice), when I click next time, it has 30 values (each value gets populated thrice)
I saw a post to include enableviewstate= "false" as an extra parameter and when I did that, dropdown value does not repeat, but selected dropdown value comes as a null value when I try to get it in .cs file.
I also saw another post to include SecurityQuestion2.AppendDataBoundItems = false; after databind, and I couldn't succeed
I tried below if statement and couldnt succeed either.
if (statename.Items.Count <= 1)
{
statename.DataSource = getStateName.Keys;
statename.DataBind();
}
How to prevent this duplication while doing databind?
Re-write your statement like this, debug it and see which one is creating issue.
Comment this line and break it into few lines :
statename.SelectedIndex = statename.Items.IndexOf(statename.Items.FindByText(GetKeyFromValue(getStateName[1].Id)));
Change to:
string text=statename.Items.FindByText(yourText);//Any value that exists within your dropdown just for testing purpose.
string id=getStateName[1].Id;//change string to int if its an int type
string value=GetKeyFromValue(getStateName[1].Id);
Now check the values of these temporaly created variables by putting a breakpoint at the very first line, press F10 and see whether they contains any value or not, if not then track why they are not giving expected results.
Although it is not the solution but It may help you to track what the actual issue is.
protected void Page_Load(object sender, EventArgs e)
{
Dictionary<int, string> getStateName = new Dictionary<int, string>();
getStateName.Add(3,"New York");
getStateName.Add(2,"Chicago");
getStateName.Add(1,"Washington");
getStateName.Add(0,"Toronto");
statename.DataSource = getStateName;
statename.DataTextField = "Value";
statename.DataValueField = "Key";
}
and button click event
protected void SaveState_Click(object sender, EventArgs e)
{
statename.DataBind();
}
and ....
<asp:DropDownList ID="statename" runat="server" AppendDataBoundItems="true"
Width="600px" style= "top:100%;"></asp:DropDownList>
try the enableViewstate="false" it may solve part of the problem, with that dropdownlist. If ViewState is switched off then this will no longer happen, because the values are not remembered between Postbacks. Instead, the DropDownList gets populated from scratch each time.

Editing nested listview producing "Item has already been added. Key in dictionary" error

I’ve got an asp.net page with a listview on it, this master listview has several child (nested) listviews within it.
The idea of the page is that the master listview will only ever display 1 record at a time but each of the child list view could contain 0 or more records. Regardless of whether the master listview is being inserted or editing, the user should be able to insert, edit and delete rows within the child listviews
What I’m finding is that when I’m insetting a record into the master listview and editing (press the update button) on any row within any of the child listviews I’m getting this error message:
Item has already been added. Key in dictionary: ‘JobID’ Key being
added: 'JobID'
when I press the update button in any of the nested listviews.
I know that this error message typical means that I’m trying to insert an item with a key that matches something else already held but while ‘JobID’ is within the child listviews it is not a primary key but it is the key of the master listview but I’m not (knowingly) doing anything with the master listview.
I’m not receiving any error messages when the master listview is being edited or then the master listview is inserting and I’m inserting rows into the child listviews
If it makes any difference all listviews have properties of a singleton object as their datasources.
What I suspect is happening is that when I press the update button in the child listview something is calling the master listview. Because the master listview is in “insert mode”, the master listview is trying to insert the master details into something resulting in a “second” inserting record.
If my suspicion is correct, does any one have any ideas how I can stop the master listview firing its insert routine? Am I missing something or is this a “feature” of listviews?
Code below
Thanks in advance
Master list view
<asp:ListView ID="lvJob" runat="server" DataSourceID="dsJob"
DataKeyNames="JobID" oniteminserting="lvJob_ItemInserting"
onitemupdating="lvJob_ItemUpdating"
onitemupdated="lvJob_ItemUpdated"
oniteminserted="lvJob_ItemInserted" EditIndex="0"
onprerender="lvJob_PreRender" ondatabound="lvJob_DataBound" >
Child list view
<asp:ListView ID="lvQuotes" runat="server" DataKeyNames="QuoteID"
DataSourceID="dsQuotes" InsertItemPosition="LastItem"
oniteminserting="lvQuotes_ItemInserting"
oniteminserted="lvQuotes_ItemInserted"
onitemupdated="lvQuotes_ItemUpdated"
OnItemUpdating="lvQuotes_ItemUpdating" >
<EditItemTemplate>
<tr style="">
<td>
<asp:Button ID="UpdateButton" runat="server" CommandName="Update"
Text="Update" CssClass="ListButton" />
<asp:Button ID="CancelButton" runat="server" CommandName="Cancel"
Text="Cancel" CssClass="ListButton" />
As requested - the Code behind just to note when the error occurs these events are not fired but they do fire in other scenarios
updating event on one of the child listviews
protected void lvQuotes_ItemUpdating(object sender, ListViewUpdateEventArgs e)
{
//meaningless code to attach a breakpoint to
JobId = JobId;
}
Inserting event on the Master list view
protected void lvJob_ItemInserting(object sender, ListViewInsertEventArgs e)
{
try
{
e.Values["DateRecieved"] = TestDate(e.Values["DateRecieved"]);
e.Values["DateDue"] = TestDate(e.Values["DateDue"]);
}
catch (Exception ex)
{
e.Cancel = true;
ShowErrorMsg(ex);
}

Populate DropDownList inside Grid Template Column

I have a dropdownlist inside a template column on a obout grid. Currently i am populating the dropdownlist on page load with a sqldatasource. However, i now have to load the dropdownlist dependent on the value of a certain column. For example: If status = 1, i populate the dropdownlist with a list of available options that pertain to status 1.
<obout:Column ID="colStatus" DataField="wf_status_id" Align="center" HeaderText="Status" HeaderAlign="center" Width="130px" Wrap="true" runat="server" AllowGroupBy="true" AllowFilter="true">
<TemplateSettings EditTemplateId="tmpStatusIDEdit" TemplateId="tmpStatusID" />
</obout:Column>
<obout:GridTemplate runat="server" ID="tmpStatusID" >
<Template>
<%# Container.DataItem["Status"]%>
</Template>
</obout:GridTemplate>
<obout:GridTemplate runat="server" ID="tmpStatusIDEdit" ControlID="ddlStatus" ControlPropertyName="value">
<Template>
<obout:OboutDropDownList runat="server" ID="ddlStatus" Width="100%" Height="200" MenuWidth="215" DataSourceID="sdsStatus" DataTextField="wf_status_text" DataValueField="wf_status_id" />
</Template>
</obout:GridTemplate>
public void OnGridRowDataBound(object sender, Obout.Grid.GridRowEventArgs e)
{
if (e.Row.RowType == Obout.Grid.GridRowType.DataRow)
{
DropDownList ddlStatus = new DropDownList();
ddlStatus = (DropDownList)e.Row.FindControl("ddlStatus");
//LOAD DROP DOWN HERE//
}
}
When i attempt to execute this code, it shows that ddlStatus is null everytime. I have a tried a multitude of ways to get this and for some reason cannot seem to get it. Perhaps aother set of eyes or other ideas could help me out. Please let me know what it is i am doing wrong. Thank you ahead of time
UPDATE:DATABOUND EVENT CODE
<obout:OboutDropDownList runat="server" ID="ddlStatus" Width="100%" Height="200" MenuWidth="215" OnDataBinding="ddlStatus_DataBinding" DataSourceID="sdsStatus" DataTextField="wf_status_text" DataValueField="wf_status_id" />
protected void ddlStatus_DataBinding(object sender, EventArgs e)
{
Obout.Interface.OboutDropDownList ddl = (Obout.Interface.OboutDropDownList)(sender);
string statusID = Eval("wf_status_id").ToString();
}
I added the DataSource because i do not see another way to have the databinding event triggered.
I don't really know anything about obout controls but I have to assume they act very similar to the asp.net controls and have just been extended. With that assumption in mind, I will try and answer your question.
Your RowDataBound event has a few coding issues... for example, I am not sure why you are defining a new DropDownList and then trying to overwrite it with the next line. Also it sounds like the next line is returning null anyways.
First off I suggest not using the DataBound event at the row level. Use the control's DataBinding event as it will localize your code a lot better because you can trigger off the specific control's DataBinding and therefore not have to search for it. If your code changes (markup or codebehind) it is also a lot easier to change as it will not affect other things and there is less room for introducing bugs.
So I would make the following changes to address this:
Change your DropDownList definition to implement DataBinding:
<obout:OboutDropDownList runat="server" ID="ddlStatus" Width="100%" Height="200"
MenuWidth="215" OnDataBinding="ddlStatus_DataBinding" />
Then implement the OnDataBinding event (get rid of your DataBound event if you weren't using it for anything else):
protected void ddlStatus_DataBinding(object sender, System.EventArgs e)
{
// This will point to ddlStatus on the current row that is DataBinding so you
// don't have to search for it.
OboutDropDownList ddl = (OboutDropDownList)(sender);
// Now you can fill the DropDownList and set the default value how ever you like
...
}
You can also see this other question I answered a long time ago to see if it helps as it is doing the same thing but with a Repeater but it is pretty much the same thing as a grid:
Populating DropDownList inside Repeater not working
EDIT: Changed the code to use OboutDropDownList in the DataBinding.
you can find plenty of samples on obout knowledge base and examples. These should help you out: http://www.obout.com/combobox/aspnet_integration_grid.aspx, http://www.obout.com/grid/aspnet_ajax_cascading_comboboxes.aspx
(they refer to comboBox, but you can easily adapt them do a ddl)

GridView not updating from SelectedIndexChange event of a DropDownList within an UpdatePanel

My problem:
An UpdatePanel holds a DropDownList and a GridView. The GridView populate baseds on a value that is stored in a Label and a value that is selected from a DropDownList. It does NOT populate on PageLoad; it only populates when a query string is present or when a button is pressed, so there is no code in the !IsPostBack. The GridView is populated with the initial SelectedValue of the DropDownList, and I want the GridView to repopulate when the SelectedValue of the DropDownList changes.
The thing is... IT DOES CHANGE! But only once. The GridView initially populates with data using the value of the default SelectedValue of the DropDownList. When the SelectedValue is changed, the data in the GridView changes. But only once. After changing the DropDownList value the second time or more, nothing happens.
<asp:UpdatePanel runat="server" ID="theUpdatePanel" UpdateMode="Conditional" ChildrenAsTriggers="True">
<ContentTemplate>
<asp:DropDownList runat="server" ID="theDropDownList" OnSelectedIndexChanged="theDropDownList_OnSelectedIndexChanged" EnableViewState="true" AutoPostBack="true" />
<asp:GridView ID="theGridView" runat="server" AutoGenerateColumns="False">
<Columns>
...
</Columns>
</asp:GridView>
</ContentTemplate>
</asp:UpdatePanel>
theUpdatePanel's UpdateMode is Conditional, so it updates when its children PostBack. ChildrenAsTriggers is true, so children cause it to update.
theDropDownList's AutoPostBack is true, so it will postback on change. EnableViewState is true (false makes it not even load). SelectedIndexChange links to the right function, and I know that it's getting called by putting a break inside it during debug mode.
Here is the SelectedIndexChanged method:
public void theDropDownList_OnSelectedIndexChanged(object sender, EventArgs e)
{
//get value that is stored in theLabel - I know this value is correct every time
int theValueFromTheLabel = Int32.Parse(theLabel.Text);
//populate theGridView with data from the DB
theGridView_Populate(theValueFromTheLabel);
//update theUpdatePanel (it works for the first change whether this line is here or not)
theUpdatePanel.Update();
}
I've tried both with and without the updatepanel.Update(); method, and it's the same result: the first change of theDropDownList's value works, and every following change does nothing.
The function to actually populate the GridView is pretty simple:
protected void theGridView_Populate(int theValueFromTheLabel)
{
//get value from theDropDownList - this value does change when theDropDownList's value changes
int theValueFromTheDropDownList = Int32.Parse(theDropDownList.SelectedValue);
//get data from DB - the data here does change every time theDropDownList's value changed
ComplexClassController controller = new ComplexClassController();
List<ComplexClass> data = controller.GetData(theValueFromTheLabel, theValueFromTheDropDownList);
//load theGridView - this changes the data, but doesn't refresh theGridView to be able to see it
theGridView.DataSource = data;
theGridView.DataBind();
}
Any ideas? I must be misunderstanding the events behind UpdatePanel.
what is valueLabel? It doesn't look like it ever gets changed?
Why not just do ((DropDownList)sender).selectedValue to get your value to redatabind the gridview with?
Kev
It does not seems to be any problem of getting value from dropdown list the reason might be in your giving data source of your combo
place load of combo in !postback of page load event some thing like this
protected void Page_Load(object sender, EventArgs e)
{
if(!ispostback){
Loadcomobo();
}
}
if its not the case so can you place your page load event code here
thanks...
I'm still not sure why it updated once and then never again, but the problem has been fixed by placing the DropDownList and the GridView in their own UpdatePanels instead of the same one.
Try adding a trigger to the update panel
<triggers>
<asyncpostback ControlID="theDropDownList" />
</triggers>
Or something like that before the
</asp:UpdatePanel>

Dynamically adding a command button to a GridView

I'm having an issue with trying to add a button to my grid. My GridView is first loaded with data in the PageLoad event.
I'm then taking the data in the first cell of each row, and creating a button that will link to a URL. To get the URL, I have to run a query with the data in the first cell as a parameter. I was doing this in the RowDataBound event at first, but hitting that query for every row was making it really slow.
So I decided to add a button that would retrieve the URL only when you clicked the button.
Here's my GridView:
<asp:GridView ID="gvResults" runat="server"
OnRowDataBound="gvResults_RowDataBound"
OnRowCommand="gvResults_RowCommand">
</asp:GridView>
And my code:
protected void gvResults_RowDataBound(object sender, GridViewRowEventArgs e)
{
if (e.Row.DataItem != null)
{
LinkButton lb = new LinkButton();
lb.CommandArgument = e.Row.Cells[0].Text;
lb.CommandName = "NumClick";
lb.Text = e.Row.Cells[0].Text;
e.Row.Cells[0].Controls.Add((Control)lb);
}
}
protected void gvResults_RowCommand(object sender, CommandEventArgs e)
{
switch (e.CommandName.ToLower())
{
case "numclick":
string url = GetUrl(e.CommandArgument.ToString());
Response.Redirect(url);
break;
default:
break;
}
}
The grid generates fine, the button gets added to the grid for each row. But when I click on it, the RowCommand event doesn't fire, and the page just refreshes.
Does anyone know what the issue is?
Why use a dynamic button at all? You can easily put the linkbutton directly into the markup of the gridview (as long as you don't mind using a template field) and there will be no need to mess around with the RowDataBound event.
Your markup would look something like the following:
<Columns>
<asp:TemplateField HeaderText="SomeHeaderText">
<ItemTemplate>
<asp:LinkButton ID="lnkBtn" runat="server" CommandName="NumClick" CommandArgument= '<%# (string)Eval("dbValue") %>' Text='<%# (string)Eval("dbValue") %>'></asp:LinkButton>
</ItemTemplate>
</asp:TemplateField>
<asp:BoundField></asp:BoundField>
<asp:BoundField></asp:BoundField>
<asp:BoundField></asp:BoundField>
</Columns>
Add breakpoints to the RowCommand event and make sure that you can hit the breakpoints.
The problem may lie elsewhere.
Also, make sure that you're not databinding on postback.
You have a big trouble with your code. It's pretty hard for me to explain what's your big mistake, but I can easily tell you how to fix.
The problem is that you generate a new button inside the RowDataBound event, definitely the wrongest choice. The button gets rendered because it exists after that event when page renders, but doesn't exist before data binding. If you bind data everytime you load the page (even during postback) the button still gets rendered because you generate a new button.
But since the button doesn't exist before data binding, it cannot raise events. You must declare the button from markup into a template of GridView, then access it not by using new LinkButton() but by using e.Row.Cells[0].FindControl("buttonId") and set its text. Then, you have to set its markup in order to fire its own Command event (not RowCommand) and handle it as you used (don't forget to set CommandArgument during data binding)
[Edit] I also made a mistake: controls inside data bound controls also don't exist before data binding. But they are initialized not with new Control() (by the private methods of data bound control) but with Page.LoadControl(typeof(Control)). That's the first thing you must fix when you load controls dynamically!!
Because the control is added dynamically on databind and you have to databind the gridview for each postback, the control being "clicked" is different each time. The event doesn't fire because at the time it needs to fire it doesn't exist as it did in the last iteration of the page.
I notice you don't have any logic determine if the button should be there, and it always goes into cell[0].
You should place this button into a TemplateItem so that it exists properly. If you have a need to do it in code-behind, you are probably better served doing it in the RowCreated event.

Categories

Resources