How to bind data to DropDownList in UpdatePanel inside DetailsView - asp.net

I use DetailsView to insert rows in database. Row has fields id, subcategory_id etc. I want to fill dynamically dropdownlist ddl_subcategories, which is used in TemplateField. Selected item value of first dropdownlist ddl_categories is used as parameter for generating collection for ddl_subcategories. I try it with using UpdatePanel, but method DataBind returns error "Databinding methods such as Eval(), XPath(), and Bind() can only be used in the context of a databound control.".
There's code of web form
<asp:DetailsView ID="dvw" runat="server" Height="50px" Width="125px"
AutoGenerateRows="False" DataSourceID="ods"
DefaultMode="Insert" DataKeyNames="Section_id"
OnDataBound="dvw_DataBound" OnItemUpdated="dvw_ItemUpdated"
OnItemCommand="dvw_ItemCommand">
<Fields>
<asp:TemplateField HeaderText="Category" >
<ItemTemplate>
<asp:DropDownList ID="ddl_categories" runat="server" AutoPostBack="true" DataSourceID="ods_categories"
DataTextField="Name" DataValueField="Category_id" OnSelectedIndexChanged="category_select_index_changed"/>
</ItemTemplate>
</asp:TemplateField>
<asp:TemplateField HeaderText="Subcategory" >
<ItemTemplate>
<asp:UpdatePanel runat="server" UpdateMode="Conditional">
<ContentTemplate>
<asp:DropDownList ID="ddl_subcategories" runat="server"
SelectedValue='<%# Bind("Subcategory_id") %>' />
</ContentTemplate>
<Triggers>
<asp:AsyncPostBackTrigger ControlID="ddl_categories" EventName="SelectedIndexChanged" />
</Triggers>
</asp:UpdatePanel>
</ItemTemplate>
</asp:TemplateField>
...
</Fields>
</asp:DetailsView>
There is part of behind-code:
protected void category_select_index_changed(object sender, EventArgs e)
{
DropDownList ddl_categories = (DropDownList)dvw.FindControl("ddl_categories");
List<SUBCATEGORY> sections = SUBCATEGORY.Select_all_by_parameters(Int32.Parse(ddl_categories.SelectedValue));//Select all subcategories by id of category
DropDownList ddl_subcategories= (DropDownList)dvw.FindControl("ddl_subcategories");
ddl_subcategories.DataSource = sections;
ddl_subcategories.DataTextField = "Name";
ddl_subcategories.DataValueField = "Subcategory_id";
ddl_subcategories.DataBind();
}
What is my error?
Thanks.

