Trouble binding XML child to DataList using ReadXML() - c#

I am trying to bind an ASP DataList control with the values returned from an XML file. What's somewhat unique is that I'm not loading the XML from a file, but rather using XMLTextReader() to load it as a string. The reason for this is that the XML will be built from values read from the database. Here's the code which takes (in this case a hard-coded) XML string, adds it to a DataSet and then binds the DataList control...
StringBuilder xml = new StringBuilder();
xml.Append("<product>");
xml.Append("<sku>241</sku>");
xml.Append("<prodID>2SIDED</prodID>");
xml.Append("<name>Product Name</name>");
xml.Append("<price>6.99</price>");
xml.Append("<description>Product Description</description>");
xml.Append("<standalone>1</standalone>");
xml.Append("<sellable>1</sellable>");
xml.Append("<product-type>10</product-type>");
xml.Append("<customization>");
xml.Append("<option1></option1>");
xml.Append("<option2></option2>");
xml.Append("<option3></option3>");
xml.Append("</customization>");
xml.Append("</product>");
using (XmlTextReader tr = new XmlTextReader(new StringReader(xml.ToString())))
{
var document = XElement.Load(tr);
string source = document.ToString();
DataSet dsProdList = new DataSet();
dsProdList.ReadXml(new StringReader(source));
prodDetail.DataSource = dsProdList;
prodDetail.DataBind();
}
Here's the DataList control...
<asp:DataList ID="prodDetail" runat="server" OnItemDataBound="prodDetail_ItemDataBound">
<ItemTemplate>
<div class="proddetail">
<asp:Image ID="Image" runat="server" width="480" height="325" AlternateText='<%#DataBinder.Eval(Container.DataItem, "name") %>'/>
<asp:HiddenField ID="hdnStandalone" runat="server" Value='<%#DataBinder.Eval(Container.DataItem, "standalone") %>' Visible="false" />
<asp:HiddenField ID="hdnSellable" runat="server" Value='<%#DataBinder.Eval(Container.DataItem, "sellable") %>' Visible="false" />
<asp:HiddenField ID="hdnTypeId" runat="server" Value='<%#DataBinder.Eval(Container.DataItem, "product-type") %>' Visible="false" />
<asp:HiddenField ID="hdnOption1" runat="server" Value='<%# DataBinder.Eval(Container.DataItem, "option1") %>' Visible="false" />
<asp:HiddenField ID="hdnOption2" runat="server" Value='<%#DataBinder.Eval(Container.DataItem, "option2") %>' Visible="false" />
<asp:HiddenField ID="hdnOption3" runat="server" Value='<%#DataBinder.Eval(Container.DataItem, "option3") %>' Visible="false" />
<asp:Label ID="lblDescription" runat="server" Text='<%#DataBinder.Eval(Container.DataItem, "description") %>'>
</div>
</ItemTemplate>
</asp:DataList>
This seems to be working fine until the code attempts to bind "option1" at that point I get the following exception:
DataBinding: 'System.Data.DataRowView' does not contain a property with the name 'option1'.
From what I can work out this definitely seems to be an issue with "option1" being a child of "customization". If I simply comment out the opening and closing "customization" tags, which effectively leaves the "option(s)" as children of "product" the code runs fine and I get the output I'm expecting.
Is there any way I can format the DataBinder.Eval() to see the child item? If so, I can't figure it out and hours of searching having been unsuccessful. Or should I be tackling this differently? Perhaps not using a DataSet? Any help is appreciated. Thanks.

You can try with xDoc and linq and anonymous type as,
prodDetail.DataSource = from item in xDoc.Descendants("product")
from needThis in item.Descendants("customization")
select new
{ ProductName = item.Element("sku").Value,
ID = item.Element("prodID").Value,
//----other properties----
Option1 = needThis.Element("option1").Value,
Option2 = needThis.Element("option2").Value,
//---and some more properties----
};
you have to make sure you give the property names of the anonymous object same as the property names you are expecting in the databind in datalist.

Related

Repeater grouping items from single database

