ListView DataBind Sorting Values - c#

I want to have a DropdownMenu from which i can choose how i want to sort my ListView.
This is my current code for it :
<asp:DropDownList ID="DropDownSelect" runat="server" AutoPostBack="True"
OnSelectedIndexChanged="GetProducts">
<asp:ListItem Selected="True" Value="DesDate"> Descending Date </asp:ListItem>
<asp:ListItem Value="AsDate"> Ascending Date </asp:ListItem>
<asp:ListItem Value="AsAlp"> Ascending Alphabetical </asp:ListItem>
<asp:ListItem Value="DesAlp"> Decentind Alphabetical </asp:ListItem>
</asp:DropDownList>
And I have this ListView to display my data:
<asp:ListView ID="productList" runat="server"
DataKeyNames="NewsID" GroupItemCount="1"
ItemType="SiteStiri.Models.News" SelectMethod="GetProducts">
<EmptyDataTemplate>
<table>
<tr>
<td>No data was returned.</td>
</tr>
</table>
</EmptyDataTemplate>
<EmptyItemTemplate>
<td/>
</EmptyItemTemplate>
<GroupTemplate>
<tr id="itemPlaceholderContainer" runat="server">
<td id="itemPlaceholder" runat="server"></td>
</tr>
</GroupTemplate>
<ItemTemplate>
<td runat="server">
<table>
<tr>
<td>
<a href="NewsDetails.aspx?newsID=<%#:Item.NewsID%>">
<img src="/Catalog/Images/Thumbs/<%#:Item.ImagePath%>"
width="100" height="75" style="border: solid" /></a>
</td>
</tr>
<tr>
<td>
<a href="NewsDetails.aspx?newsID=<%#:Item.NewsID%>">
<p style="color: black;">
<%#:Item.NewsTitle%>
</p>
</a>
</td>
</tr>
<tr>
<td> </td>
</tr>
</table>
</p>
</td>
</ItemTemplate>
<LayoutTemplate>
<table style="width:100%;">
<tbody>
<tr>
<td>
<table id="groupPlaceholderContainer" runat="server"
style="width:100%">
<tr id="groupPlaceholder"></tr>
</table>
</td>
</tr>
<tr>
<td></td>
</tr>
<tr></tr>
</tbody>
</table>
</LayoutTemplate>
</asp:ListView>
The thing that i have no idea how to do is:
After selecting a sorting rule from the dropdown menu, i can't figure out how to write(or where to write) the method that would update my ListView as it should. My attemp is :
public IQueryable<News> GetProducts()
{
var _db = new SiteStiri.Models.NewsContext();
IQueryable<News> query = _db.News;
if (("DesDate").Equals(DropDownSelect.SelectedItem.Value))
{
query.OrderByDescending(u => u.ReleaseDate);
}
if (("AsDate").Equals(DropDownSelect.SelectedItem.Value))
{
query.OrderBy(u => u.ReleaseDate);
}
if (("AsAlp").Equals(DropDownSelect.SelectedItem.Value))
{
query.OrderBy(u => u.NewsTitle);
}
if (("DesApl").Equals(DropDownSelect.SelectedItem.Value))
{
query.OrderByDescending(u => u.NewsTitle);
}
return query;
}
which gives me a bunch of errors and it doesn't even work .... a little bit of help please? I am new to this (2 days).

Let's fix your code step by step.
Event handlers need to have a certain signature. In case of every ASP.NET control I can remember of, they need to receive two parameters, event arguments and event source object, and return void. Also note that just calling GetProduct is not going to update ListView, we need to trigger databinding for the control itself. We'll get to that later. For now let's introduce a proper handler:
public void DropDownSelect_SelectedIndexChanged(object sender, EventArgs e)
Don't forget to update markup as well:
OnSelectedIndexChanged="DropDownSelect_SelectedIndexChanged"
The conditions on how you show data in the ListView have changed. That means that you need to rebind it with calling DataBind, which should call GetProducts (as a one specified in SelectMethod):
public void DropDownSelect_SelectedIndexChanged(object sender, EventArgs e)
{
productList.DataBind();
}
Finally in GetProducts note that LINQ calls do not update the current object, but rather they produce new one every time. So you should have something like this:
if ("DesDate".Equals(DropDownSelect.SelectedItem.Value))
{
query = query.OrderByDescending(u => u.ReleaseDate);
}