The problem is that when you are data binding the subcategories dropdown, there is no DataBinding Context (see here for more info, pay attention to David Fowler's comments). I don't think the UpdatePanel is the issue as the behavior is the same with or without it (and if you wrap the entire DetailsView in an UpdatePanel).
A solution is to load the items individually instead of data binding the control. It seems a little clumsy, I admit, but it does work.
protected void category_select_index_changed(object sender, EventArgs e)
{
DropDownList ddl_categories = (DropDownList)dvw.FindControl("ddl_categories");
IEnumerable<SUBCATEGORY> sections = new SUBCATEGORY[] { new SUBCATEGORY() { Name = "First " + ddl_categories.SelectedValue, Subcategory_id = "1" }, new SUBCATEGORY() { Name = "Second", Subcategory_id = "2" } };
DropDownList ddl_subcategories = (DropDownList)dvw.FindControl("ddl_subcategories");
ddl_subcategories.Items.Clear();
foreach (SUBCATEGORY cat in sections)
{
ddl_subcategories.Items.Add(new ListItem(cat.Name, cat.Subcategory_id));
}
}
A better option would be to not use Bind in the control, and just set the value in the ObjectDataSource's parameters on Insert/Update.
Update: You can set the ObjectDataSource value directly. Add an event handler for the OnInserting (or OnUpdating as needed) as follows:
protected void ods_OnInserting(object sender, ObjectDataSourceMethodEventArgs e)
{
DropDownList ddl_subcategories = (DropDownList)dvw.FindControl("ddl_subcategories");
e.InputParameters["Subcategory_id"] = ddl_subcategories.SelectedValue;
}
I can't actually test this right now, but it should be close.

Remove the Bind("DBFieldName") and add the following line to the YourDetailsView_ItemInserting like the following:
e.Values["DBFieldName"] =
((DropDownList)YourDetailsView.Rows[1].FindControl("ddl_categories")).SelectedValue;
Note: Don't forget to replace the Rows[1] to your Dropdown templete field index.

Try wrapping the entire DetailsView with the update panel to see if that makes a difference...

Related

Bind data ObjectDataSource to Label

How do you bind data from an ObjectDataSource to a label form an Formview (Detailsview)?
This is my code from the DetailsView ()
Normally it shows the ID of Afstand (Distance) but it has to show instead of id=1 --> "5km"
ID_AFSTAND:
<asp:Label ID="ID_AFSTANDLabel" runat="server" DataSourceId="dtsrcAFstandKilometer" Text='<%# Bind("AFSTAND") %>' DataValueField="ID_AFSTAND" DataTextField="AFSTAND" AppendDataBoundItems="true" />
<asp:ObjectDataSource ID="dtsrcAFstandKilometer" runat="server" OldValuesParameterFormatString="original_{0}" SelectMethod="GetDataAfstand" TypeName="InschrijvenTableAdapters.tblAfstandenTableAdapter"></asp:ObjectDataSource>
<br />
Personally I've never seen binding of a label to any type of data source done from the design view and I'm not sure whether it's even possible because the data sources generally contain multiple records and to bind to a label you would need to implement some kind of "top 1" type logic in the binding.But this can quite easily be done in the code behind, here's an exmple:
Code behind:
protected void Page_Load(object sender, EventArgs e)
{
DataView afstande = dtsrcAFstandKilometer.Select() as DataView;
string firstID = afstande[0][0].ToString();
string firstKM = afstande[0][1].ToString();
ID_AFSTANDLabel.Text = firstKM;
}
.ASPX:
<asp:Label ID="ID_AFSTANDLabel" runat="server" />

How to assign UpdatePanel Trigger's control ID with button's from gridview

currently I have a UpdatePanel for jQuery Dialog use, which contains a GridView.
And that GridView contains a FileUpload control in footer and EmptyDataTemplate
In order to get FileUpload control work in javascript, I know that we need trigger.
However, the button that I wanna assign as trigger is inside GridView's template...
when the button btnAdd clicked, file in FileUpload control will be saved.
Here is the code:
<asp:UpdatePanel ID="upnlEditExpense" runat="server">
<Triggers>
<asp:AsyncPostBackTrigger ControlID="btnAdd"/>
</Triggers>
......................
........................
.........................
<asp:GridView runat="server" ID="grdExpense" ShowHeader="True" ShowFooter="True"
AutoGenerateColumns="False">
<Columns>
...................
<asp:TemplateField>
<FooterTemplate>
<asp:LinkButton runat="server" ID="btnAdd" Text="Add" OnClick="btnAdd_Click"></asp:LinkButton>
</FooterTemplate>
</asp:TemplateField>
</Columns>
</asp:GridView>
</asp:UpdatePanel>
If I put the button id directly in trigger's control ID like this, error come up saying btnAdd could not be found...
what should I do to get FileUpload control work?
This works
protected void grdExpense_RowCreated(object sender, GridViewRowEventArgs e)
{
LinkButton btnAdd = (LinkButton)e.Row.Cells[0].FindControl("btnAdd");
if (btnAdd != null)
{
ScriptManager.GetCurrent(this).RegisterPostBackControl(btnAdd);
}
}
Try registering the post back control from code behind like this:
protected void grdExpense_RowCreated(object sender, GridViewRowEventArgs e)
{
LinkButton btnAdd = (LinkButton)e.Row.Cells[0].FindControl("btnAdd");
if (btnAdd != null)
{
ScriptManager1.RegisterAsyncPostBackControl(btnAdd);
}
}
Instead of adding a trigger to upnlEditExpense maybe you can try to add an update panel around the link button inside the template with no triggers...
<asp:TemplateField>
<FooterTemplate>
<asp:UpdatePanel ID="upnlBtnAdd" runat="server">
<ContentTemplate>
<asp:LinkButton runat="server" ID="btnAdd" Text="Add" OnClick="btnAdd_Click"></asp:LinkButton>
</ContentTemplate>
</asp:UpdatePanel>
</FooterTemplate>
</asp:TemplateField>
I had a similar problem and this post helped me, but I found that registering the control in the scriptmanager works only if the updatepanels UpdateMode is set to "Always". If its set to "Conditional" this approach does not work.
I found another approach that always works which is to add triggers to the updatepanel in the DataBound() event of the gridview:
Dim CheckBoxTrigger As UpdatePanelControlTrigger = New AsyncPostBackTrigger()
Dim SelectCheckBox As CheckBox
For i = 0 To GridViewEquipment.Rows.Count - 1 Step 1
SelectCheckBox = GridViewEquipment.Rows(i).Cells(12).FindControl("CheckBoxSign")
CheckBoxTrigger.ControlID = SelectCheckBox.UniqueID
UpdatePanelEquipment.Triggers.Add(CheckBoxTrigger)
Next

Lost Focus Events for a Control Within GridView

I have multiple textboxes and dropdown lists within my GridView. For one particular textbox I need trigger a server event which gets data from the database and fills it in other columns of the Grid. Is there a simple way to do it or a slightly complicated way as detailed here
I have no problems implementing the above method or thinking of a work around but then thought that there is Cell Lost Focus in a grid control surprises me a little. Am I missing something ? Any help on this would appreciated.
You can set AutoPostBack to true and handle it's TextChanged event.
<asp:GridView ID="GridView1" runat="server" EmptyDataText="It's Empty.">
<Columns>
<asp:TemplateField HeaderText="Name">
<ItemTemplate>
<asp:TextBox ID="txtName"
runat="server"
Text='<%#Eval("Name") %>'
AutoPostBack="true"
OnTextChanged="NameChanged" >
</asp:TextBox>
</ItemTemplate>
</asp:TemplateField>
</Columns>
</GridView>
in codebehind:
protected void NameChanged(Object sender, EventArgs e)
{
var txtName = (TextBox) sender;
var row = (GridViewRow) txtName.NamingContainer;
// you could find other controls in this GridViewRow via
// row.FindControl("ControlID") in case of a TemplateField or
// row.Cells[0].Text (0 = index of column) in case of a BoundField
}

give the ID as a custom attribute to checkbox in a grid for manual update

I like to update just the value of my checkbox in a asp grid.
so i thought i bind the id to the checkbox ( but how?) and fire an update in code behind by clicking the checkbox.
but how can I get the ID, and where I have to bind this id to get it in code behind in the event?
Thanks
Here is what I've managed to do. (Be aware there should be an easier way to do this. I'm very new to ASP.NET)
Here you have a TemplateField in a GridView. Inside it there is an UpdatePanel and the CheckBox is inside it. This is done to make the checking of the TextBox do post in the backgroung (ajax). You might not need it (UpdatePanel) at all.
<asp:TemplateField HeaderText="Private" SortExpression="IsPrivate">
<ItemTemplate>
<asp:UpdatePanel ID="upIsPrivate" runat="server" UpdateMode="Always" ChildrenAsTriggers="true">
<ContentTemplate>
<asp:CheckBox ID="chkIsPrivate" runat="server" OnCheckedChanged="chkIsPrivate_CheckedChanged" AutoPostBack="true" />
</ContentTemplate>
</asp:UpdatePanel>
</ItemTemplate>
</asp:TemplateField>
And this is the method that handles this. Notice that I get the Id from the GridViewRow that contains the CheckBox:
GridViewRow row = (GridViewRow)((CheckBox)sender).Parent.Parent.Parent.Parent;
protected void chkIsPrivate_CheckedChanged(object sender, EventArgs e)
{
if (editMode)
{
GridViewRow row = (GridViewRow)((CheckBox)sender).Parent.Parent.Parent.Parent;
Int32 id = (Int32)uxPhoneCallList.DataKeys[row.RowIndex]["Id"];
CheckBox isPrivate = (CheckBox)row.FindControl("chkIsPrivate");
PhoneCall phoneCall = PhoneCallManager.GetById(id);
...
}
}

