Stránkování v ovládacím prvku DataGrid inteligentněji

8. dubna 2004

V prvním článku o listování a třídění v DataGridu jsem se zmínil o takovém řešení stránkování, které nevyžaduje načtení celé tabulky do paměti. Rozdíl ovšem nespočívá v odlišném kódu ASP.NET aplikace, ale v použití uložené procedury pro MS SQL. Jistou výhodou tohoto řešení je, kromě úspory výkonu a paměti, také nezávislost na použitém skriptovacím jazyku.

Pokud si můžeme být jisti, že máme v tabulce sloupec, který obsahuje nikde nepřerušenou vzestupnou řadu ID, můžeme si gratulovat, práci máme velice zjednodušenou. V uložené proceduře na základě zadaných parametrů provedeme výběr příslušných záznamů:

CREATE PROCEDURE sp_strankovani
   @pocet_vet int OUTPUT,
   @aktualni_strana int = 0,
   @velikost_strany int=10
AS
   SET NOCOUNT ON
   DECLARE @prvni_zaznam int, @posledni_zaznam int
   SET @prvni_zaznam=@aktualni_strana* @velikost_strany
   SET @posledni_zaznam=((@aktualni_strana+1)*@velikost_strany)
   select * from Products where ProductID>@prvni_zaznam and ProductID<=@posledni_zaznam
   select @pocet_vet=count(*) from Products
GO

Procedura vrací jako výsledek své činnosti požadovanou stránku o požadované velikosti. Pokud při „volání“ procedury nespecifikujeme požadovanou stránku či velikost stránky, obdržíme první stránku s deseti záznamy.

Pro testování jsem si opět vybral databázi NorthWind, která je součástí instalace MS SQL serveru. Na tabulce Products pracuje vše bez problémů, ale to jen díky tomu, že posloupnost ve sloupci ProductsID začíná od 1 a je zcela konzistentní.

Protože však neustále nějaké záznamy mažeme a na konzistentní posloupnost se tudíž nemůžeme spolehnout, musíme si pomoci trochu jinak. Vytvoříme si dočasnou tabulku, která bude mít svoji vlastní Identity, na jejímž základě budeme provádět vlastní výběr z databáze:

  • bude možnost provádět řazení podle požadovaného sloupce
  • vybírat z databáze podle různých podmínek

CREATE PROCEDURE sp_strankovani1
   @pocet_vet int OUTPUT,
   @aktualni_strana int = 0,
   @velikost_strany int=10
AS
   set nocount on
   DECLARE @prvni_zaznam int, @posledni_zaznam int
   set @prvni_zaznam=@aktualni_strana* @velikost_strany
   set @posledni_zaznam=((@aktualni_strana+1)*@velikost_strany)
   create table #temptable
   (
      id int IDENTITY PRIMARY KEY,
      ProductID int,
      ProductName nvarchar(40),
      UnitPrice money
   )
   insert into #temptable
   (ProductID, ProductName, UnitPrice) select ProductID,ProductName,UnitPrice from products order by ProductName
   select * from #temptable where id>@prvni_zaznam and id<=@posledni_zaznam
   SELECT @pocet_vet=count(*) from #temptable
GO

Nechme už ale programovaní uložených procedur a ukažme si, jak tyto procedury využít pro zobrazení dat v DataGridu. Na stránku umístíme serverový ovládací prvek DataGrid, kterému nastavíme požadované vlastnosti. Kromě vlastnosti AllowPaging je bezpodmínečně nutné nastavit na „true“ také vlastnost AllowCustomPaging. Nastavení této vlastnosti umožní přizpůsobovat stránkování našim okamžitým potřebám.

<%@ Page language=“c#“ Codebehind=“datagrid_sp.aspx.cs“ AutoEventWireup=“false“ Inherits=“DATAGRID1.datagrid_sp“ %>
<!DOCTYPE HTML PUBLIC „-//W3C//DTD HTML 4.0 Transitional//EN“ >
<HTML>
<HEAD>
<title>datagrid_sp</title>
<meta name=“GENERATOR“ Content=“Microsoft Visual Studio .NET 7.1″>
<meta name=“CODE_LANGUAGE“ Content=“C#“>
<meta name=“vs_defaultClientScript“ content=“JavaScript“>
<meta name=“vs_targetSchema“ content=“http://schemas.microsoft.com/intellisense/ie5″>
</HEAD>
<body>
<form id=“Form1″ method=“post“ runat=“server“>
<asp:DataGrid id=“DG“ runat=“server“
AllowPaging=“True“ AllowCustomPaging=“True“>
<PagerStyle Mode=“NumericPages“></PagerStyle>
</asp:DataGrid>
</form>
</body>
</HTML>

Podobně jako v předchozím článku o stránkování v DataGridu vytvoříme metodu obsluhují událost OnPageIndexChanged, ve které voláme metodu zajišťující naplnění DataGridu požadovanými daty:

private void DG_PageIndexChanged(object source, System.Web.UI.WebControls.DataGridPageChangedEventArgs e)
{
napln_datagrid(e.NewPageIndex);
}

