Best way to handle table with dynamic columns - c#

I usually deal with Repeater to generate tables in my asp.net pages, but now I need to handle dynamic columns in my table, so I wonder if there is a common approach to solve this issue using web controls.
I've never used GridView, so I don't know if this is better to render tables with dynamic columns? Can you suggest me wich is the more appropriate approach? Is there a way to achieve this using Repeater?

You could a GridView with AutoGenerateColumns set to true. It would inspect and add the columns in your DataSource
<asp:sqldatasource id="CustomersSource"
selectcommand="SELECT CustomerID, CompanyName, FirstName, LastName FROM SalesLT.Customer"
connectionstring="<%$ ConnectionStrings:AWLTConnectionString %>"
runat="server"/>
<asp:gridview id="CustomersGridView"
datasourceid="CustomersSource"
autogeneratecolumns="True"
emptydatatext="No data available."
allowpaging="True"
runat="server" DataKeyNames="CustomerID">
</asp:gridview>

It is very important to know how you want to handle the situation.
Situation 1:
You have an existing table and from time to time you add columns (which is not very practical though) you can straight away go for the GridView and set the AutoGenerateColumns binding property to true.
Situation 2
You can create your own HTmlHelper Class for handling the tables.
For this you can visit the following post:
How to create MVC HtmlHelper table from list of objects
if you are using MVC.