DataBound DropDownList in DataGrid - order of binding

I have a DataGrid that looks like this (slightly simplified here):
<asp:DataGrid ID="grdQuotas" runat="server" AutoGenerateColumns="False">
<HeaderStyle CssClass="quotas-header" />
<Columns>
<asp:TemplateColumn>
<HeaderTemplate>
Max order level</HeaderTemplate>
<ItemTemplate>
<asp:DropDownList ID="ddlMaxOrderLevel" runat="server" DataSourceID="xdsOrderLevel"
DataTextField="Text" DataValueField="Value" SelectedValue='<%# Bind("MaxOrderLevel") %>'>
</asp:DropDownList>
</ItemTemplate>
</asp:TemplateColumn>
</Columns>
</asp:DataGrid>
<asp:XmlDataSource ID="xdsOrderLevel" runat="server" DataFile="~/App_Data/OrderLevels.xml">
</asp:XmlDataSource>
In my Page_Load event handler I am creating a DataTable containing default values and DataBinding it to the DataGrid.
The problem is that this is taking place before the DropDownList ddlMaxOrderLevel has been bound to its DataSource, so I get a runtime error telling me that the SelectedValue cannot be set.
If ddlMaxOrderLevel was not in a DataGrid I could just call DataBind() on it. However I cannot do that in this scenario - since it is in an ItemTemplate.
Can anyone suggest a workaround or alternate approach?
You could do the Databinding of the DropDownlist in the Databound event of the DataGrid.
Edit:
I will give you an example that i have tested:
protected void dg_ItemDataBound(object sender, DataGridItemEventArgs e)
{
if (e.Item.ItemType != ListItemType.Header && e.Item.ItemType != ListItemType.Footer)
{
DropDownList dl = (DropDownList)((DataGridItem)e.Item).FindControl("ddlMaxOrderLevel");
dl.DataSource = levels;
dl.DataBind();
dl.SelectedValue = ((DataRowView)e.Item.DataItem)["number"].ToString();
}
}
Create another DataSource and bind it to the DataGrid. Where the SelectMethod would return the default values in a simple object.
Then all the binding should happily work together.

Resources