nested dictionary to nested repeater asp.net c# - asp.net

I'm making an asp.page that will display hierarchical information about company assets.
To grab the data I used a lambda expression:
FASAssetInfoDataContext fasInfo = new FASAssetInfoDataContext();
var data = from g in fasInfo.Asset_Informations
where g.Department.Substring(0, 3) == p
select g;
Dictionary<string, Dictionary<string, List<info>>> branch = data.GroupBy(e => e.Location)
.ToDictionary(g => g.Key,
g => g.GroupBy(gl => gl.G_L_Asset_Acct_No)
.ToDictionary(gl => gl.Key,
gl => gl.Select(s => new info
{
acqDate = s.Acquisition_Date,
asstNo = s.Co_Asset_Number,
classInfo = s.Class,
description = s.Description,
mfgSerialNo = s.Mfg_Serial_No,
deprThisRun = s.Depr_This_Run__Int_,
AcqValue = s.Acquisition_Value__Int_,
currentAccDepr = s.Current_Accum_Depr__Int_,
estLife = s.Est_Life__YYMM___Int_,
inServiceDate = s.Placed_In_Service_Date__Int_,
netBookValue = s.Current_Net_Book_Value__Int_,
oldAcqValue = s.Acquisition_Value__Tax_
}).ToList()));
So I now have a nested set of dictionaries with a list of information at the end. My question is how do I best display this information on the page itself? I can get the first level but have been struggling to get the nested repeater to function properly. If there is a better control to use I'm all ears :)
Thanks,
Marco

If you do indeed need to use nested repeaters, its possible, but getting it working isn't particularly obvious. The following would work (simplified for brevity):
<asp:Repeater id="level1" OnItemDataBound="level1_ItemDataBound" ...>
<ItemTemplate>
<asp:Repeater id="level2" OnItemDataBound="level2_ItemDataBound" ...>
<ItemTemplate>
<asp:Repeater id="level3" OnItemDataBound="level3_ItemDataBound" ...>
</asp:Repeater>
</ItemTemplate>
</asp:Repeater>
</ItemTemplate>
</asp:Repeater>
Code behind:
protected void Page_Load(object sender, EventArgs e)
{
Dictionary<string, Dictionary<string, List<info>>> branch = ...;
level1.DataSource = branch;
level1.DataBind();
}
protected void level1_ItemDataBound(object sender, RepeaterItemEventArgs e)
{
Dictionary<string, List<info>> data = (Dictionary<string, List<info>>)e.Item.DataItem;
Repeater level2 = e.Item.FindControl("level2") as Repeater;
level2.DataSource = data;
level2.DataBind();
}
protected void level2_ItemDataBound(object sender, RepeaterItemEventArgs e)
{
List<info> data = (List<info>)e.Item.DataItem;
Repeater level3 = e.Item.FindControl("level3") as Repeater;
level3.DataSource = data;
level3.DataBind();
}
protected void level3_ItemDataBound(object sender, RepeaterItemEventArgs e)
{
// Bind properties from info elements in List<info> here
}

There's an nicer way of doing this without any ItemDataBound events. For simplicity let's assume two level of repeaters.
<asp:Repeater id="level1" >
<ItemTemplate>
<asp:Repeater id="level2"
DataSource='<%# ((System.Collections.Generic.KeyValuePair<string,System.Collections.Generic.List<string>>)Container.DataItem).Value %>' >
<ItemTemplate>
<%# Container.DataItem %>
</ItemTemplate>
</asp:Repeater>
</ItemTemplate>
</asp:Repeater>
I usually prefer this way, since for some reason I hate ItemDataBound events :)

asp:TreeView? Not sure how it will handle the nested data set but it's designed to display it.

Related

Repeater does not show anything after second set of data binding

