This is my first attempt at asp.net and webforms, windowsforms, all of it. I originally populated data in a listbox, but I could only get one column and thought the listView sounded like it would do what I wanted.
The most promising solution I found was this:
DataSet listData = new DataSet();
CancellationsControls cancelCtrl = new CancellationsControls();
listData = cancelCtrl.GetScheduledReleaseDataSet();
DataTable dtable = listData.Tables[0];
scheduledReleasesTag.Items.Clear();
foreach (DataRow row in dtable.Rows)
{
string[] ar = Array.ConvertAll(row.ItemArray, p => p.ToString());
scheduledReleasesTag.Items.Add(new ListViewDataItem(ar));
}
The dtable is a custom table of a query joining a number of tables.
in the foreach loop the ar string array succesfully shows the colums of data that I want, but the ListViewDataItem requires two int arguments instead of a string array like the example I pulled this from.
I've tried to figure out more about how the listView control works, but this is as close as I have been able to get to getting anything. Any help with explanations would be very appreciated.
Thank you :)
I'm pretty beginner in asp, but to bind ListView control with data, smth like this should work:
DataSet listData = new DataSet();
CancellationsControls cancelCtrl = new CancellationsControls();
listData = cancelCtrl.GetScheduledReleaseDataSet();
DataTable dtable = listData.Tables[0];
ListView1.DataSource = dtable;
ListView1.DataBind();
Now we have to create an ItemTemplate. Your ListView control should look like:
<asp:ListView ID="ListView1" runat="server">
<ItemTemplate>
<tr id="Tr1" class="item" runat="server">
<td>
<asp:Label ID="column_name" runat="server" Text='<%# Eval("column_name") %>' />
</td>
</tr>
<tr id="Tr2" class="item" runat="server">
<td>
<asp:Label ID="another_column_name" runat="server" Text='<%# Eval("another_column_name") %>' />
</td>
</tr>
</ItemTemplate>
</asp:ListView>
Just replace markers(column_name,another_column_name) with names of columns, that contains data you want to display. This template displays pairs of rows values from two columns one by one.
Related
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.
I have a GridView which gets the data from a SQL Server database.
The GridView binds when the user select the date from an CalendarExtender, because the data is different one day to another, also the rows quantity.
E. G., in Saturdays the GridView is filled with 18 rows. In Tuesdays, with 58.
Concern:
What I need to do is to split the GridView in 2 parts (2 GridViews). E. G., In tuesdays, 29 rows each GridView, in saturdays, 9 rows each.
I have tried:
To bring the daily data into a GridView, called "GVTotal":
if (Weekday.Value == "Saturday")
{
GVTotal.DataSourceID = SaturdayData.ID;
GVTotal.DataBind();
}
To count the rows from GVTotal, and divide by 2.
int everything = GVTotal.Rows.Count;
int half = everything / 2;
What I want to do now, is to "Copy" the rows from 0 to half to GVPart1, and from half to everything to GVPart2, in the exactly same order than in GVTotal.
I have read that maybe using a DataTable will made this possible.
I am not pretty sure how to do that. Could someone help me please?
You could have a Repeater with two items, one for each GridView. In the example below, the Repeater is rendered as a table with a single row. Each GridView is in a cell of that row, with an empty cell between them.
<asp:Repeater ID="repeater1" runat="server" OnItemDataBound="repeater1_ItemDataBound">
<HeaderTemplate>
<table cellspacing="0" cellpadding="0">
<tr>
</HeaderTemplate>
<ItemTemplate>
<td>
<asp:GridView ID="gvHalf" runat="server" >
...
</asp:GridView>
</td>
</ItemTemplate>
<SeparatorTemplate>
<td style="width: 32px;" />
</SeparatorTemplate>
<FooterTemplate>
</tr>
</table>
</FooterTemplate>
</asp:Repeater>
In order to get the two data sources, you declare two DataTables in your class:
private DataTable dtTopHalf;
private DataTable dtBottomHalf;
In Page_Load, the two DataTables are populated by splitting the full DataTable in two parts, and the data source of the Repeater is set to two boolean values:
void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
using (SqlConnection conn = new SqlConnection("Data Source=(local); Integrated Security = True; Initial Catalog=TestMP"))
using (SqlCommand cmd = new SqlCommand("SELECT * FROM Clients ORDER BY ClientID ASC", conn))
{
cmd.CommandType = CommandType.Text;
SqlDataAdapter dataAdapter = new SqlDataAdapter(cmd);
DataTable dt = new DataTable();
dataAdapter.Fill(dt);
int halfCount = dt.Rows.Count / 2;
dtTopHalf = dt.AsEnumerable().Select(x => x).Take(halfCount).CopyToDataTable();
dtBottomHalf = dt.AsEnumerable().Select(x => x).Skip(halfCount).CopyToDataTable();
}
// Each value in the Repeater indicates if it is the top half or not
repeater1.DataSource = new List<bool>() { true, false };
repeater1.DataBind();
}
}
The specific data source can then be set for each GridView in the ItemDataBound event handler of the Repeater:
protected void repeater1_ItemDataBound(object sender, RepeaterItemEventArgs e)
{
if (e.Item.ItemType == ListItemType.Item || e.Item.ItemType == ListItemType.AlternatingItem)
{
bool isTopHalf = (bool)e.Item.DataItem;
GridView gvHalf = e.Item.FindControl("gvHalf") as GridView;
gvHalf.DataSource = isTopHalf ? dtTopHalf : dtBottomHalf;
gvHalf.DataBind();
}
}
Note 1: The Repeater allows to share the same markup for both GridViews. If you prefer, you can declare two separate GridViews in the markup and apply the specific data source to each one in Page_Load.
Note 2: You need a reference to System.Data.DataSetExtensions in your project to use some of the LINQ methods mentioned above.
Thanks to ConnorsFan for the assistance. His answer is the right way to do what I wanted.
As I need to select the date before the data comes in, I wrote Connor's code into my date Textbox OnTextChanged event, into the if (Weekday.Value == "<day of the week>") statement.
This is the solution:
ASPX:
Updated: Only with one GridView, as reccomended:
<asp:Repeater ID="repeater1" runat="server" OnItemDataBound="repeater1_ItemDataBound">
<HeaderTemplate>
<table cellspacing="200px" cellpadding="0">
<tr style="vertical-align: top;">
</HeaderTemplate>
<ItemTemplate>
<td>
<asp:GridView ID="gvHalf" runat="server" BackColor="White" AutoGenerateColumns="False" HeaderStyle-CssClass="tituloshandoff" RowStyle-CssClass="contenidohandoffbatch">
<Columns>
<asp:BoundField HeaderText="IDBATCH" DataField="IDBatch" SortExpression="IDBatch" HeaderStyle-CssClass="TituloInvisible" ItemStyle-CssClass="TituloInvisible" />
<asp:BoundField HeaderText="BATCH" DataField="Nombre" SortExpression="Nombre" />
<asp:BoundField HeaderText="DEALER" DataField="DealerCodigo" SortExpression="DealerCodigo" />
<asp:BoundField HeaderText="CT TIME" DataField="CTStart" SortExpression="CTStart" />
<asp:BoundField HeaderText="STATUS" DataField="Estado" SortExpression="Estado" />
<asp:TemplateField HeaderText="CONTROL">
<ItemTemplate>
<asp:Button ID="Button1" CssClass="botongrid" runat="server" Text="Select" Width="100px" /></ItemTemplate>
</asp:TemplateField>
</Columns>
</asp:GridView>
</td>
</ItemTemplate>
<SeparatorTemplate>
<td style="width: 100px;" />
</SeparatorTemplate>
<FooterTemplate>
</tr>
</table>
</FooterTemplate>
</asp:Repeater>
C#:
I will show only one day in example:
string LaConexion = #"<My Connection String>";
private DataTable dtTopHalf;
private DataTable dtBottomHalf;
protected void TextDate_TextChanged(object sender, EventArgs e)
{
if (diasemana.Value == "Monday")
{
using (SqlConnection conexion = new SqlConnection(LaConexion))
using (SqlCommand comando = new SqlCommand("SELECT [1Monday].IDBatch, Batch.Nombre, Dealer.DealerCodigo, Batch.CTStart, BatchDatos.Estado FROM [1Monday] INNER JOIN Batch ON [1Monday].IDBatch = Batch.IDBatch INNER JOIN Dealer ON Batch.IDDealer = Dealer.IDDealer LEFT OUTER JOIN BatchDatos ON [1Monday].ID = BatchDatos.ID ORDER BY Batch.CTStart", conexion))
{
comando.CommandType = CommandType.Text;
SqlDataAdapter dataAdapter = new SqlDataAdapter(comando);
DataTable dt = new DataTable();
dataAdapter.Fill(dt);
int halfCount = dt.Rows.Count / 2;
dtTopHalf = dt.AsEnumerable().Select(x => x).Take(halfCount).CopyToDataTable();
dtBottomHalf = dt.AsEnumerable().Select(x => x).Skip(halfCount).CopyToDataTable();
}
// Each value in the Repeater indicates if it is the top half or not
repeater1.DataSource = new List<bool>() { true, false };
repeater1.DataBind();
}
}
A Picture:
I have a Datatable that has a checkbox and a value, What I want is that when I click a button it would get all the checked boxes and their corresponding value and Add them to a blank Datatable. I not planning to save the data moved from 1 list to another, as there will be another functionality to confirm if I want to save the changes(not being asked in this question).
This is my html code:
<asp:ListView runat="server" ID="uoListViewAirportSaved" EmptyDataText="No Data Found">
<LayoutTemplate>
<table border="0" cellpadding="0" cellspacing="0" width="500px">
<tr>
<th runat="server"> </th>
<th runat="server">Airport</th>
</tr>
<asp:PlaceHolder runat="server" ID="itemPlaceHolder"></asp:PlaceHolder>
</table>
</LayoutTemplate>
<ItemTemplate>
<tr>
<td class="leftAligned">
<asp:CheckBox ID="uoCheckBoxSelect" runat="server" />
</td>
<td class="leftAligned">
<asp:HiddenField ID="uoHiddenFieldAirport" runat="server" Value='<%# Eval("ColAirportCodeVarchar") %>' />
<asp:Label ID="uoLabelAirport" runat="server" Text='<%# Eval("colAirportFullName")%>' />
</td>
</tr>
</ItemTemplate>
<EmptyDataTemplate>
<table border="0" cellpadding="0" cellspacing="0" width="500px">
<tr>
<td>Airport</td>
</tr>
<tr>
<td colspan="3" class="leftAligned">No Record</td>
</tr>
</table>
</EmptyDataTemplate>
</asp:ListView>
And the code when I click the add button:
private void AddAirport() {
CheckBox uoCheckBoxSelect;
HiddenField uoHiddenFieldAirport;
Label uoLabelAirport;
DataTable dt = new DataTable();
DataTable dt2 = new DataTable();
DataTable dt3 = new DataTable();
string serviceProvider = GlobalCode.Field2String(Request.QueryString["pid"]);
///datatable with all list of airports
dt = VendorMaintenanceBLL.GetServiceProviderAirportbyBrand(serviceProvider);
///datatable with provider's current airports
dt2 = VendorMaintenanceBLL.GetServiceProviderAirportbyVendor(serviceProvider);
foreach(ListViewItem item in uoListViewAirport.Items)
{
uoCheckBoxSelect = (CheckBox)item.FindControl("uoCheckBoxSelect");
if (uoCheckBoxSelect.Checked == true)
{
uoHiddenFieldAirport = (HiddenField)item.FindControl("uoHiddenFieldAirport");
uoLabelAirport = (Label)item.FindControl("uoLabelAirport");
/// my new list should contain the values from the checked values of the checkboxes
???
}
}
}
My Datatable contains the AirportID and the AirportStringName columns.
I found a code that I'm trying to replicate, logic-wise. However it is designed for a list:
List<AirportDTO> listToBeAdded = new List<AirportDTO>();
List<AirportDTO> listAdded = new List<AirportDTO>();
listToBeAdded = GetAirportNotInUser(false, false);
listAdded = GetAirportInUser(false);
foreach (ListViewItem item in uoListViewAirport.Items)
{
uoCheckBoxSelect = (CheckBox)item.FindControl("uoCheckBoxSelect");
if (uoCheckBoxSelect.Checked == true)
{
uoHiddenFieldAirport = (HiddenField)item.FindControl("uoHiddenFieldAirport");
uoLabelAirport = (Label)item.FindControl("uoLabelAirport");
var listToAdd = (from a in listToBeAdded
where a.AirportIDString == GlobalCode.Field2String(uoHiddenFieldAirport.Value)
select new
{
AirportID = a.AirportIDString,
AirportName = a.AirportNameString,
}).ToList();
}
}
I do not know how I would get the necessary data from the datatable as it is different from a list. Is there a way for me to do the same thing with DataTables the way lists were tackled.
Since I really need to fix the problem yesterday, I decided to do the following.
What I did was just basic for loop, then inside compare each row and then just use Datatable.ImportRow and DataTable.RemoveAt. I also added a temporary Datatable for the changed lists.
foreach(ListViewItem item in uoListViewAirport.Items)
{
uoCheckBoxSelect = (CheckBox)item.FindControl("uoCheckBoxSelect");
if (uoCheckBoxSelect.Checked == true)
{
uoHiddenFieldAirport = (HiddenField)item.FindControl("uoHiddenFieldAirport");
uoLabelAirport = (Label)item.FindControl("uoLabelAirport");
string HiddenFieldAirport = GlobalCode.Field2String(uoHiddenFieldAirport.Value);
for (var f = 0; f < dt.Rows.Count; f++ )
{
if (dt.Rows[f]["colAirportIDInt"].ToString() == HiddenFieldAirport)
{
dt3.ImportRow(dt.Rows[f]);
dt.Rows.RemoveAt(f);
}
}
}
}
The code is dirty, plus I had to sort it as well, but since I really needed it, I can make do with this.
I have a code block that returns a list of employee object.
The resultset contains more than one employee record. One of the elements, is EmployeeID.
I need to populate listview (lstDepartment) with only the EmployeeID. How can I do that?
lstDepartment.DataSource = oCorp.GetEmployeeList(emp);
lstDepartment.DataBind()
You have to also specify this:
lstDepartment.DataSource = oCorp.GetEmployeeList(emp);
lstDepartment.DataTextField = "EmployeeID";
lstDepartment.DataValueField = "EmployeeID";
lstDepartment.DataBind()
One way would be to use an anonymous type:
lstDepartment.DataSource = oCorp.GetEmployeeList(emp)
.Select(emp => new { emp.EmployeeID });
lstDepartment.DataBind();
Edit: But you also could select all columns but diplay only one. A ListView is not a ListBox or DropDownList. Only what you use will be displayed. So if you're ItemTemplate looks like:
<ItemTemplate>
<tr runat="server">
<td>
<asp:Label ID="LblEmployeeID" runat="server" Text='<%# Eval("EmployeeID") %>' />
</td>
</tr>
... only the EmployeeID is displayed, no matter whatelse is in your DataSource.
You may mention what to display in your ItemTemplate of ListView
<asp:ListView ID="lstDepartment" runat="server">
<ItemTemplate>
<p> <%#Eval("EmployeeID") %> </p>
</ItemTemplate>
</asp:ListView>
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