PDA

View Full Version : صفحه بندی در دیتالیست



Hamid-PI
پنج شنبه 09 فروردین 1386, 16:03 عصر
سلام.
چطور میشه اطلاعات رو توی دیتالیست یا ریپیتر صفحه بندی کرد؟

manager
پنج شنبه 09 فروردین 1386, 18:08 عصر
صفحه بندی رکوردها در هنگام نمایش بر روی صفحات وب یکی از عملیات مهم و قابل توجه هست که کمی بی دقتی در انجام این مهم باعث به هدر رفتن منابع سیستمی و وقت کاربر می شه که دست آخر به کم شدن بازدهی سایت و درنتیجه از دست دادن مراجعه کنندگان منجر می شه.
سعی می کنم در این جا روشی کوتاه و ساده و در عین حال کارائی رو جهت صفحه بندی اطلات جهت نمایش در DataList و GrideView شرح بدم.


http://i11.tinypic.com/2le749k.jpg


فرض کنید شما شماره صفحه را از طریق QueryString دریافت می کنید و باید با توجه به اندازه صفحات، صفحه مورد نظر را از روی Cache و یا پایگاه داده بازیابی نمائید.
به کدهای زیر دقت نمائید :



private void gvList_Databind(int pagenum, bool useCache)
{
int rowCount = -1;

string cachKey = "lst" + pagenum.ToString() +...
string rowCountCachKey = "rwqt" + pagenum.ToString() + ...

IList list = null;
if (Cache[cachKey] != null && useCache)
{
list = (IList)Cache[cachKey];
rowCount = (int)Cache[rowCountCachKey];
}
if (null == list || -1 == rowCount )
{
try
{

list = RetrievePage(pagenum, gvList.PageSize, ref rowCount);
}
catch (Exception)
{
MessageBox1.Show("خطا", "متاسفانه در هنگام بازیابی اطلاعات مشکلی پیش آمده است.", IconType.CriticalError, "Default.aspx", false);
return;
}

if (0 == list.Count && pagenum > 1)
Response.Redirect(Farahy.General.Uri.ChangeQuerySt ring("page", "1"));
}

ViewState["PageCount"] = (int)Math.Ceiling((float)rowCount / gvList.PageSize);

gvpnHeader.PageCount = (int)ViewState["PageCount"];
gvpnFooter.PageCount = (int)ViewState["PageCount"];

if (Cache[cachKey] != null)
{
Cache[cachKey] = list;
Cache[rowCountCachKey] = rowCount;
}
else
{
Cache.Insert(cachKey, list, null, DateTime.MaxValue,
TimeSpan.FromMinutes(10));
Cache.Insert(rowCountCachKey, rowCount, null, DateTime.MaxValue,
TimeSpan.FromMinutes(10));
}

gvList.DataSource = list;
gvList.DataBind();
}

متد فوق در کلاس Page صفحه ی مورد نظر شما به صورت Private وجود دارد. با هر بار فراخوانی این متد GrideView و یا DataList خود را به داده های بازیابی شده از دیتابیس Bind می کنید، البته به صورت بهینه.

بسیار خوب، پارامترهای ورودی به این متد pagenum و useCache هستند که به ترتیب تعیین کننده شماره ی صفحه مورد نظر و نحوه ی استفاده از Cache می باشند. useCache تعیین می کند که آیا از Cache استفاده بشود و یا خیر. بسته به موقعیت های مختلف می توانید این مقدار را به true و یا false تغییر بدین.

خطوط زیر یک کلید برای هر گزارش تولید می کنند. این کلید باید دربرگیرنده ی پارامترهای هر گزارش باشد. مثلا اگر شما می خواهید صفحه ای خاصی از جدول یا گزارش خاصی رو با نحوه ی مرتبط کردن خاصی و در کل محدودیت های خاصی بازیابی کنید، باید کلیدی ایجاد کنید که هر نمونه گزارش با پارامترها و محدودیت های مختلف رو از هم تمیز بده. منظورم از ... اشاره به سایر ویژگی های گزارش بوده.



string cachKey = "lst" + pagenum.ToString() +...
string rowCountCachKey = "rwqt" + pagenum.ToString() + ...