Nesmíme zapomenout postarat se o naplnění DataGridu při pvním přístupu na stránku. Pro tento účel s výhdou použijeme metodu Page_Load, ve které si otestujeme vlastnost IsPostBack objektu Page:

private void Page_Load(object sender, System.EventArgs e)
{
if (!Page.IsPostBack)
{
napln_datagrid(0);
}
}

V metodě napln_datagrid provedeme vytvoření instance objektu SqlCommand, pomocí něhož vytvoříme objekt „rdr“, který je instancí objektu SqlDataReader. Následně si do instance objektu DataTable načteme data z objektu „rdr“ a tento objekt zavřeme. Jako zdroj dat DataGridu nastavíme vytvořenou instanci objektu DataTable a zpracujeme výstupní parametr uložené procedury, který určuje počet vět v databázové tabulce. Údaj o počtu vět zapíšeme do vlastnosti VirtualItemCount. Na závěr nezapomeňte nastavit CurrentPageIndex na index právě zobrazované stránky (ovlivňuje tvorbu odkazů pro stránkování) a zavolat metodu DataBind objektu DataGrid.

void napln_datagrid(int stranka)
{
   SqlConnection conn1=new
   SqlConnection(ConfigurationSettings.AppSettings[„conn_DB“]);
   SqlCommand command1 =new SqlCommand(„sp_strankovani1“,conn1);
   command1.CommandType=CommandType.StoredProcedure;
   // priprava parametru pro ulozenou proceduru
   SqlParameter prm;
   prm=new SqlParameter(„@aktualni_strana“,SqlDbType.Int);
   prm.Value=stranka;
   prm.Direction=ParameterDirection.Input;
   command1.Parameters.Add(prm);
   prm=new SqlParameter(„@velikost_strany“,SqlDbType.Int);
   prm.Value=DG.PageSize;
   prm.Direction=ParameterDirection.Input;
   command1.Parameters.Add(prm);
   prm=new SqlParameter(„@pocet_vet“,SqlDbType.Int);
   prm.Direction=ParameterDirection.Output;
   command1.Parameters.Add(prm);
   DataTable dt= new DataTable();
   DataRow dr;
   SqlDataReader rdr;
   dt.Columns.Add(„id“,System.Type.GetType(„System.Int16“));
   dt.Columns.Add(„ProductID“,System.Type.GetType(„System.Int16“));
   dt.Columns.Add(„ProductName“,System.Type.GetType(„System.String“));
   dt.Columns.Add(„UnitPrice“,System.Type.GetType(„System.String“));
   conn1.Open();
   rdr= command1.ExecuteReader(System.Data.CommandBehavior.CloseConnection);
   while (rdr.Read())
   {
      dr= dt.NewRow();
      dr[„id“]=rdr[„id“];
      dr[„ProductID“]=rdr[„ProductID“];
      dr[„ProductName“]=rdr[„ProductName“];
      dr[„UnitPrice“]=rdr[„UnitPrice“];
      dt.Rows.Add(dr);
   }
   rdr.Close();
   DG.DataSource=dt;
   DG.VirtualItemCount= (int) command1.Parameters[„@pocet_vet“].Value;
   DG.CurrentPageIndex=stranka;
   DG.DataBind();
}

Možná teď přemýšlíte, proč plním DataGrid tak složitě pomocí DataTable a neprovedu rovnou třeba následující:

…..
   DG.DataSource=rdr;
   DG.VirtualItemCount= (int) command1.Parameters[„@pocet_vet“].Value;
   DG.CurrentPageIndex=stranka;
   DG.DataBind();
….

Je to proto, že výstupní parametr uložené procedury je k dispozici až po ukončení práce s objektem, který tuto uloženou proceduru zavolal. Pro naplnění DataGridu potřebujeme mít existující objekt v době „volání“ metody DataBind, ale současně je potřeba nastavit vlastnosti DataGridu před voláním této metody. Hodnotu pro VirtualItemCount můžeme však získat až po uzavření SqlDataReadru a proto je nutné použít „berličku“ ve formě DataTable nebo získat informaci o počtu vět jiným způsobem, například dalším dotazem.

Pokud se pozastavíte nad tím, že uložená procedura není univerzální a je tedy nutné mít různé procedury pro výpisy z různých tabulek či dokonce i z různých sloupců téže tabulky, prozradím vám, že lze vytvořit i poměrně univerzální uloženou proceduru, pomocí které bude možné vypisovat různé sloupce z jedné tabulky a navíc provádět různá třídění. Vzhledem ke značné komplikovanosti si však její popis vyžaduje samostatného článku, v němž ji rozeberu podrobněji.

Starší komentáře ke článku

Pokud máte zájem o starší komentáře k tomuto článku, naleznete je zde.

Další článek aquatab.net
Štítky: Články

Mohlo by vás také zajímat

Nejnovější

Napsat komentář

Vaše e-mailová adresa nebude zveřejněna. Vyžadované informace jsou označeny *