I am guessing you want the data and not the query right? That code would look like this, all you have to do is parse the selected value and pass it into the method. If the ListView is bound to the data (and it should be) you're all set. This example shows the separation of concerns, whereby the view is just a framework for HOW to show the data, and the code behind is a framework for WHAT data is supplied based on triggers from the view. The trigger in this case is the "filter" parameter.
List<News> query = _db.News.ToList();
public List<News> GetProduct(string filter) {
if (filter == "DesDate") return query.OrderByDescending(u => ReleaseDate).ToList();
}
One last note: IQueryable is a class that lets you pass queries around, you can alter the query itself from method to method. But this class does not produce results until it is told to do so by using any of the To methods like ToList(), ToArray(), ToLookup() and or any attempt to iterate over it such as:
Foreach(var thing in MyQuerable){ //results are here }

Related

Using Tuple to create a table structure

I have a List of tuple that i create in the following format
List<Tuple<String, String, String>> ExtendedSpecsList = new List<Tuple<String, String, String>>();
Theinputs to these tuple would be
{Audio, Type, Speakers - stereo - internal }
{Audio, Output Power / Channel, 2 Watt }
{General, Display Type, LCD monitor / TFT active matrix }
{General, Diagonal Size, 23.6" }
Based on this i am trying to create the following HTML structure for each top category/ Item1 of the tuple. Howe can i do this. Or should i use some other process other than tuple to do this...
<div class="ccs-ds-extendedSpec-group">
<div class="ccs-ds-extendedSpec-header">Audio</div>
<div class="ccs-ds-extendedSpec-body">
<table>
<tbody>
<tr>
<td class="ccs-ds-extendedSpec-item">Type</td>
<td class="ccs-ds-extendedSpec-value">Speakers - stereo - internal</td>
</tr>
<tr>
<td class="ccs-ds-extendedSpec-item" >Output Power/Channel</td>
<td class="ccs-ds-extendedSpec-value" >2 Watt</td>
</tr>
</tbody>
</table>
</div>
</div>
So the first issue is to have the structure of your data match how you want to display it. You want your data grouped on the first value, so you need to do that before binding the data:
var query = yourOriginalData.GroupBy(item => item.Item1)
.Select(group => new { Category = group.Key, Items = group });
With only that change it's ready to be bound to something that can render it.
To render it, a Repeater would be the right choice. Here, since you have a sub-collection within your main collection you'll need nested repeaters:
<asp:Repeater runat="server" ID="repeater">
<ItemTemplate>
<div class="ccs-ds-extendedSpec-group">
<div class="ccs-ds-extendedSpec-header"><%# Eval("Category") %></div>
<div class="ccs-ds-extendedSpec-body">
<table>
<tbody>
<asp:Repeater runat="server" DataSource='<%# Eval("Items") %>'>
<ItemTemplate>
<tr>
<td class="ccs-ds-extendedSpec-item">
<%# Eval("Item2") %>
</td>
<td class="ccs-ds-extendedSpec-value">
<%# Eval("Item3") %>
</td>
</tr>
</ItemTemplate>
</asp:Repeater>
</tbody>
</table>
</div>
</div>
</ItemTemplate>
</asp:Repeater>
With that you only need to bind your query to this repeater and you're golden.
It's also worth noting that while Tuple certainly works here, the readability of the code would be improved dramatically by using a class that has meaningful property names.
You could use Linq to filter ExtendedSpecsList by the first component as follows.
var Audio = ExtendedSpecList.Where(iItem => iItem.First == "Audio");
var General = ExtendedSpecList.Where(iItem => iItem.First == "General");
Afterwards the different categories can be processed seperately. If the different Categories are not known beforehand, the list can be grouped as follows.
foreach(var Group in ExtendedSpecList.GroupBy(iItem => iItem.First))
{
// do some processing of the group
foreach (var iItem in Group)
{
// do something with an individual item
}
}

Use ListView control to display data but join a reference table

I'm really new to ASP.NET and I've looked at countless tutorials trying to figure this out but can't seem to get my head around it. I can successfully output records from tbProject using ListView and the Entity Framework but instead of the refDepartmentID, I want to display refDepartmentValue from the refDepartment table. Here is a quick table structure.
tbProject
-ProjectID
-refDepartmentID
-ProjectName
refDepartment
-refDepartmentID
-refDeparmentValue
I can write this in SQL no problem with a JOIN but I don't know where I'd put it. I've played around with LINQ a bit and it seems like this is how I'm going to accomplish this. Maybe write the LINQ in the code behind file then bind it to the ListView control, maybe?
Here's the ListView control:
<asp:ListView runat="server"
DataSourceID="EntityDataSource1">
<ItemTemplate>
<tr style="">
<td>
<asp:Label ID="ProjectIDLabel" runat="server" Text='<%# Eval("ProjectID") %>' />
</td>
<td>
<asp:Label ID="refDepartmentIDLabel" runat="server"
Text='<%# Eval("refDepartmentID") %>' />
</td>
</tr>
</ItemTemplate>
<LayoutTemplate>
<table runat="server">
<tr runat="server">
<td runat="server">
<table ID="itemPlaceholderContainer" runat="server" border="0" style="">
<tr runat="server" style="">
<th runat="server">
ProjectID</th>
<th runat="server">
refDepartmentID</th>
</tr>
<tr ID="itemPlaceholder" runat="server">
</tr>
</table>
</td>
</tr>
<tr runat="server">
<td runat="server" style="">
</td>
</tr>
</table>
</LayoutTemplate>
</asp:ListView>
Here's how I'm using the EF to populate a dropdownlist so you can at least refer to how things are named.
// populates Departments dropdownlist
using (dbOrganizationEntities1 myEntities = new dbOrganizationEntities1())
{
var allDepartments = from refDepartments in myEntities.refDepartments
select refDepartments;
ddlDepartments.DataSource = allDepartments;
ddlDepartments.DataValueField = "refDepartmentID";
ddlDepartments.DataTextField = "refDepartmentValue";
ddlDepartments.DataBind();
}
Any help would be appreciated. Thanks!
Disclaimer, I did this in my head without Visual Studio, so it might have a minor syntax error. Let me know if it does.
You need to do two things, First you must join your tbProject to refDepartments Entites using an inner (or outer) join as needed, depending on your desired behavior. One you have the two tables joined in LINQ, you want to create a new anonymous class using "select new" operator which creates it. The members of this new class will be created based on the datatypes of the Entities they are selected from.
using (dbOrganizationEntities1 myEntities = new dbOrganizationEntities1())
{
var allDepartments = (from tbProject in myEntities.tbProjects
// inner join to department lookup table
from refDepartments in myEntities.refDepartments.Where(x=>x.refDepartmentID == tbProject.refDepartmentID) // to do a left join instead of an inner, append .DefaultIfEmpty() after this where clause
// select new anon type
select new {
refDepartmentID = tbProject.refDepartmentID,
ProjectName = tbProject.ProjectName,
refDepartmentValue = refDepartments.refDepartmentValue,
}).ToList(); // I chose to turn the result into a list to demonstrate something below, you can leave it as an enumerable.
// you can access the properties of the anon type like so
System.Diagnostics.Debug.Print(allDepartments[0].refDepartmentID);
System.Diagnostics.Debug.Print(allDepartments[0].refDepartmentValue);
// bind to your listview, make sure control name is accurate and ItemTemplates are defined for each data column.
MyListView.DataSource = allDepartments;
MyListView.DataBind();
}

textbox not displaying in code behind c# file

I have a sales simulator written in C#. I did not write this however I am currently modifying it to suit different requirements.
It basically displays a list of products from a database, and has a textbox for each product where you can enter in the quantity sold. The quantity sold is calculated based on it's price in the database.
Now it's all working fine, however there is one SMALL issue. When "Buy" is clicked, it returns me back to the list of products which IS correct, however the quantity I have entered for the previous product remains.
I would like to know how to restore the text boxes to their default value when the after the data is submitted to the database.
I always thought the way to do this would be in the .cs code behind file
txtQuantity.Text = "";
However, for some odd reason, txtQuantity will not show up.
Can anyone think of anything I am doing wrong? Here is a snippet of code from the aspx file.
<form id="form1" runat="server">
Date:
<asp:TextBox ID="txtDate" runat="server" />
Retailer:
<asp:DropDownList ID="dlStore" runat="server"
onselectedindexchanged="dlStore_SelectedIndexChanged" />
<asp:ListView ID="lbProducts" runat="server">
<LayoutTemplate>
<layouttemplate>
<table border="1" cellpadding="1" style="width:800px">
<tr style="background-color:#E5E5FE">
<th>ID</th>
<th>ProductCode</th>
<th>Product Title</th>
<th>RRP $</th>
<th>Quantity</th>
<th>Sale Price $</th>
</tr>
<tr id="itemPlaceholder" runat="server"></tr>
</table>
</layouttemplate>
</LayoutTemplate>
<ItemTemplate>
<tr>
<td style="width: 50px;">
<%#Eval("ProductID") %>
</td>
<td>
<%#Eval("ProductCode") %>
</td>
<td>
<%#Eval("ProductTitle") %>
</td>
<td>
<%#Eval("USListPrice") %>
</td>
<td style="width: 50px;">
<asp:TextBox ID="txtQuantity" runat="server" Text="0" />
</td>
<td style="width: 50px;">
<asp:TextBox ID="txtSalePrice" runat="server" Text="0.00" />
</td>
</tr>
</ItemTemplate>
</asp:ListView>
<asp:Button ID="btnBuy" Text="Buy These Items" runat="server" OnClick="btnBuy_Click" />
<asp:Button ID="btnClear" Text="Clear Existing Sales" runat="server"
onclick="btnClear_Click" />
</form>
There's a lot going on in the code behind file and I wouldn't expect anyone to go through it, but how I collect the data from txtQuantity is done with the following line of code:
Int32 quantity = Int32.Parse(((TextBox)item.FindControl("txtQuantity")).Text);
So what I want to be able to do is set this textbox either to be empty, or back to zero.
Any help is appreciated, thanks.
Because that txtQuantity control is within a ListView, there could be any number of instances of that control generated. So you can't access all of them through a single variable.
You will need to look through all controls within that ListView (and several levels deep) to find all your txtQuantity controls.
The same of course for the txtSalePrice control.
EDIT
You could find those textboxes with code like (untested)
public IEnumerable<TextBox> FindTextBoxes(Control parent)
{
if (parent == null) yield break;
foreach (Control child in parent.Controls)
{
TextBox tb = child as TextBox;
if (tb != null)
yield return tb; // found one!
else
foreach(TextBox tb in FindTextBoxes(child))
yield return tb; // found it deeper
}
}
and call it like:
foreach(TextBox tb in FindTextBoxes(lbProducts)
{
if (tb.Name == "txtQuantity")
{
// found a quantity
}
else if (tb.Name == "txtSalePrice")
{
// found the salesprice
}
}

How can I set a table row color in my repeater based on the values databound to that row in ASP.NET?

I have a repeater control:
<table style="width: 100%">
<asp:Repeater runat="server" ID="rptOptions" OnItemDataBound="rptOptions_ItemDataBound">
<HeaderTemplate>
<tr>
<td class="GridHeader">Account</td>
<td class="GridHeader">Margin</td>
<td class="GridHeader">Symbol</td>
<td class="GridHeader">Price</td>
</tr>
</HeaderTemplate>
<ItemTemplate>
<tr>
<td class="GridRow"><asp:Label runat="server" ID="lblOptionAccount"></asp:Label></td>
<td class="GridRow"><asp:Label runat="server" ID="lblOptionMargin"></asp:Label></td>
<td class="GridRow"><asp:Label runat="server" ID="lblOptionSymbol"></asp:Label></td>
<td class="GridRow"><asp:Label runat="server" ID="lblOptionPrice"></asp:Label></td>
</tr>
</ItemTemplate>
</asp:Repeater>
</table>
And the following code-behind databound method:
protected void rptOptions_ItemDataBound(object sender, RepeaterItemEventArgs e)
{
if (e.Item.ItemType == ListItemType.Item || e.Item.ItemType == ListItemType.AlternatingItem)
{
Option rowOption = (Option)e.Item.DataItem;
((Label)e.Item.FindControl("lblOptionAccount")).Text = rowOption.Account;
((Label)e.Item.FindControl("lblOptionMargin")).Text = rowOption.Margin ? "Y" : "N";
((Label)e.Item.FindControl("lblOptionSymbol")).Text = rowOption.Symbol;
((Label)e.Item.FindControl("lblOptionPrice")).Text = rowOption.Price.ToString("C", currencyFormat);
}
}
There are more columns in that grid, but I've slimmed it down just for the question.
Now, what I would like to do is change the tr's background color based on the price amount. If it is within different levels, I would like to change the rows background color correspondingly.
Do I have to do this with javascript or is there some way I can get access to the table rows in the code-behind to set this color?
make it to runat="Server"
<tr runat="server" ID="trHeader"></tr>
Then find that Table Row in by ID in your code behind in databound event, like what you are doing for other server side control and change color.
another option:
<tr class="<%# GetClassForPrice( Container.DataItem ) %>">
and in code-behind
protected string GetClassForPrice( object data )
{
var rowOption = (Option)data;
if(rowOption.Price > 100) return "red";
else return "green";
}
also... any reason you're not using data binding? it would let you eliminate your ItemDataBound code-behind method.
<tr>
<td class="GridRow"><%# Eval("Account") %></td>
<td class="GridRow"><%# ((bool)Eval("Margin")) ? "Y" : "N" %></td>
<td class="GridRow"><%# Eval("Symbol") %></td>
<td class="GridRow"><%# Eval("Price", "{0:c}") %></td>
</tr>
use this code inside repeter databound event.
HtmlTableRow tr = (HtmlTableRow)e.Item.FindControl("trID");
tr.Visible = false;
Another option you can use is jQuery. Seeing you're using a repeater, that gives you full control over your output. Jquery can get into your table, look at the data and format it how you want it.
Look at:
http://plugins.jquery.com/project/Plugins/category/54
http://plugins.jquery.com/project/Colorize
http://franca.exofire.net/js/demo_cross.html
Alternately you can use the Code behind in the repeater to set the CSS Class of the cells/rows/columns you need changed.
You'll need to make those controls server controls (require: ID="my_thing" runat="server"), create those controls (use the find control and bind them), then set the CSS class after you determine the value.
Dave Thieben's answer works nicely, except that the css is missing.
If you add the following then the example will work:
table tr.red td { background-color:red; }
table tr.green td { background-color:green; }

Optimize asp.net C# code for repeaters using a table

On the aspx:
<table>
<tr>
<asp:Repeater ID="rptHeader" runat="server">
<ItemTemplate>
<th><%#Eval("Category")%></th>
</ItemTemplate>
</asp:Repeater>
</tr>
<tr>
<asp:Repeater ID="rptContents" runat="server">
<ItemTemplate>
<td valign="top">
<%#Eval("Content")%>
</td>
</ItemTemplate>
</asp:Repeater>
</tr>
</table>
On the code-behind:
protected void Page_Load(object sender, EventArgs e)
{
rptHeader.DataSource = DataSource;
rptHeader.DataBind();
rptContentBlocks.DataSource = DataSource;
rptContentBlocks.DataBind();
}
The problem here is that instead of using Two repeaters, can we use only one?
We actually need the header to be separated from the contents using a different table row...
Edit: changed rptHeader's ItemTemplate's html element from <td> to <th> to be a little clearer. :-)
IMO it's impossible without repeater hacking. Anyway, there is a rather irregular approach that maybe works. Also you can use two foreach statements instead of repeaters.
Code
protected string[] headers = { "A", "B", "C", "D" };
protected string[] contents = { "Alpha", "Beta", "Counter", "Door" };
//string[] headers = { "A", "B", "C", "D" };
//string[] contents = { "Alpha", "Beta", "Counter", "Door" };
//DataList1.RepeatColumns = headers.Length;
//DataList1.DataSource = headers.Concat(contents);
//DataList1.DataBind();
Html markup
<table>
<tr>
<%foreach (string item in headers)
{ %>
<th><%= item %></th>
<% } %>
</tr>
<tr>
<%foreach (string item in contents)
{ %>
<td><%= item %></td>
<% } %>
</tr>
</table>
<!--
<asp:DataList ID="DataList1" runat="server" RepeatDirection="Horizontal">
<ItemTemplate>
<%# Container.DataItem %>
</ItemTemplate>
</asp:DataList>
-->
An HTML table is always declared as cells nested within rows, i.e.
<table>
<tr>
<td>
...
</td>
</tr>
</table>
rather than
<table>
<td>
<tr>
...
</tr>
</td>
</table>
This means you won't be able to write a single Category followed by a single Content. You will have to repeate the Category values and then the Content values as you are already doing.
I see nothing wrong with the approach you are using. The use of 2 repeaters rather than one should be a negligible overhead.
The alternative would be to nest a table within each column. This would allow you to use just a single repeater but the resulting HTML would be rather contrived and bloated. I would not recommend this approach but somehow I can't stop myself from providing an example.
<table>
<tr>
<asp:Repeater ID="rptHeader" runat="server">
<ItemTemplate>
<td>
<table>
<tr>
<td valign="top">
<strong><%#Eval("Category")%></strong>
</td>
</tr>
<tr>
<td valign="top">
<%#Eval("Content")%>
</td>
</tr>
</table>
</td>
</ItemTemplate>
</asp:Repeater>
</tr>
</table>
If it's important to place the content in a table, then you could "rotate" your table into a different structure and bind on that structure. Or if it's not important that the data is in a table, you could place the items in divs and float them so they're side-by-side.
Edit:
Here's what I mean by rotating the data. If your data is currently in a structure like List<MyDataClass>, where MyDataClass is defined as something like:
class MyDataClass
{
public string Category { get; set; }
public string Content { get; set; }
public int OtherField { get; set; }
}
...then the logical layout when iterating through the structure would look like this:
MyDataClass[0]: Category, Content, OtherField
MyDataClass[1]: Category, Content, OtherField
...
When rotating the data, you'd turn those rows into columns and columns into rows. For example, you could populate a List<List<string>> with code such as this:
var rotated = new List<List<string>> {
new List<string>(), new List<string>(), new List<string>(),
};
for each (MyDataClass object in myCollection)
{
rotated[0].Add(object.Category);
rotated[1].Add(object.Content);
rotated[2].Add(object.OtherField.ToString("n0"));
}
Now your data structure looks like this:
rotated[0]: MyDataClass[0].Category, MyDataClass[1].Category, ...
rotated[1]: MyDataClass[0].Content, MyDataClass[1].Content, ...
rotated[2]: MyDataClass[0].OtherField, MyDataClass[1].OtherField, ...
Basically, you transform the data into a form that can be dropped right into your table.
Edit 2:
I actually have to do these sorts of rotations for reports often enough that I made an extension method for IEnumerable that will do the rotation in a somewhat more elegant manner. Here's the extension method:
public static class MyCollectionExtensionMethods
{
public static IEnumerable<IEnumerable<TResult>> Rotate<TOrig, TResult>(
this IEnumerable<TOrig> collection,
params Func<TOrig, TResult>[] valueSelectors)
{
return valueSelectors.Select(s => collection.Select(i => s(i)));
}
}
And here's how I'd use it:
IEnumerable<IEnumerable<string>> rotated = data.Rotate(
i => i.Category, i => i.Content, i => i.OtherField.ToString("n0"))
Correct me if i misunderstood but it sounds to me like you could use only one repeater, integrating your first repeater into a HeaderTeplate tag and the second into the ItemTemplate.

Categories