قبل از اینکه شما دیتای خودتون رو در کش ذخیره کنید مجبور هستید که آنها را از دیتابیس بازیابی کنید. کد زیر متدی از DAL رو فراخوانی می کنه که این عمل رو برای ما انجام می ده :



list = RetrievePage(pagenum, gvList.PageSize, ref rowCount);

بررسی نحوه ی کارکرد این متد در این مکان نمی گنجه ولی به طور خیلی ساده داخل این متد می تونه گزارش شبیه به گزارش زیر از دیتابیس درخواست بشه (شبه کد) :



public DataTable RetrievePage(int page, int pageSize, ref int rowCounts)
{
...
DbCommand cmd = SpecialDataAccess.CreateCommand();
cmd.CommandText = "GetRowsOfYourTable";
cmd.CommandType = CommandType.StoredProcedure;

... اضافه کردن پارامترهای اختیاری

SqlParameter pagenumber = new SqlParameter();
pagenumber.ParameterName = "@pagenumber";
pagenumber.DbType = DbType.Int32;
pagenumber.Value = page;
cmd.Parameters.Add(pagenumber);

SqlParameter pageSizeparam = new SqlParameter();
pageSizeparam.ParameterName = "@pageSize";
pageSizeparam.DbType = DbType.Int32;
pageSizeparam.Value = pageSize;
cmd.Parameters.Add(pageSizeparam);

SqlParameter howmany = new SqlParameter();
howmany.ParameterName = "@howmany";
howmany.DbType = DbType.Int32;
howmany.Value = rowCounts;
howmany.Direction = ParameterDirection.Output;
cmd.Parameters.Add(howmany);

DataTable data = SpecialDataAccess.ExecuteSelectCommand(cmd);

rowCounts = (int)howmany.Value;

return data;
}


و GetRowsOfYourTable شما می تواند به شکل زیر باشد.




CREATE PROCEDURE GetRowsOfYourTable
( // Your Constraints ...
@PageNumber INT,
@PageSize INT,
@HowManyProducts INT OUTPUT)
AS
-- declare a new TABLE variable
DECLARE @ttemp TABLE
(RowNumber INT, ... /* All your */ )

INSERT INTO @ttemp
SELECT ROW_NUMBER() OVER (ORDER BY YourTable.ID),
/* All your fields */
FROM YourTable
WHERE /* Apply your constraints */

SELECT @HowManyItems = COUNT(YourPrimaryKey) FROM @ttemp

SELECT *
FROM @ttemp
WHERE RowNumber > (@PageNumber - 1) * @PageSize
AND RowNumber <= @PageNumber * @PageSize


توجه داشته باشید که کدهای فوق صرفا جهت ارائه مسیر ارائه شده است و توانائی اجرائی از آنها سلب شده است.

در این روش ما از SP استفاده کرده ایم که خوب بدیهی ست ما را به استفاده از MSSQL محدود می کند ولی همان طور که قلا به آن اشاره شد هدف از ارائه راه حل صفحه بندی در این قسمت، UI می باشد و نه DAL . البته برای پاسخ گوئی به این سوال که اگر دیتابیس SP رو پشتیبانی نمی کرد آن وقث چه کار باید کنیم، باید عرض کنم روش های مختلفی وجود دارد که اساس کار تغریبا شبیه به هم است.
پیشنهاد بنده برای کارا و موثر تر بودن صفحه بندی, عدم استفاده از شماره صفحه برای دسترسی مستقیم به آن است. به جای آن می توانید از Next و Back خالی استفاده کنید.پردازش این عمل به منابع سیستم کمتری نیاز دارد ولی برای کاربر جذاب نیست.

بعد از گزارش گیری باید شماره صفحات را تولید کنیم. این مهم را کنترل سفارشی که کمی جلوتر آن را شرح خواهم داد انجام می دهد. تنها خصوصیت مهم این کنترل سفارشی خاصیت PageCount است که تعداد کل صفحات را معلوم می کند.



gvpnHeader.PageCount = (int)ViewState["PageCount"];
gvpnFooter.PageCount = (int)ViewState["PageCount"];


سورس کد کامل آن در زیر لیست شده است :