I have a table with headers(example): Group, DisplayName, EditableData, description.
I have been trying for the last few days to learn repeaters to sort the information based on the Group so I can get something that looks like the following layout. This is a user control I am trying to put together.
Group1
------------------
Name1 EditableData Some Description
Name2 EditableData Some Description
Group2
------------------
Name3 EditableData Some Description
Name4 EditableData Some Description
I have looked a the following other examples online:
Nested repeaters - grouping data and
Nested Repeaters in ASP.NET
I believe I do not properly understand how repeaters work enough to deal with nesting or datasource.
<asp:Repeater ID="Repeater1" runat="server">
<ItemTemplate>
Group: <%#Eval("Group")%><br />
<%--Nested Repeater would go here for all the children info for each "Group"--%>
</ItemTemplate>
</asp:Repeater>
Using DISTINCT in my SQL to just retrieve "Group" leaves me with the proper groups without repeats and I guess I could just instead set the groups in labels and then later make repeaters for each specific label... This seems terrible when I may later update editableData back to the database.
What I really want I guess is at least a link to a walkthrough that explains how repeaters work along with Eval() and datasources. I mean, code to do everything I need to complete this first step in my project would be perfect ;P But I also want to be able to understand these better as I am probably going to be using them often in the near future.
I once encountered the same issue where I was to sort the data by group and I had to display the common items in a grouped segment.
There are of-course multiple ways of how you retrieve data, for example, you can get Distinct Group Names and bind it to the repeater and then on ItemDataBound event you can execute and get other elements like this:
<asp:Repeater runat="server" ID="rptrGroups" OnItemDataBound="rptrGroups_ItemDataBound">
<ItemTemplate>
<asp:Label runat="server" ID="lblGroupName" Text='<%# Eval("GroupName") %>' />
<asp:GridView runat="server" ID="gv">
</asp:GridView>
</ItemTemplate>
</asp:Repeater>
protected void rptrGroups_ItemDataBound(object sender, RepeaterItemEventArgs e)
{
if (e.Item.ItemType == ListItemType.Item)
{
var lblGroupName = (Label)e.Item.FindControl("lblGroupName");
GridView gv = (GridView)e.Item.FindControl("table");
var dataTable = FetchDataWithGroupName(lblGroupName.Text); // Method that fetches data with groupname.
gv.DataSource = dataTable;
gv.DataBind();
}
}
This is not a recommended way because it goes to database, runs query, and then fetches data for each item (if you are fetching this data from db). If you have thousands of Groups then it will make db calls for thousands of times which is a bad thing.
The second solution is, you design a model and feed a custom model that will do the job. Let me explain it by a sample model:
public class GroupedModel
{
public string GroupName {get; set;}
public List<NestedData> TableData {get; set;}
}
public class NestedData
{
public string Id {get; set;}
// Your columns here...
}
Then query and initialize list of GroupedModel class then feed it to the repeater. Let me do it with some dummy data.
var tableData = new List<NestedData>();
var nestedData1 = new NestedData { Id = "1" };
var nestedData2 = new NestedData { Id = "2" };
tableData.Add(nestedData1);
tableData.Add(nestedData2);
var groupedModel = new GroupedModel
{
GroupName = "Group1",
TableData = tableData
};
var listGroupedModel = new List<GroupedModel>();
listGroupedModel.Add(groupedModel);
rptrGroups.DataSource = listGroupedModel;
Then modify the markup like this:
<asp:Repeater runat="server" ID="rptrGroups">
<ItemTemplate>
<asp:Label runat="server" ID="lblGroupName" Text='<%# Eval("GroupName") %>' />
<asp:GridView runat="server" ID="gv" DataSource='<%# ((GroupedModel)Container.DataItem).TableData %>'>
</asp:GridView>
</ItemTemplate>
</asp:Repeater>
I find that just using a gridview or list view works rather nice. However, DO NOT attempt to use the grouping feature - as it is for placing items across the page, not down.
Lets make this really simple!
Ok, so I have a list of Hotels, but I want to group by city.
So, you build a query like this:
Dim strSQL As String =
"SELECT ID, FirstName, LastName, HotelName, City FROM tblHotels ORDER BY City, HotelName"
GridView1.DataSource = Myrst(strSQL)
GridView1.DataBind()
Ok, so that fills out our grid view. We get this:
So far, two lines of code!
But, we want to group by City.
So at the forms class level, add simple var:
Public Class HotelGroupGrid
Inherits System.Web.UI.Page
Dim LastCity As String <----- this one
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
If IsPostBack = False Then
LastCity = ""
Call LoadGrid()
End If
End Sub
Ok, so now on the data item bind event, simply add a NEW row.
The code looks like this:
If e.Row.RowType = DataControlRowType.DataRow Then
' if grouping = 1 then create a new row!
Dim gvRow As DataRowView = DirectCast(e.Row.DataItem, DataRowView)
If gvRow("City") <> LastCity Then
LastCity = gvRow("City")
' insert a new row for grouping header
Dim MyRow As New GridViewRow(-1, -1, DataControlRowType.DataRow, DataControlRowState.Normal)
Dim MyCel As New TableCell()
'MyCel.Width = Unit.Percentage(100)
Dim MyTable As Table = e.Row.Parent
MyCel.ColumnSpan = MyTable.Rows(0).Controls.Count
Dim MyLable As New Label
MyLable.Text = "<h2>" & gvRow("City") & "</h2>"
MyCel.Controls.Add(MyLable)
MyRow.Cells.Add(MyCel)
MyTable.Rows.AddAt(MyTable.Rows.Count - 1, MyRow)
End If
End If
Now, above is a "bit" of a chunk to chew on - but still not a lot of code.
So, now when we run above, we get this:
Our grid view markup looks like this:
<asp:GridView ID="GridView1" runat="server" AutoGenerateColumns="False" DataKeyNames="ID">
<Columns>
<asp:BoundField DataField="City" HeaderText="City" InsertVisible="False" ReadOnly="True" SortExpression="ID" />
<asp:BoundField DataField="ID" HeaderText="ID" InsertVisible="False" ReadOnly="True" SortExpression="ID" />
<asp:BoundField DataField="FirstName" HeaderText="FirstName" SortExpression="FirstName" />
<asp:BoundField DataField="LastName" HeaderText="LastName" SortExpression="LastName" />
<asp:BoundField DataField="HotelName" HeaderText="HotelName" SortExpression="HotelName" />
</Columns>
</asp:GridView>
So, not too bad.
If you decide to use a listview? Then the code becomes quite a bit less, but the markup for listview is quite a handfull.
All we do is create a row that is our heading, and now show, or hide that row based on the start of new grouping.
So, if one decides to use a list view? Then we get this:
(I assume you use the databind wizards - your not possibly typing in the markup by hand - right? - saving world poverty here)
So, for a list view (and I think the list view is BETTER, since the layout options for that heading row is wide open to any kind markup and extra controls you dream up.
So, the markup (generated - and then chopped out the fat) is this:
<asp:ListView ID="ListView1" runat="server" DataKeyNames="ID">
<EmptyDataTemplate>
<table runat="server" style="">
<tr><td>No data was returned.</td></tr>
</table>
</EmptyDataTemplate>
<ItemTemplate>
<tr id="GroupHeading" runat="server" style="display:none">
<td colspan="4">
<h2><asp:Label ID="City" runat="server" Text='<%# Eval("City") %>' /></h2>
</td>
</tr>
<tr>
<td><asp:Label ID="IDLabel" runat="server" Text='<%# Eval("ID") %>' /></td>
<td><asp:Label ID="FirstNameLabel" runat="server" Text='<%# Eval("FirstName") %>' /></td>
<td><asp:Label ID="LastNameLabel" runat="server" Text='<%# Eval("LastName") %>' /></td>
<td><asp:Label ID="HotelNameLabel" runat="server" Text='<%# Eval("HotelName") %>' /></td>
</tr>
</ItemTemplate>
<LayoutTemplate>
<table id="itemPlaceholderContainer" runat="server" border="0" style="">
<tr runat="server">
<th runat="server">ID</th>
<th runat="server">FirstName</th>
<th runat="server">LastName</th>
<th runat="server">HotelName</th>
</tr>
<tr id="itemPlaceholder" runat="server">
</tr>
</table>
</LayoutTemplate>
</asp:ListView>
Now no question that the listview spits out a lot more markup - but we now have a full row for the heading. So we get this:
But, now our code simply will hide or show that "extra" row we have in the marketup.
And it quite simple now:
If e.Item.GetType = GetType(ListViewDataItem) Then
Dim MyRow As HtmlTableRow = e.Item.FindControl("GroupHeading")
Dim lblCity As Label = MyRow.FindControl("City")
If lblCity.Text <> LastCity Then
LastCity = lblCity.Text
' Hide/show group heading
MyRow.Style("display") = "normal"
Else
MyRow.Style("display") = "none"
End If
End If
So the trick in most cases is to simply layout that extra row item, and then on the item data bound event you simply hide or show that heading part.

How to pass a hidden field value on javascript function inside repeater control

I tried a lot but couldn't figure it out. I wanted to pass data on my javascript function.
I am saving data on hidden filed. what I want as soon I click on my button it will call javascript function & pass my hidden field vlaue.
<asp:Repeater ID="rptGallary" runat="server" >
<ItemTemplate>
<asp:HiddenField ID="hfsportsmanfeedid" runat="server" value='<%# DataBinder.Eval(Container.DataItem,"SportsmanFeedId") %>'/>
<asp:Button ID="btnLike" runat="server" Text="Like" OnClientClick="Test("How to pass here"));" />
</ItemTemplate>
</asp:Repeater>
Thanks for your help.
You just need to pass the clicked element on Test button click
Try with this code:
HTML/ASPX Markup
<asp:Button ID="btnLike" runat="server" Text="Like"
OnClientClick="Test(this);" />
Javascript
function Test(element){
var $btn = $(element) // Gets clicked button
var hiddenBValue = $btn.prev().val(); // Gets hidden element value
}
Docs
prev()
This should work!
You can pass it like,
<asp:HiddenField ID="hfsportsmanfeedid" runat="server" value='<%# DataBinder.Eval(Container.DataItem,"SportsmanFeedId") %>'/>
<asp:Button ID="btnLike" runat="server" Text="Like" OnClientClick="Test('<%# DataBinder.Eval(Container.DataItem,\"SportsmanFeedId\") %>'));" />
SCRIPT
function Test(value){
alert(value);
}
Or use prev() like,
<asp:Button ID="btnLike" runat="server" Text="Like" OnClientClick="Test(this));" />
SCRIPT
function Test(ths){
alert($(ths).prev().val());
}
Do it like this-
<asp:Button ID="btnLike" runat="server" Text="Like" OnClientClick="javascript:Test(document.getElementById('hfsportsmanfeedid').value);" />
get the value of hidden field from repeater rptGallary_ItemCommand event and pass that to java script
protected void rptGallary_ItemCommand(object source, RepeaterCommandEventArgs e)
{
//add command name to btnLike button let it bet test here
if (e.CommandName == "test")
{
HiddenField hiddenfield = (HiddenField)e.Item.Parent.Parent.FindControl("hfsportsmanfeedid");
//pass that to javascript
}
}

how to bind xml to gridview

This is my xml file
<ISPChecklist>
<Domain name="Coping Skills">
<Indicator>
Client shows impovement in either 1 of the areas listed below and shows reduction in frequency of inapporiate coping behaviors :
- anger management
- ability to make concrete plans about his/her future
- self percetion/self worth
- expand internal locus of control.
</Indicator>
<AttainmentDate></AttainmentDate>
<Remarks></Remarks>
</Domain>
</ISPChecklist>
This is my grid view
<asp:GridView ID="ISPChecklist" runat="server"
OnRowDataBound="OnDataBound"
onselectedindexchanged="ISPChecklist_SelectedIndexChanged">
<Columns>
<asp:BoundField HeaderText="Domain" DataField= "Domain" />
<asp:BoundField HeaderText="Indicator" DataField="Indicator" />
<asp:TemplateField HeaderText="Date Of Attainment">
<ItemTemplate>
<asp:TextBox runat="server" ID="TB_Date" Columns="5"></asp:TextBox>
</ItemTemplate>
</asp:TemplateField>
<asp:TemplateField HeaderText="Remarks">
<ItemTemplate>
<asp:TextBox runat="server" ID="TB_Remarks" Columns="5"></asp:TextBox>
</ItemTemplate>
</asp:TemplateField>
</Columns>
</asp:GridView>
This is my .cs file
DataSet checklistDataSet;
string filePath = Server.MapPath("clientISPChecklist.xml");
checklistDataSet = new DataSet();
//Read the contents of the XML file into the DataSet
checklistDataSet.ReadXml(filePath);
ISPChecklist.DataSource = checklistDataSet.Tables[0].DefaultView;
ISPChecklist.DataBind();//errors occurs here
Whenever i run this set of codes, i would receive an error message that states that "A field or property with the name 'Coping Skills' was not found on the selected data source". Does anyoen knows how to solve it? if not are there any tutorials or guides available
Use XMLDataSource and XPath() method.
I guess the Linq-XML is better option.
XDocument doc = XDocument.Load(MapPath("~/filename.xml"));
var result = from n in doc.Descendants("Domain")
select new
{
Domain = n.HasAttributes ? n.Attribute("name").Value : "",
Indicator=n.Element("Indicator").Value,
AttainmentDate = n.Element("AttainmentDate").Value,
Remark=n.Element("AttainmentDate").Value
};
ISPChecklist.DataSource = result.ToList();
ISPChecklist.DataBind();

Eval(), can only be used in the context of a databound control Error. Why it happens? What can I do about it?

I am getting the following error, Using asp.net and nHibernate.
Databinding methods such as Eval(), XPath(), and Bind() can only be used in the context of a databound control.
I have this listview
<asp:ListView ID="CurrentHourList" runat="server" ItemPlaceholderID="itemPlaceholder">
<LayoutTemplate>
<asp:PlaceHolder ID="itemPlaceholder" runat="server"></asp:PlaceHolder>
</LayoutTemplate>
<ItemTemplate>
<asp:ImageButton ID="DeleteDateButton" CausesValidation="false" runat="server" CommandName="Delete"
ImageUrl="img/delete.png" />
<span style="display: none">
<asp:Label ID="Id" runat="server" Text='<%# Eval("Id") %>'></asp:Label></span>
<asp:Label ID="Date" Text='<%# Eval("Date", "{0:dd MMM yyyy}") %>' runat="server"></asp:Label>
<asp:DropDownList ID="MedicalType" runat="server" SelectedValue='<%# Eval("MedicalType.Id") %>'>
<asp:ListItem Text="Paid" Value="1"></asp:ListItem>
<asp:ListItem Text="Unpaid" Value="2"></asp:ListItem>
<asp:ListItem Text="Special" Value="3"></asp:ListItem>
</asp:DropDownList>
Half-Day:<asp:CheckBox ID="HalfDay" runat="server" Checked='<%# Eval("IsHalfDay") %>' />
<br />
</ItemTemplate>
</asp:ListView>
and this in my code behind
private void BindData(string id)
{
MedLeaveHelper = new MedicalLeaveHelper();
DTO.MedLeaveDTO dto = MedLeaveHelper.GetMedicalRequest(id);
if (dto != null)
{
EnableForm();
this.RequestId.Text = dto.Id.ToString();
this.ddlEmployeeName.SelectedItem.Text = dto.User;
this.Note.Text = dto.Note;
this.CurrentHourList.DataSource = MedLeaveHelper.MakeMedicalDays(id);
this.CurrentHourList.DataBind();
}
MakeMedicalDays(id) simply looks like this. MedicalDays is a IList
internal IEnumerable MakeMedicalDays(string id)
{
int theId = 0;
if (int.TryParse(id, out theId))
{
MedicalRequest r = MedicalRequest.Get(theId);
return r.MedicalDays;
}
return null;
}
When I get to the DataBind() call the error shows up. I've poked around on the tubes but nothing concrete is jumping out at me. I've tried changing the syntax to something like this
DataBinder.Eval(Container.DataItem,"id")
and the error goes away but no data makes it into my ListView either.
as I understand it DataBinder.Eval uses reflection to determine my datatype, but I am not sure how to troubleshoot this issue. Any help you could provide would be great.
Thanks
Jim
Not sure if the problem is in your listview. I tried a simple repro using the following databinding code - which worked fine with the ASPX you included above:
this.CurrentHourList.DataSource = new[] { new { Id = 1, Date = DateTime.Now, MedicalType = new { Id = 1 }, IsHalfDay = false }, new { Id = 1, Date = DateTime.Now, MedicalType = new { Id = 1 }, IsHalfDay = false } };
this.CurrentHourList.DataBind();
Is it possible that the binding error is coming from a different control (ie. not inside of the listview?)
If you need to use declarative databinding on a control or property which doesn't support it, you can also look into building your own custom expression builder.

Controlling Checkboxes inside a Gridview Row in ASP.NET

Not really sure how to handle this issue but here goes...
I have a gridview with two checkboxes for each row, below is an example of the item template:
<ItemTemplate>
<asp:CheckBox ID="MasterCheckbox" runat="server"/>
<asp:CheckBox ID="ChildCheckbox" runat="server" />
</ItemTemplate>
I would like the 'enabled' property of the ChildCheckbox to be controlled by the 'Checked' property of the MasterCheckbox ... so in other words the ChildCheckbox is only enabled when the MasterCheckbox has been checked.
I know that I will need to append a handler onto the MasterCheckbox control to invoke some javascript to perform the necessary actions on the client-side - this will probably be done in the row_databound() method?
I can't quite figure out the javascript required to get this to work, so any hints/tips would be welcome.
Thanks
Dal
First you dont need to answer your own question,you can add comments into your very first question.
Since you are using GridView , I think you are binding something for MasterCheckBox,
so let's say that it's boolean value in a dataTable.
For Example it's a row contaning column with name IsMasterChecked
You can handle Enabling the other one with binding custom expressions as
<ItemTemplate>
<asp:CheckBox ID="MasterCheckbox" runat="server" />
<asp:CheckBox ID="ChildCheckbox" runat="server" Enabled='<%# Convert.ToBoolean(Eval("IsMasterChecked")) %>'/>
</ItemTemplate>
or
<ItemTemplate>
<asp:CheckBox ID="MasterCheckbox" runat="server" />
<asp:CheckBox ID="ChildCheckbox" runat="server" Enabled='<%# Convert.ToBoolean(Eval("IsMasterChecked")) ? "true" : "false" %>'/>
</ItemTemplate>
Hope this helps.
Off the top of my head, I think what you will have to do is something along the lines of the following...
<asp:TemplateField HeaderText="Checkbox">
<ItemTemplate>
<asp:CheckBox ID="MasterCheckbox" runat="server" AutoPostBack="true" OnCheckedChanged="checkGridViewChkBox" />
</ItemTemplate>
</asp:TemplateField>
With the following code behind.
CheckBox MasterCheckbox;
CheckBox ChildCheckbox;
private void checkGridViewChkBox()
{
int i;
int x = GridView1.Rows.Count;
for (i = 0; i < x; i++) //loop through rows
{
findControls(i);
if (MasterCheckbox.Checked)
{
ChildCheckbox.Enabled = true;
}else{
ChildCheckbox.Enabled = false;
}
}
}
private void findControls(int i)
{
MasterCheckbox = (CheckBox)(GridView1.Rows[i].FindControl("MasterCheckbox"));
ChildCheckbox = (CheckBox)(GridView1.Rows[i].FindControl("ChildCheckbox"));
}
It's not terribly efficient but works ok.

Categories