I have the following repeater inside an ASCX file:
<asp:Repeater ID="repeater1" runat="server">
<HeaderTemplate>
<ul class="formList">
</HeaderTemplate>
<ItemTemplate>
<li><a href="plugins/umbracocontour/editForm.aspx?guid=<%# ((Umbraco.Forms.Core.Form)Container.DataItem).Id %>"
class="form">
<%# ((Umbraco.Forms.Core.Form)Container.DataItem).Name %></a> <small><a href="plugins/umbracocontour/editForm.aspx?guid=<%# ((Umbraco.Forms.Core.Form)Container.DataItem).Id %>">
Open form designer</a> <a href="plugins/umbracocontour/editFormEntries.aspx?guid=<%# ((Umbraco.Forms.Core.Form)Container.DataItem).Id %>">
View entries</a> </small></li>
</ItemTemplate>
<FooterTemplate>
</ul>
</FooterTemplate>
</asp:Repeater>
It is populated with data via a method named ShowAllForms()
private void ShowAllForms()
{
using (var formStorage = new FormStorage())
{
var list = formStorage.GetAllForms(false).OrderBy(f => f.Name).Where(form => Security.HasAccessToForm(form.Id)).ToList();
this.repeater1.DataSource = list;
this.repeater1.DataBind();
if (list.Count == 0)
{
this.paneBrowse.Visible = false;
}
}
}
and `SearchForms:
private void SearchForms()
{
var forms =
this.formRepository.GetFormByFreeText(this.txtFormSearch.Text).Where(form => Security.HasAccessToForm(form.Id)).
ToList();
this.repeater1.DataSource = forms;
this.repeater1.DataBind();
}
ShowAllForms() is called in Page_Load, if there has not been a postback, and also in the click event for the "Show All Forms" button. SearchForms() is called in the postback for the "Search Forms" button.
As a non-admin user, when I view the list of forms I see a whole bunch of them initially. I then search for a form via it's name which should return no items. So far so good. I then click the "Show All Forms" button which will execute ShowAllForms(), which is what was called to show all the forms in the first place.
However, when called a second time, ShowAllForms() does not show any form data in the repeater. To clarify, I can see that there are items returned in the forms variable, so the collection is not empty, and yet none of those items appear in the repeater.
I'm puzzled as to what could be going on here.
EDIT:
OnLoad event for the page:
protected void Page_Load(object sender, EventArgs e)
{
if (!this.IsPostBack)
{
this.ShowAllForms();
}
}
OnInit:
protected void Page_Init(object sender, EventArgs e)
{
this.formRepository = TinyIoC.TinyIoCContainer.Current.Resolve<IFormRepository>();
}
I believe the problem is here:
if (list.Count == 0)
{
this.paneBrowse.Visible = false;
}
I don't see where you set paneBrowse's visiblity to true again.
A quick fix would be:
this.paneBrowse.Visible == (list.Count > 0);

can't get values from items on dropdownlist

I have this in my page:
<asp:DropDownList ID="membros" runat="server">
</asp:DropDownList>
and in the code behind, I have this:
protected void Page_Load(object sender, EventArgs e)
{
members.Items.Clear();
members.Items.Add(new ListItem("Choose...", ""));
foreach (DataRow item in com.Execute("select * from tableMembers").Rows)
{
members.Items.Add(new ListItem(item["name"].ToString(), item["id_user"].ToString()));
contForeach++;
}
}
and when I try to get the value from SelectedIndex I can't because he always get me 0 independently of what index I choose.
put that in if(!IsPostBack) so that it can load the dropdown at first time the form loads,right now it is loading everytime the page loads so your missing your selection.
if(!IsPostBack)
{
members.Items.Clear();
members.Items.Add(new ListItem("Choose...", ""));
foreach (DataRow item in com.Execute("select * from tableMembers").Rows)
{
members.Items.Add(new ListItem(item["name"].ToString(), item["id_user"].ToString()));
contForeach++;
}
}
Apart from the IsPostBack mentioned, you should be doing it this way instead of looping through the data
Modify your markup
<asp:DropDownList ID="membros" runat="server" DataTextField="name" DataValueField="user_id">
From code behind, bind the data
if(!IsPostBack)
{
DataTable dt = com.Execute("select * from tableMembers");
members.DataSource = dt;
members.DataBind();
members.Items.Insert(0, new ListItem("Choose...", ""));
}
Bound controls like DropdownList, GridView, DataList etc have DataSource property that you can use to assign a collection to them and they would loop through it and extract the data.

Managing images in asp.net repeater (add class to each picture)

This is my code:
Codebehind:
public class data
....
...
public List<dataImages> Images { get; set; }
...
var data= GarageBLL.LoadData(Convert.ToInt32(DataId), Convert.ToInt32(MemberId));
rptImages.DataSource = data.Images.Take(3);
rptImages.DataBind();
aspx:
<asp:Repeater runat="server" ID="rptImages">
<ItemTemplate>
<asp:Image runat="server" CssClass="img1" ImageUrl='<%# String.Format("/images/{0}/{1}.{2}", DataId, Eval("ImageId"), Eval("Extension")) %>' />
</ItemTemplate>
</asp:Repeater>
So, this works at the moment, but i would like to add different classes to each image.
Anyone got any idea how i can do that?
You can use repeater's ItemDataBound event to set the CssClass:
protected void rptImages_ItemDataBound(Object Sender, RepeaterItemEventArgs e) {
// This event is raised for the header, the footer, separators, and items.
// Execute the following logic for Items and Alternating Items.
if (e.Item.ItemType == ListItemType.Item || e.Item.ItemType == ListItemType.AlternatingItem) {
dataImages img = (dataImages) e.Item.DataItem;
Image img1 = (Image) e.Item.FindControl("img1");
// add your correct logic here according to the dataImages properties
img.CssClass = "YourCssClass";
// assuming you just want different classes for your three images, use ItemIndex with remainder:
string class = "img" + (e.Item.ItemIndex % 3 + 1).ToString() + "class";
img.CssClass = class;
}
}

getting wrong value in dropdownlist in code behind

I've read a few different examples of this, and it seems to be a clear newbie question for those that don't fully understand the asp.net page life cycle, sorry still learning. None of my attempts to fix have panned out.
aspx:
...
<%
for( int j = 0; j < 11; j++)
{
ChildWeightPounds.Items.Add( new ListItem(j.ToString(),j.ToString()));
}
%>
<asp:DropDownList ID="ChildWeightPounds" runat="server" OnSelectedIndexChanged="DropDownListSelected">
<asp:ListItem Value="">-Select-</asp:ListItem>
</asp:DropDownList>
...
<asp:Button ID="InsertButton" runat="server" Text="Submit" OnClick="InsertButton_Click" />
aspx.cs:
protected void InsertButton_Click(object sender, EventArgs e)
{
foreach (Control c in NewPatientForm.Controls)
{
....
if (c is TextBox)
{
TextBox tb = (TextBox)c;
//Expected response:
Response.Write( "field: " + tb.ID + " = " + tb.Text + "<br />");
}
if (c is DropDownList)
{
DropDownList ddl = (DropDownList)c;
//Unexpected response:
//this is not giving me my selected value, but only the top item ("--select--")
Response.Write("field: " + ddl.ID + ", selectedItem: " + ddl.SelectedItem.Value + "<br />");
}
}
}
It's pretty clear this is a IsPostBack, DataBind(), problem with my lack of understanding of the page life cycle. But what doesn't make sense, is i'm iterating through all controls, and textboxes, checkboxes, checkboxlists all work fine, and give me the value in the field, for some reason the dropdownlist doesn't give me the value.
I've tried using the OnSelectedIndexChanged event, I've tried using the DataBind() function, but playing with these, still hasn't gotten me the value.
The biggest issue with your example is you are using inline C# within your page with <% %>. This isn't advised for asp.net. That's more of a legacy/classic ASP approach which won't play well with .NET for many reasons.
Try moving your code that adds the items to the dropdownlist from the markup file into the .cs file, and be sure to hook into a page event that happens at or before OnPreRender. That is the last point you can alter the page controls and have viewstate/lifecycle work correctly.
protected void Page_Load(object sender, EventArgs e)
{
for( int j = 0; j < 11; j++)
{
ChildWeightPounds.Items.Add( new ListItem(j.ToString(),j.ToString()));
}
}
It's likely without running your example that the values are being inserted into the dropdownlist at the incorrect time in the lifecycle, and because of that when you try to access the selected value in the code behind it doesn't work.
Consider the following article on asp.net lifecycle which may assist you.
You can adjust AutoPostBack="true" on your DropDownList, and define OnSelecIndexChanged event
<asp:DropDownList ID="ChildWeightPounds" runat="server"
OnSelectedIndexChanged="DropDownListSelected" AutoPostBack="true>
<asp:ListItem Value="">-Select-</asp:ListItem>
</asp:DropDownList>
Code Behind
void DropDownListSelected(Object sender, EventArgs e)
{
var value = ChildWeightPounds.SelectedValue;
}

Calls to successive calls on repeater with XML datasource is not binding new data

I have the following Page_Load function...
protected void Page_Load(object sender, EventArgs e)
{
XmlDataSource1.Data = GetXmlFromFile(Request.QueryString["file"]);
XmlDataSource1.DataBind();
Repeater1.DataBind();
}
The page in which this Page_Load resides is called by a parent page. Each time this Page_Load gets called the value of "file" in the query string will be different. So each time I will be receiving the XML contents from a different file which are inserted into the XmlDataSource and then bound against by the Repeater. Getting the XML data from the file works great but when I bind against the repeater that only works the first time through Page_Load. Each time after that when this code is executed the same results from the first XML file are displayed in the repeater.
What am I missing here. How can I get the XML data to be bound against the repeater on each page load instead of just the first one?
Tried creating a new XmlDataSource on each load?
protected void Page_Load(object sender, EventArgs e)
{
XmlDataSource source = new XmlDataSource();
source.ID = "MyDS";
source.Data = GetXmlFromFile(Request.QueryString["file"]);
source.DataBind();
Repeater1.DataSource = source;
Repeater1.DataBind();
}
I tried to duplicate the problem with a simple implementation that follows your main concept.
Code behind:
protected void Page_Load(object sender, EventArgs e)
{
if (Request.QueryString["file"] != null)
{
XmlDataSource XmlDataSource1 = new XmlDataSource();
XmlDataSource1.DataFile = "~/App_Data/" + Request.QueryString["file"];
Repeater1.DataSource = XmlDataSource1;
Repeater1.DataBind();
}
}
Markup:
<asp:Repeater runat="server" ID="Repeater1" DataSourceID="XmlDataSource1">
<ItemTemplate>
<asp:Label runat="server" ID="lbl" Text='<%# Eval("Name") %>'></asp:Label>
</ItemTemplate>
</asp:Repeater>
This works when I browse to "Default.aspx?file=West.xml" where west.xml is an xml file in my App_Data folder. When I enter another existing xml file in the query string, it performs as expected. It's quick n' dirty, but maybe it will give you some idea when comparing it against your existing code.
On further investigation turns out the problem is a caching flag which is set to default true on XmlDataSource's. Other data source types like SqlDataSource do not have this flag enabled.
This is more fully outlined in the following article http://geekswithblogs.net/ranganh/archive/2006/04/28/76638.aspx
Upon disabling the caching feature everything works as expected.

Resources