namespace Farahy.WCC
{
[DefaultProperty("Text")]
[ToolboxData("<{0}:GridViewPageNumberGenerator runat=server></{0}:GridViewPageNumberGenerator>")]
public class GridViewPageNumberGenerator : WebControl
{

...
protected override void CreateChildControls()
{
this.Controls.Clear();
HyperLink prev = new HyperLink();
prev.ID = this.ClientID + "prev";
prev.Text = PrevText;
prev.CssClass = this.CssClass;
this.Controls.Add(prev);
this.Controls.Add(new LiteralControl("&nbsp;&nbsp;"));
if (PageCount > 1 && CurrentPage > 1)
{
prev.NavigateUrl = Farahy.General.Uri.ChangeQueryString("page", (CurrentPage - 1).ToString());
prev.Enabled = true;
}
else
{
if (PageCount == 0)
prev.Visible = false;
else
prev.Enabled = false;
}
for (int i = 1; i <= PageCount; ++i)
{
HyperLink pageLink = new HyperLink();
pageLink.ID = this.ClientID + i.ToString();
pageLink.Text = i.ToString();
if (i != CurrentPage)
{
pageLink.NavigateUrl = Farahy.General.Uri.ChangeQueryString("page", i.ToString());
pageLink.Enabled = true;
}
else
pageLink.Enabled = false;
pageLink.CssClass = this.CssClass;
this.Controls.Add(pageLink);
this.Controls.Add(new LiteralControl("&nbsp;&nbsp;"));
}
HyperLink next = new HyperLink();
next.ID = this.ClientID + "next";
next.Text = NextText;
next.CssClass = this.CssClass;
this.Controls.Add(next);

if (PageCount > 1 && CurrentPage < PageCount)
{
next.NavigateUrl = Farahy.General.Uri.ChangeQueryString("page", (CurrentPage + 1).ToString());
next.Enabled = true;
}
else
{
if (PageCount == 0)
next.Visible = false;
else
next.Enabled = false;
}
}
...
}
}


بسته به نیاز خودتون می تونید این کنترل رو گسترش بدین ولی چیزی که واضح است اینه که شک نکنید در پروژه های بعدی خود به آن نیاز پیدا می کنید، پس هر چه قدر کامل ترش کنید ضرر نمی کنید.
برای استفاده سریع از آن می توانید این کنترل که ضمیمه این پاسخ شده است را دانلود کرده و به پروژه خودتو ضمیمه کنید بعد از کامپایل آن هر جا خواستید اعداد صفحات را تولید کنید، این کنترل را از روی Toolbox در آنجا drag & drop کنید و لذت ببرید.

در پایا ن در صفحه اصلی مثلا در رویداد Page_OnLoad می توانید کد زیر را جهت DataBind کردن و شروع همه چیز قرار دهید.



protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
try
{
pageNumber = int.Parse(Request.QueryString["page"]);
}
catch (Exception)
{
pageNumber = 1;
}
gvList_Databind(pageNumber, true);
}
}

صفحه ی aspx شما نیاز به چیزی ندارد. فقط این مطلب مهم است که اندازه صفحات خود را فقط از یکجا و آن هم از طریق PageSize کنترل GrideView و یا یک متغیر خصوصی Static تنظیم کنید که هر بار خواستید اندازه صفحات را تغییر دهید بدانید از کجا اقدام کنید.



<tr class="GrideViewNumberRow">
<td colspan="4" align="center" valign="bottom" style="height: 6px">
<cc2:GridViewPageNumberGenerator ID="gvpnHeader" runat="server" />
</td>
</tr>
<tr>
<td colspan="4" align="right">
<asp:GridView ID="gvList" PageSize="2" runat="server" AutoGenerateColumns="False" GridLines="None"
EmptyDataText=”هیچ آیتمی وجود ندارد">
...
</asp:GridView>
</td>
</tr>
<tr class="GrideViewNumberRow">
<td colspan="4" align="center">
<cc2:GridViewPageNumberGenerator ID="gvpnFooter" runat="server" />
</td>
</tr>

جهت مشاهده نمونه کاربردی این نوع صفحه بندی به آدرس http://mehrna.com مراجعه بفرمائید.

دانلود کنترل (http://mfarahy.persiangig.com/Components/GridViewPageNumberGenerator.rar)

sajad_boj70
یک شنبه 12 شهریور 1391, 16:26 عصر
دوست عزیز خیلی ممنون از پاسختون.
میشه لطف کنید یک پروژه sample بزراید که بهتر متوجه بشیم؟