Repetidor agrupando elementos de una sola base de datos

Nov 09 2020

Tengo una tabla con encabezados (ejemplo): Grupo, DisplayName, EditableData, descripción. He intentado durante los últimos días aprender a los repetidores para ordenar la información según el grupo para poder obtener algo que se parezca al siguiente diseño. Este es un control de usuario que estoy tratando de armar.

Group1
------------------
Name1     EditableData    Some Description
Name2     EditableData    Some Description

Group2
------------------
Name3     EditableData    Some Description
Name4     EditableData    Some Description

He mirado los siguientes ejemplos en línea: Repetidores anidados: agrupación de datos y repetidores anidados en ASP.NET

Creo que no entiendo correctamente cómo funcionan los repetidores lo suficiente como para lidiar con el anidamiento o la fuente de datos.

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

Usar DISTINCT en mi SQL para simplemente recuperar "Grupo" me deja con los grupos adecuados sin repeticiones y supongo que podría simplemente configurar los grupos en etiquetas y luego hacer repetidores para cada etiqueta específica ... Esto parece terrible cuando puedo más tarde actualice editableData de nuevo a la base de datos.

Lo que realmente quiero, supongo, es al menos un enlace a un tutorial que explique cómo funcionan los repetidores junto con Eval () y las fuentes de datos. Quiero decir, el código para hacer todo lo que necesito para completar este primer paso en mi proyecto sería perfecto; P Pero también quiero poder entenderlos mejor, ya que probablemente los usaré a menudo en un futuro cercano.

Respuestas

2 JamshaidK. Nov 09 2020 at 17:52

Una vez encontré el mismo problema en el que tenía que ordenar los datos por grupo y tenía que mostrar los elementos comunes en un segmento agrupado.

Por supuesto, hay varias formas de recuperar datos, por ejemplo, puede obtener nombres de grupos distintos y vincularlos al repetidor y luego, en el ItemDataBoundevento, puede ejecutar y obtener otros elementos como este:

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

Esta no es una forma recomendada porque va a la base de datos, ejecuta la consulta y luego obtiene datos para cada elemento (si está obteniendo estos datos de db). Si tiene miles de grupos, hará llamadas a la base de datos miles de veces, lo cual es malo.

La segunda solución es diseñar un modelo y alimentar un modelo personalizado que hará el trabajo. Déjame explicarlo con un modelo de muestra:

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

Luego consulte e inicialice la lista de GroupedModelclases y luego envíela al archivo repeater. Déjame hacerlo con algunos datos ficticios.

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;

Luego modifique el marcado de esta manera:

<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>
1 AlbertD.Kallal Nov 09 2020 at 21:53

Encuentro que solo usar una vista de cuadrícula o una vista de lista funciona bastante bien. Sin embargo, NO intente utilizar la función de agrupación, ya que es para colocar elementos en la página, no hacia abajo.

¡Hagamos esto realmente simple!

Ok, tengo una lista de hoteles, pero quiero agruparlos por ciudad.

Entonces, crea una consulta como esta:

    Dim strSQL As String = 
       "SELECT ID, FirstName, LastName, HotelName, City FROM tblHotels ORDER BY City, HotelName"

    GridView1.DataSource = Myrst(strSQL)
    GridView1.DataBind()

Ok, eso llena nuestra vista de cuadrícula. Obtenemos esto:

Hasta ahora, ¡dos líneas de código!

Pero queremos agrupar por ciudad.

Entonces, en el nivel de clase de formularios, agregue var simple:

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

Bien, ahora en el evento de vinculación del elemento de datos, simplemente agregue una fila NUEVA.

El código tiene este aspecto:

    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

Ahora, arriba hay un "poco" de un fragmento para masticar, pero aún no mucho código.

Entonces, ahora cuando corremos arriba, obtenemos esto:

Nuestro marcado de vista de cuadrícula se ve así:

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

Así que no está tan mal.

¿Si decide utilizar una vista de lista? Entonces el código se vuelve un poco menor, pero el marcado para listview es bastante complicado.

Todo lo que hacemos es crear una fila que sea nuestro encabezado y ahora mostrar u ocultar esa fila según el inicio de la nueva agrupación.

Entonces, ¿si uno decide usar una vista de lista? Entonces obtenemos esto:

(Supongo que usa los asistentes de enlace de datos, no es posible que escriba el marcado a mano, ¿verdad? - salvando la pobreza mundial aquí)

Entonces, para una vista de lista (y creo que la vista de lista es MEJOR, ya que las opciones de diseño para esa fila de encabezado están abiertas a cualquier tipo de marcado y controles adicionales que sueñe).

Entonces, el marcado (generado y luego cortado la grasa) es este:

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

Ahora, no hay duda de que la vista de lista arroja mucho más marcado, pero ahora tenemos una fila completa para el encabezado. Entonces obtenemos esto:

Pero, ahora nuestro código simplemente ocultará o mostrará esa fila "extra" que tenemos en el mercado.

Y ahora es bastante simple:

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

Entonces, el truco en la mayoría de los casos es simplemente diseñar ese elemento de fila adicional, y luego, en el evento de enlace de datos del elemento, simplemente oculta o muestra esa parte del encabezado.