Here's a quick hack to do this if you must use Repeater:
In the .aspx:
<asp:Repeater ID='rptr' runat='server'>
<HeaderTemplate>
<table>
<thead>
<tr>
<th>
Always visible header col
</th>
<th id='thHidable' runat='server' class='hideable'>
Hideable header col
</th>
</tr>
</thead>
<tbody>
</HeaderTemplate>
<ItemTemplate>
<tr>
<td>
Repeated col
</td>
<td id='tdHideable' runat='server' class='hideable'>
Hideable repeated col
</td>
</tr>
</ItemTemplate>
<FooterTemplate>
</tbody></table>
</FooterTemplate>
</asp:Repeater>
In the code behind (assuming C# used):
protected override void Page_Init()
{
this.rptr.ItemDataBound += new RepeaterItemEventHandler(rptr_ItemDataBound);
}
void rptr_ItemDataBound(object sender, RepeaterItemEventArgs e)
{
RepeaterItem item = (RepeaterItem)e.Item;
if (item.ItemType == ListItemType.Header)
{
HtmlTableCell thHidable = (HtmlTableCell)item.FindControl("thHidable");
if (hideCondition)
{
// thHidable.Visible = false; // do not render, not usable by client script (use this approach to prevent data from being sent to client)
thHidable.Style["display"] = "none"; // rendered hidden, can be dynamically shown/hidden by client script
}
}
else if (item.ItemType == ListItemType.Item || item.ItemType == ListItemType.AlternatingItem)
{
HtmlTableCell tdHideable = (HtmlTableCell)item.FindControl("tdHideable");
if (hideCondition)
{
// tdHideable.Visible = false; // do not render, not usable by client script (use this approach to prevent data from being sent to client)
tdHideable.Style["display"] = "none"; // rendered hidden, can be dynamically shown/hidden by client script
}
}
}
(optional) If you want to dynamically show column on client side (assuming it was rendered), using jQuery (for brevity):
$(".hideable").show();

Related

Show hide repeater item template field when used with user control in item template

Hi I have a user control inside repater item template .I want to hide the user controls column based on certain values coming in the string.Below are my code
<asp:Repeater runat="server" ID="MyRepeater" OnItemDataBound="MyRepeater_ItemDataBound">
<ItemTemplate>
<uc2:UCToolEventSummary runat="server" ID="UCSummary"
TaskId='<%#Eval("TaskId")%>'
SystemName='<%#Eval("SystemName")%>'
ResourceName='<%#Eval("ToolName")%>'
Requestor='<%#Eval("Requestor")%>'
CategoryName='<%#Eval("CategoryName")%>'
</ItemTemplate>
</asp:Repeater>
I want to hide the columns based on a string list
list<string> columnsHidden = "SystemName,CategoryName"
So systemname and categoryName should be hidden in the user control
I tried below approach in item databound event but could not able to do that
protected void MyRepeater_ItemDataBound(object sender, RepeaterItemEventArgs e)
{
List<String> columnsList = new List<String>();
columnsList = "SystemName,CategoryName";
if (e.Item.ItemType == ListItemType.Item ||
e.Item.ItemType == ListItemType.AlternatingItem)
{
var control = e.Item.FindControl("UCSummary");
foreach (String columnName in columnsList)
{
}
}
}
My Usercontrol code is below
<table class="table table-bordered">
<tr class="row">
<td class="col25">
Task Id: <%=TaskId %>
</td>
<td class="col25">
System Name: <%=SystemName %>
</td>
<td class="col25">
Task Status: <%=TaskStatus %>
</td >
<td class="col25">
Requestor: <%=Requestor %>
</td>
<td class="col25">
Request Type: <%=CategoryName %>
</td>
</tr>
</table>
Can anybody help me on this?
Thank you
You have in your markup added some custom attributes with the columns you need. Thus, on item data bound you should be able to grab/get that control, and then hide/show other columns (controls) in that repeater.
And I don't see the need for a for each loop. The item data bound fires ONE time for each row on data bind.
Thus:
var UCToolEventSummary UC = e.Item.FindControl("UCSummary");
if UC.Attributes.item("CategoryName") = "zoo" {
get other controls - hide or show them - use style in place of visible

Once clicking the select button of a row, a gridview appears between that row and the rest of the gridview. Possible?

I'd like to have the user click the select button of, lets say, the 250th row of a 400 row gridview. When they click that, then another gridview that's 3x12 appears below that row, then the 150 other rows appear below that. Is this at all possible? I guess I could create a whole other div that'll have three gridviews that output depending on being <= and > the index of the selected row.
It starts as:
Gridview rows 1-400
Then after row 350 is selected is it:
Gridview rows 1-350
Gridview of row 350 info
Gridview rows 351-400.
It's definitely possible, but I would use a ListView or DataList as your parent container instead, because with a GridView, you'll have to put the child list in a column, which will look ugly. This should put you on the right path:
<asp:ListView ID="lstOuterList" runat="server" DataKeyNames="ID, OtherColumn">
<LayoutTemplate>
<table width="100%">
<asp:PlaceHolder runat="server" ID="itemPlaceHolder" />
</table>
</LayoutTemplate>
<ItemTemplate>
<tr>
<td><asp:LinkButton ID="LinkButton1" runat="server" Text="Expand" OnCommand="LinkButton1_Command" CommandArgument='<%#Container.DisplayItemIndex%>'></asp:LinkButton></td>
<td><%#Eval("Value")%></td>
<td><%#Eval("OtherValue")%></td>
<td><%#Eval("OtherOtherValue")%></td>
</tr>
<asp:PlaceHolder ID="plcInnerList" runat="server">
<asp:ListView ID="lstInnerList" runat="server" Width="100%">
<LayoutTemplate>
<tr>
<td colspan="4">
<div style="padding:20px;background-color:#fffeee;">
<table width="100%">
<asp:PlaceHolder runat="server" ID="itemPlaceHolder" />
</table>
</div>
</td>
</tr>
</LayoutTemplate>
<ItemTemplate>
<tr>
<td><%#Eval("Value")%></td>
<td><%#Eval("OtherValue")%></td>
<td><%#Eval("OtherOtherValue")%></td>
</tr>
</ItemTemplate>
</asp:ListView>
</asp:PlaceHolder>
</ItemTemplate>
</asp:ListView>
And when the user clicks the LinkButton/Button in DataList1, do something like this:
protected void LinkButton1_Command(object sender, CommandEventArgs e)
{
//pass index of item in command argument
var itemIndex = Convert.ToInt32(e.CommandArgument);
//find the pnlChildView control
var innerPlaceHolder = lstOuterList.Items[itemIndex].FindControl("plcInnerList") as PlaceHolder;
if (innerPlaceHolder != null)
{
innerPlaceHolder.Visible = !innerPlaceHolder.Visible;
if (innerPlaceholder.Visible)
{
var innerList = innerPlaceHolder.FindControl("lstInnerList") as ListView;
if (innerList != null)
{
//the id to retrieve data for the inner list
int keyValue = (int)lstOuterList.DataKeys[itemIndex]["ID"];
//bind the list using DataList1 data key value
innerList.DataSource = new DataTable("DataSource"); //your datasource
innerList.DataBind();
}
}
}
}
one way is:
on the rowcommand of the main grid:
create c# code for the grid will be inside GridView grd = new GridView();
bind this instance like any other grid
add on the controls from the main grid current line, should be something like
e.Cells[0].Controls.Add(grd);
I don't have VS here right now but I guess you could get the idea, I use this approach all the time

Select All Checkbox feature in gridview not being implemented

I am having header in gridview that labels as "xls" and a checkbox, that when selected should select all the checkbox columns in gridview and unchecking the xls column should uncheck all the columns.
I am following two links:
Link-1
Here, the totalChkBoxes variable is coming null (despite my gridview has rows). In fact when debugging the JS, code inside parseInt and below line is coming as ''
Link-2
Here also the GridView2 variable is coming null.
One common change that i am doing in both the JS is replacing the <%=.....%> by <%#....%>
Please guide as to what i am doing wrong. You can also help by giving some suitable link to implement the desired functionality
CODE UPDATED WITH MY WORKING JS
<script type="text/javascript" language="javascript">
function checkAllBoxes() {
var gvControl = document.getElementById("gvSample");
//this is the checkbox in the item template.
var gvChkBoxControl = "chkSelectItem";
//Header Template checkbox.
var mainChkBox = document.getElementById("chkBoxAll");
//Array of Inputs in gridview.
var inputTypes = gvControl.getElementsByTagName("input");
for (var i = 0; i < inputTypes.length; i++) {
//if the input type is a checkbox and the id of it is what we set above
//then check or uncheck according to the main checkbox in the header template
if (inputTypes[i].type == 'checkbox' && inputTypes[i].id.indexOf(gvChkBoxControl, 0) >= 0)
inputTypes[i].checked = mainChkBox.checked;
}
}
GRIDVIEW CODE
<asp:TemplateField>
<HeaderTemplate>
<table style="width: 15px" cellpadding="0" cellspacing="0">
<tr>
<td>
<asp:Label ID="lblXls" runat="server" Text="xls"></asp:Label>
<br />
<input id="chkBoxAll" type="checkbox" onclick="checkAllBoxes()" />
</td>
</tr>
</table>
</HeaderTemplate>
<ItemTemplate>
<asp:CheckBox ID="chkSelectItem" runat="server" />
</ItemTemplate>
</asp:TemplateField>
Thanks!
Try changing your parseInt to something like this to see if it helps at all. I know it's only a small change, but small things tend to break JS code:
var totalChkBoxes = parseInt("<%=gvTest.Rows.Count%>");
Secondly, if you have runat="server" on the checkbox in the header, you may need to change this line if your JSFunction:
var mainChkBox = document.getElementById("<%=chkBoxAll.UniqueID%>");

How do I switch visibility of a control in a repeater?

I have a shopping cart that I am developing as a web user control. ucCart.ascx will appear on three different pages and I want the functionality of the cart to alter depending on which page it appears on. When the customer is confirming their order for example, I do not want to the delete cart item buttons or the recalculate cart button to be visible.
Can this be done programmatically in code behind? I'd rather not use JavaScript. I naively tried to use cartDelete.Visible = false; but that's not liked at all!
You need to get a reference to those controls and call set Visible property to false; something like this pseudo code;
ShoppingCartControlVariable.FinControl("idOfTheControlYouWantToHide").Visible=false;
See this documentation
Adding sample code to demonstrate how this is done:
Assuming you have a repeater like this (notice the OnItemCreated handler):
<asp:Repeater ID="myrepeater" runat="server" OnItemCreated="myrepeater_ItemCreated">
<HeaderTemplate>
<table>
<thead>
<th>
Link
</th>
<th>
Button
</th>
</thead>
<tbody>
</HeaderTemplate>
<ItemTemplate>
<tr>
<td>
<asp:HyperLink ID="link" runat="server" Text='<%#Eval("Text")%>' NavigateUrl='<%#Eval("Url")%>'></asp:HyperLink>
</td>
<td>
<asp:Button ID="btnDelete" runat="server" Text="Delete" />
</td>
</tr>
</ItemTemplate>
<FooterTemplate>
</tbody> </table>
</FooterTemplate>
</asp:Repeater>
You can hide/show elements in the repeater rows as follows:
protected void myrepeater_ItemCreated(object sender, RepeaterItemEventArgs e)
{
if (e.Item.ItemType == ListItemType.Item && (boolean_condition_that_on_which_you_will_decide_what_to_show_and_what_to_hide))
{
e.Item.FindControl("link").Visible = false;
}
}
For example, if I want to hide all link elements on every row and just leave the delete buttons, I can do this:
protected void myrepeater_ItemCreated(object sender, RepeaterItemEventArgs e)
{
if (e.Item.ItemType == ListItemType.Item || e.Item.ItemType==ListItemType.AlternatingItem)
{
e.Item.FindControl("btnDelete").Visible = false;
}
}
And it will produce this:
For reference, the code that I used to populate my repeater was this:
List<CartItem> items = new List<CartItem>();
for (int i = 0; i < 10; i++)
{
CartItem t = new CartItem();
t.Text="Item " +i;
t.Url="http://www."+i+".com";
items.Add(t);
}
myrepeater.DataSource = items;
myrepeater.DataBind();
I think you can make a public function inside this user control named "hide controls" and call this function in the page you want and this function hide the controls or you can make a boolean property in the user control and the page can set it to false and then you can use this flag to hide the controls inside your usercontrol.

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; }

Categories