Пейджер для DataList.

Как-то мне попался жуткий и громоздкий код пейджера на AspNetMania, от которого мне аж дурно стало и я решил выложить свой вариант. Он существенно короче и вполне нормально работает в нескольких проектах. К тому же, он сделан на бейсике, а не на шарпе.


Прежде всего - для чего нужны пейджеры? Странно, но MS оснастил GridView пейджером, а DataList - нет. Между тем, DataList явно более гибкий и удобный инструмент. Ну хотя бы потому, что он позволяет НЕСКОЛЬКО ЭЛЕМЕНТОВ ДАННЫХ РАЗМЕСТИТЬ В ОДНУ СТРОКУ. Примерно так, как на рисунке слева.


С другой стороны, стандартный пейджер у GridView - кривой, дальше некуда. Он вычитывает из базы ВСЕ ЭЛЕМЕНТЫ ДАННЫХ для отображения ДАЖЕ ОДНОЙ СТРАНИЦЫ. Странная кривизна. Ну у нас сто тысяч фрагментов данных - длинных-длинных, таких что вычитывание их из на Web-сервер может продолжаться ЧАСАМИ. Но нужна нам 10005-я страничка из пяти таких форагментов - для чего же читать все?

Поэтому любой ASP2-программер в первую очередь начинает с написания своего пейджера. Мой пейджер, который без изменения перекочевал во множество моих проектов - перед вами.

00001: Imports Microsoft.VisualBasic
00002: 
00003: ''' <summary>
00004: ''' Формирует тег пейджера 
00005: ''' </summary>
00006: Public Class PagerTag
00007:     Dim _MaxPageNum As Integer
00008:     Dim _CurPageNum As Integer
00009:     Dim HtmlTag As New StringBuilder("<div style='font-size:small'>")
00010: 
00011:     Public ReadOnly Property MaxPageNum() As Integer
00012:         Get
00013:             Return _MaxPageNum
00014:         End Get
00015:     End Property
00016:     Public ReadOnly Property CurPageNum() As Integer
00017:         Get
00018:             Return _CurPageNum
00019:         End Get
00020:     End Property
00021: 
00022:     Public Function GetHtmlTag(ByVal RecordsetCount As Integer, ByVal CurrentPageNum As Integer) As String
00023:         Dim KolPages As Integer = CInt(RecordsetCount \ CInt(System.Configuration.ConfigurationManager.AppSettings("PageSize")))
00024:         If KolPages = 0 Then
00025:             'это вариант, когда все элементы умещаются на первой страничке - просто "0" без ссылки генерить не будем
00026:             _MaxPageNum = 0
00027:             _CurPageNum = 0
00028:             Exit Function
00029:         End If
00030:         If RecordsetCount Mod CInt(System.Configuration.ConfigurationManager.AppSettings("PageSize")) > 0 Then KolPages += 1
00031:         If KolPages = 1 Then
00032:             'это вариант, когда все элементы умещаются на первой страничке - просто "0" без ссылки генерить не будем
00033:             _MaxPageNum = 0
00034:             _CurPageNum = 0
00035:             Exit Function
00036:         End If
00037:         _MaxPageNum = KolPages
00038:         If CurrentPageNum > KolPages - 1 Then
00039:             _CurPageNum = KolPages - 2 'уменьшили номер - если вдруг задано больше чем реальное количество страниц
00040:         Else
00041:             _CurPageNum = CurrentPageNum
00042:         End If
00043:         For I As Integer = 0 To KolPages - 1
00044:             If I <> CurrentPageNum Then
00045:                 HtmlTag.Append("<a href='" & URL.Parse("p", I.ToString) & "'>" & I.ToString & "</a>&nbsp;") 'тынц на таком линке будет Not IsPostback
00046:             Else
00047:                 HtmlTag.Append(I.ToString & "&nbsp;")
00048:             End If
00049:         Next
00050:         HtmlTag.Append("</div>")
00051:         Return HtmlTag.ToString
00052:     End Function
00053: End Class

Как вы видете, это простейший класс для формирования последовательности чисел, со ссылками, в которых параметром, передаваемым методом GET - выступает номер странички. В отличие от других пейджеров, мне лень было формировать тут троеточие, если страничек слишком много - если вы захотите его добавить - это две строчки. Этот класс удобно в Custom-контрол уложить, чтобы использовать в разных проектах.


Теперь посмотрим, как его вызывать.

00001:         'сначала дернули общее количество данных для пейджера
00002:         Dim DV1_c As Data.DataView = GetUserDataCount.Select(New DataSourceSelectArguments)
00003:         Dim MyPager As New PagerTag
00004:         PagerPlace.Text = MyPager.GetHtmlTag(DV1_c(0)("Count"), GetUserDataStrip.SelectParameters("PageNum").DefaultValue)
00005:         'пейджер определит если задан слишком большой номер и спозиционируеутся на последнюю страничку
00006:         If MyPager.CurPageNum < GetUserDataStrip.SelectParameters("PageNum").DefaultValue Then GetUserDataStrip.SelectParameters("PageNum").DefaultValue = MyPager.CurPageNum
00007:         'теперь соственно метаданные о рисунках - текущую порцию для пейджера
00008:         GetUserDataStrip.SelectParameters("GroupID").DefaultValue = g.Value
00009:         Dim DV1 As Data.DataView = GetUserDataStrip.Select(New DataSourceSelectArguments)
00010:         If DV1 IsNot Nothing Then
00011:             DataList1.DataSource = DV1
00012:             DataList1.DataBind()
00013:         Else
00014:             Throw New Exception("Не сработал GetUserDataStrip")
00015:         End If

Вызов его на страничке заключается в этих 15 строчках. Вы спросите - почему я не вынес их в конструктор этого класса или в сам метод, раз этот код повторяется?

Ответ прост. Я бы хотел конфигурить процедуры даступа к данным с удобным дизайн-таймовым интрефейсом. Да, это противоречит изысканным теоретическим концепциям - но это ЖУТКО УДОБНО. К тому же легко модицифируемо и проверяемо прямо в дизайн-таймовом интерфейсе студии:





Именно в этом удобстве все дело. И плюс в том, что эти же SqlDataSource можно использовать многократно и для других целей на той же форме.


Вот этот фрагмент кода, который вы видите в дизайнере, в текстовом виде выглядит вот так (нижний DataList, который лежит во View3).

00001: <%@ Page Language="VB" MasterPageFile="~/M1.master" AutoEventWireup="false" CodeFile="user_foto.aspx.vb"  Inherits="user_foto" %>
......
00006: <%@ Register Src="user_title.ascx" TagName="user_title" TagPrefix="uc1" %>
00007: <%@ Register Src="user_menu.ascx" TagName="user_menu" TagPrefix="uc2" %>
00008: <%@ Register Src="user_footer.ascx" TagName="user_footer" TagPrefix="uc3" %>
00009: <%@ Register Src="InActiveLogin.ascx" TagName="InActiveLogin" TagPrefix="uc4" %>
00010: <%@ Register Src="SqlUploader.ascx" TagName="SqlUploader" TagPrefix="uc5" %>
00011: 
00012: <asp:Content ID="Content1" ContentPlaceHolderID="ContentPlaceHolder1" runat="Server">
00013:     <asp:HiddenField ID="g" runat="server" />
00014:     <asp:HiddenField ID="t" runat="server" />
00015:     <asp:HiddenField ID="j" runat="server" />
00016:     <asp:HiddenField ID="ToUserName" runat="server" />
00017:     <uc1:user_title ID="User_title1" runat="server" />
00018:     <div style="height: 20px"></div>
......
00026:     <asp:MultiView ID="MultiView1" runat="server">
00027:         <asp:View ID="View1" runat="server">
00028:             <table>
00029:                 <tr>
00030:                     <td>
00031:                         <asp:DataList ID="DataList1" runat="server" RepeatColumns="3" ItemStyle-VerticalAlign="Bottom"
.....
00063:                         &lt;/asp:DataList>
00064:                         <asp:Literal ID="Pager1" runat="server"></asp:Literal></td>
00065:                 </tr>
00066:                 <tr>
00067:                     <td>
00068:                         <hr />
00069:                     </td>
00070:                 </tr>
00071:                 <tr>
00072:                     <td>
00073:                         <uc5:SqlUploader ID="SqlUploader1" runat="server" />
00074:                    </td>
00075:                 </tr>
00076:             </table>
00077:         </asp:View>
00078:         <asp:View ID="View2" runat="server">
00079:             <uc4:InActiveLogin ID="InActiveLogin1" runat="server" />
00080:         </asp:View>
00081:         <asp:View ID="View3" runat="server">
00082:             <asp:DataList ID="DataList2" runat="server" RepeatColumns="3" ItemStyle-VerticalAlign="Bottom">
00083:                 <ItemTemplate>
00084:                     <table>
00085:                         <tr>
00086:                             <td>
00087:                                 <asp:HyperLink ID="LinkToBig1" ToolTip="Просмотр в натуральную величину" Font-Size="Small"  Target="_blank" runat="server"></asp:HyperLink>
00088:                             </td>
00089:                         </tr>
00090:                         <tr>
00091:                             <td>
00092:                                 <asp:Literal ID="LinkToBig2" runat="server"></asp:Literal>
00093:                                 <asp:Image ID="Image1" runat="server" ToolTip="Слайд-просмотр фотографий" Width="150px" />
00094:                                 <br />
00095:                                 <asp:ImageButton ID="AddImageButton1" runat="server" ToolTip="Добавить комментарии" ImageUrl="~/Images/Add.png" Width="20px" />
00096:                                 <asp:ImageButton ID="ImageStat1" runat="server" ToolTip="Посмотреть статистику" ImageUrl="~/Images/Info1.png" Width="20px" />      
00097:                             </td>
00098:                         </tr>
00099:                     </table>
00100:                 </ItemTemplate>
00101:                 <ItemStyle VerticalAlign="Bottom" />
00102:             </asp:DataList>
00103:             <asp:Literal ID="Pager2" runat="server"></asp:Literal>
00104:         </asp:View>
00105:     </asp:MultiView>
00106:     <div style="height: 10px">
00107:     </div>
00108:     <uc3:user_footer ID="User_footer1" runat="server" />
00109:     <asp:SqlDataSource ID="GetUserDataStrip" runat="server" ConnectionString="<%$ ConnectionStrings:Votpusk %>"
00110:         SelectCommand="GetUserDataStrip" SelectCommandType="StoredProcedure">
00111:         <SelectParameters>
00112:             <asp:ControlParameter ControlID="ToUserName" DefaultValue="" Name="UserName" PropertyName="Value"
00113:                 Type="String" />
00114:             <asp:ControlParameter ControlID="g" Name="GroupID" PropertyName="Value" Type="Int32" />
00115:             <asp:Parameter DefaultValue="Фото" Name="ContentTypeName" Type="String" />
00116:             <asp:Parameter Name="PageSize" Type="Int32" DefaultValue="<%$ appSettings:PageSize %>" />
00117:             <asp:Parameter Name="PageNum" Type="Int32" />
00118:         </SelectParameters>
00119:     </asp:SqlDataSource>
00120:     <asp:SqlDataSource ID="GetUserDataCount" runat="server" ConnectionString="<%$ ConnectionStrings:Votpusk %>"
00121:         SelectCommand="GetUserDataCount" SelectCommandType="StoredProcedure">
00122:         <SelectParameters>
00123:             <asp:ControlParameter ControlID="ToUserName" Name="UserName" PropertyName="Value"
00124:                 Type="String" />
00125:             <asp:ControlParameter ControlID="g" Name="GroupID" PropertyName="Value" Type="Int32" />
00126:             <asp:Parameter DefaultValue="Фото" Name="ContentTypeName" Type="String" />
00127:         </SelectParameters>
00128:     </asp:SqlDataSource>
.....
00549: </asp:Content>

Как видите, наш пэйджер использован на этой страничке дважды - о чем я говорил выше, причем оба раза на одних и тех же СклДатаСорсах. О чем собственно и говорилось выше в обоснование того, почему вынос этих команд в класс (с утратой дизайн-таймового сервиса) - неудобен.

Обратите внимание, что размер странички пейджинга задается в конфиге:





Второй вариант этой же странички View1 - с тем же пейждером - предназначен уже для залогиненного пользователя и обладает совершенно ИНЫМИ возможностями.






Для завершения рассмотрения работы пейджера нам осталось рассмотреть еще два момента. Собственно пейджинговые процедуры в SQL и класс, который парсит URL.

Начнем с процедур.


В данном случае процедура с суффиксом Count - тривиальна и не будет тут рассматриваться. А вот процедура с суффиксом Strip - собственно пейджинговая процедура - ее мы посмотрим подробнее. (Здесь слишком много проектной специфики, поэтому мы рассмотрим только первую ветку процедуры):

00001: ALTER Procedure [dbo].[GetUserDataStrip]
00002: @UserName nvarchar (256),        [email protected]
00003: @GroupID int,                    --0 - альбом по умолчанию
00004: @ContentTypeName nvarchar (50),  --фото
00005: @PageSize as int = NULL,         --10 - размер странички пейждинга (если опущно - пейджинга нету)
00006: @PageNum as int = NULL           --0 -  текущий номер странички    (если опущно - пейджинга нету) 
00007: as
00008: --без собственно потока данных
00009: Declare @NULLGroupID int
00010: if (@GroupID=0) BEGIN
00011:     --отбор данных у которых ссылка на группу равна нулю
00012:     WITH All_Records as
00013:     (
00014:          select ROW_NUMBER() OVER (order by UserData.i) as [ROW_NUMBER], COUNT(*) OVER() as [ROW_COUNT], --это другой вариант
00015:          UserData.i, UserData.ToUser, UserData.ToContentType, UserData.ToGroup, 
00016:          UserData.Name, UserData.FileName, UserData.[Len], UserData.DataPostedType, UserData.LoadDate, UserData.IsPorn, 
00017:          UserData.IsNoComment, UserData.LastComment, (select max(i) from comments where ToUserData=UserData.i) as MaxComment
00018:          from UserData join vw_aspnet_MembershipUsers on vw_aspnet_MembershipUsers.UserId=UserData.ToUser
00019:          join ContentType on UserData.ToContentType=ContentType.i
00020:          where vw_aspnet_MembershipUsers.UserName=@UserName and ContentType.TypeName=@ContentTypeName 
00021:          and UserData.ToGroup is NULL
00022:     )
00023:     select * from All_Records
00024:     where [ROW_NUMBER]>ISNULL(@PageSize,1)*ISNULL(@PageNum,0) and [ROW_NUMBER]<=ISNULL(@PageSize,1)*ISNULL(@PageNum+1,1000) --макс колич записей, выдаваемое буз пейджинга
00025: END
00026: else BEGIN
00027:     WITH All_Records as
.....

Как видите, тут использовано CTE в нерекурсивном варианте. Обратите внимание, что эта процедура сдалана так хитро, что параметры пейджинга можно не задавать - тогда будет возвращен ВЕСЬ рекордсет. Точнее 1000 записей - ибо больше нет смысла выводить на клиента - слишком велик будет размер странички - она будет загружаться в браузер годами.


И наконец, последний момент, имеющий прямое отношение к пейджингу. Как вы видите на рисунке выше - текущий номер отображаемой странички - это просто значение параметра P, паредаваемое страничке GET-запросом. Именно он вычитывается в шестой строке фрагмента кода, где вызывается пейджер. И паредается параметром в SqlDataSource с именем GetUserDataStrip, а оттуда в одноименную процедуру. И процедура поднимает на форму затребованный фрагмент данных. В этом суть пейджинга.

Однако как корректно распрасить URL и заменить значение параметра P с числа 1 на число 2, 3 и так далее? Вызов этого парсера содержится в 45-й строке сабственно класса пейджера. К сожалению, MS не предусмотрела стандартного парсера URL - и каждый программер его пишет сам.

Его можно сделать очень по-разному, многие его делают на основе ParmArray, но я его делаю опять же как мне кажется предельно просто и эффективно:

00001: Imports Microsoft.VisualBasic
00002: 
00003: 
00004: ''' <summary>
00005: ''' Устанавливает указанное значение параметра в Request - переопределяя или добавляя параметр
00006: ''' </summary>
00007: Public Class URL
00008:     Public Shared Function Parse(ByVal Key1 As String, ByVal Value1 As String) As String
00009:         Dim Insert As Boolean = False
00010:         Dim PRM As New Collections.Specialized.NameValueCollection
00011:         For Each K As String In HttpContext.Current.Request.QueryString.AllKeys
00012:             If K = Key1 Then
00013:                 PRM.Add(K, Value1)
00014:                 Insert = True
00015:             Else
00016:                 PRM.Add(K, HttpContext.Current.Request.QueryString(K))
00017:             End If
00018:         Next
00019:         If Not Insert Then PRM.Add(Key1, Value1)
00020:         '
00021:         Dim NewUrl As New System.Text.StringBuilder(HttpContext.Current.Request.Url.Scheme & "://" & HttpContext.Current.Request.Url.Authority & HttpContext.Current.Request.Url.AbsolutePath)
00022:         If PRM.Count = 1 Then
00023:             NewUrl.Append("?" & PRM.Keys(0) & "=" & PRM.Item(0))
00024:         ElseIf PRM.Count > 1 Then
00025:             NewUrl.Append("?" & PRM.Keys(0) & "=" & PRM.Item(0))
00026:             For i As Integer = 1 To PRM.Count - 1
00027:                 NewUrl.Append("&" & PRM.Keys(i) & "=" & PRM.Item(i))
00028:             Next
00029:         End If
00030:         Return NewUrl.ToString
00031:     End Function
00032: 
00033:     Public Shared Function Parse(ByVal Key1 As String, ByVal Value1 As String, ByVal Key2 As String, ByVal Value2 As String) As String
00034:         Dim Insert() As Boolean = {False, False}
00035:         Dim PRM As New Collections.Specialized.NameValueCollection
00036:         For Each K As String In HttpContext.Current.Request.QueryString.AllKeys
00037:             If K = Key1 Then
00038:                 PRM.Add(K, Value1)
00039:                 Insert(0) = True
00040:             ElseIf K = Key2 Then
00041:                 PRM.Add(K, Value2)
00042:                 Insert(1) = True
00043:             Else
00044:                 PRM.Add(K, HttpContext.Current.Request.QueryString(K))
00045:             End If
00046:         Next
00047:         If Not Insert(0) Then PRM.Add(Key1, Value1)
00048:         If Not Insert(1) Then PRM.Add(Key2, Value2)
00049:         '
00050:         Dim NewUrl As New System.Text.StringBuilder(HttpContext.Current.Request.Url.Scheme & "://" & HttpContext.Current.Request.Url.Authority & HttpContext.Current.Request.Url.AbsolutePath)
00051:         If PRM.Count = 1 Then
00052:             NewUrl.Append("?" & PRM.Keys(0) & "=" & PRM.Item(0))
00053:         ElseIf PRM.Count > 1 Then
00054:             NewUrl.Append("?" & PRM.Keys(0) & "=" & PRM.Item(0))
00055:             For i As Integer = 1 To PRM.Count - 1
00056:                 NewUrl.Append("&" & PRM.Keys(i) & "=" & PRM.Item(i))
00057:             Next
00058:         End If
00059:         Return NewUrl.ToString
00060:     End Function
00061: 
00062:     Public Shared Function Parse(ByVal Key1 As String, ByVal Value1 As String, ByVal Key2 As String, ByVal Value2 As String, ByVal Key3 As String, ByVal Value3 As String) As String
00063:         Dim Insert() As Boolean = {False, False, False}
00064:         Dim PRM As New Collections.Specialized.NameValueCollection
00065:         For Each K As String In HttpContext.Current.Request.QueryString.AllKeys
00066:             If K = Key1 Then
00067:                 PRM.Add(K, Value1)
00068:                 Insert(0) = True
00069:             ElseIf K = Key2 Then
00070:                 PRM.Add(K, Value2)
00071:                 Insert(1) = True
00072:             ElseIf K = Key3 Then
00073:                 PRM.Add(K, Value3)
00074:                 Insert(2) = True
00075:             Else
00076:                 PRM.Add(K, HttpContext.Current.Request.QueryString(K))
00077:             End If
00078:         Next
00079:         If Not Insert(0) Then PRM.Add(Key1, Value1)
00080:         If Not Insert(1) Then PRM.Add(Key2, Value2)
00081:         If Not Insert(2) Then PRM.Add(Key3, Value3)
00082:         '
00083:         Dim NewUrl As New System.Text.StringBuilder(HttpContext.Current.Request.Url.Scheme & "://" & HttpContext.Current.Request.Url.Authority & HttpContext.Current.Request.Url.AbsolutePath)
00084:         If PRM.Count = 1 Then
00085:             NewUrl.Append("?" & PRM.Keys(0) & "=" & PRM.Item(0))
00086:         ElseIf PRM.Count > 1 Then
00087:             NewUrl.Append("?" & PRM.Keys(0) & "=" & PRM.Item(0))
00088:             For i As Integer = 1 To PRM.Count - 1
00089:                 NewUrl.Append("&" & PRM.Keys(i) & "=" & PRM.Item(i))
00090:             Next
00091:         End If
00092:         Return NewUrl.ToString
00093:     End Function
00094: 
00095:     Public Shared Function Parse(ByVal Key1 As String, ByVal Value1 As String, ByVal Key2 As String, ByVal Value2 As String, ByVal Key3 As String, ByVal Value3 As String, ByVal Key4 As String, ByVal Value4 As String) As String
00096:         Dim Insert() As Boolean = {False, False, False}
00097:         Dim PRM As New Collections.Specialized.NameValueCollection
00098:         For Each K As String In HttpContext.Current.Request.QueryString.AllKeys
00099:             If K = Key1 Then
00100:                 PRM.Add(K, Value1)
00101:                 Insert(0) = True
00102:             ElseIf K = Key2 Then
00103:                 PRM.Add(K, Value2)
00104:                 Insert(1) = True
00105:             ElseIf K = Key3 Then
00106:                 PRM.Add(K, Value3)
00107:                 Insert(2) = True
00108:             ElseIf K = Key4 Then
00109:                 PRM.Add(K, Value4)
00110:                 Insert(3) = True
00111:             Else
00112:                 PRM.Add(K, HttpContext.Current.Request.QueryString(K))
00113:             End If
00114:         Next
00115:         If Not Insert(0) Then PRM.Add(Key1, Value1)
00116:         If Not Insert(1) Then PRM.Add(Key2, Value2)
00117:         If Not Insert(2) Then PRM.Add(Key3, Value3)
00118:         If Not Insert(3) Then PRM.Add(Key4, Value4)
00119:         '
00120:         Dim NewUrl As New System.Text.StringBuilder(HttpContext.Current.Request.Url.Scheme & "://" & HttpContext.Current.Request.Url.Authority & HttpContext.Current.Request.Url.AbsolutePath)
00121:         If PRM.Count = 1 Then
00122:             NewUrl.Append("?" & PRM.Keys(0) & "=" & PRM.Item(0))
00123:         ElseIf PRM.Count > 1 Then
00124:             NewUrl.Append("?" & PRM.Keys(0) & "=" & PRM.Item(0))
00125:             For i As Integer = 1 To PRM.Count - 1
00126:                 NewUrl.Append("&" & PRM.Keys(i) & "=" & PRM.Item(i))
00127:             Next
00128:         End If
00129:         Return NewUrl.ToString
00130:     End Function
00131: 
00132:     Public Shared Function Parse(ByVal Key1 As String, ByVal Value1 As String, ByVal Key2 As String, ByVal Value2 As String, ByVal Key3 As String, ByVal Value3 As String, ByVal Key4 As String, ByVal Value4 As String, ByVal Key5 As String, ByVal Value5 As String) As String
00133:         Dim Insert() As Boolean = {False, False, False}
00134:         Dim PRM As New Collections.Specialized.NameValueCollection
00135:         For Each K As String In HttpContext.Current.Request.QueryString.AllKeys
00136:             If K = Key1 Then
00137:                 PRM.Add(K, Value1)
00138:                 Insert(0) = True
00139:             ElseIf K = Key2 Then
00140:                 PRM.Add(K, Value2)
00141:                 Insert(1) = True
00142:             ElseIf K = Key3 Then
00143:                 PRM.Add(K, Value3)
00144:                 Insert(2) = True
00145:             ElseIf K = Key4 Then
00146:                 PRM.Add(K, Value4)
00147:                 Insert(3) = True
00148:             ElseIf K = Key5 Then
00149:                 PRM.Add(K, Value5)
00150:                 Insert(4) = True
00151:             Else
00152:                 PRM.Add(K, HttpContext.Current.Request.QueryString(K))
00153:             End If
00154:         Next
00155:         If Not Insert(0) Then PRM.Add(Key1, Value1)
00156:         If Not Insert(1) Then PRM.Add(Key2, Value2)
00157:         If Not Insert(2) Then PRM.Add(Key3, Value3)
00158:         If Not Insert(3) Then PRM.Add(Key4, Value4)
00159:         If Not Insert(4) Then PRM.Add(Key4, Value5)
00160:         '
00161:         Dim NewUrl As New System.Text.StringBuilder(HttpContext.Current.Request.Url.Scheme & "://" & HttpContext.Current.Request.Url.Authority & HttpContext.Current.Request.Url.AbsolutePath)
00162:         If PRM.Count = 1 Then
00163:             NewUrl.Append("?" & PRM.Keys(0) & "=" & PRM.Item(0))
00164:         ElseIf PRM.Count > 1 Then
00165:             NewUrl.Append("?" & PRM.Keys(0) & "=" & PRM.Item(0))
00166:             For i As Integer = 1 To PRM.Count - 1
00167:                 NewUrl.Append("&" & PRM.Keys(i) & "=" & PRM.Item(i))
00168:             Next
00169:         End If
00170:         Return NewUrl.ToString
00171:     End Function
00172: 
00173: End Class


В принципе это все. Но как бы хотелось выбросить микрософтовскую кривизну - и выборосить микрософтосвкий пейджер из GridView. Но для этого придется немного напрячься.


Дело в том, что вписывание данных в шаблон DataList из ADO.NET-датасета содержит как правило сотни и тысячи строк. Дублировать такой код дважды или трижды - самоубийство. Конечно надо ОДИН экземпяр такого кода - на страничку, а лучше для всего проекта. НО ведь класс шаблона DataList отличен от класса шаблона GridView! На первый взгляд типизация нам подложила такую свинью, которую ну никак не проглотить!.

Но нет - нам помогут Дженерики! Взгляните на это фрагмент кода. Здесь данные из буфера ADO.NET загружаются в шаблоны GridView и DataList одной и той же процедурой.







В процессе обсуждения этой странички в инете, я получил несколько писем с просьбой показать - как я формирую символ гороскопа. Я отвечу на этот вопрос прямо на этой страничке. Хотя это к теме пейджинга отношения не имеет.

Увы, тут никакого GDI у меня нет - все просто - только 12 заведомо подготовленных рисунков с графикой:

00001: Imports Microsoft.VisualBasic
00002: 
00003: Public Class Zodiak
00004:     Public Shared Function Number(ByVal ForDate As DateTime) As Integer
00005:         Dim DayNumber As Integer = ForDate.Subtract(DateTime.Parse("01/01/" & ForDate.Year.ToString)).Days
00006:         If DayNumber >= 79 And DayNumber < 109 Then : Number = 1      'Овен  21.03-20.04 
00007:         ElseIf DayNumber >= 109 And DayNumber < 139 Then : Number = 2 'Телец  21.04-20.05 
00008:         ElseIf DayNumber >= 139 And DayNumber < 171 Then : Number = 3 'Близнецы  21.05-21.06 
00009:         ElseIf DayNumber >= 171 And DayNumber < 202 Then : Number = 4 'Рак  22.06-22.07 
00010:         ElseIf DayNumber >= 202 And DayNumber < 234 Then : Number = 5 'Лев  23.07-23.08 
00011:         ElseIf DayNumber >= 234 And DayNumber < 265 Then : Number = 6 'Дева  24.08-23.09 
00012:         ElseIf DayNumber >= 265 And DayNumber < 295 Then : Number = 7 'Весы  24.09-23.10 
00013:         ElseIf DayNumber >= 295 And DayNumber < 325 Then : Number = 8 'Скорпион  24.10-22.11 
00014:         ElseIf DayNumber >= 325 And DayNumber < 354 Then : Number = 9 'Стрелец  23.11-21.12 
00015:         ElseIf DayNumber >= 354 or DayNumber < 19 Then : Number = 10 'Козерог  22.12-20.01 
00016:         ElseIf DayNumber >= 19 And DayNumber < 50 Then : Number = 11  'Водолей  21.01-20.02 
00017:         ElseIf DayNumber >= 50 And DayNumber < 78 Then : Number = 12  'Рыбы  21.02-20.03 
00018:         End If
00019:     End Function
00020:     Public Shared Function Name(ByVal ForDate As DateTime) As String
00021:         Dim DayNumber As Integer = Number(ForDate)
00022:         Select Case DayNumber
00023:             Case 1 : Name = "Овен"
00024:             Case 2 : Name = "Телец"
00025:             Case 3 : Name = "Близнецы"
00026:             Case 4 : Name = "Рак"
00027:             Case 5 : Name = "Лев"
00028:             Case 6 : Name = "Дева"
00029:             Case 7 : Name = "Весы"
00030:             Case 8 : Name = "Скорпион"
00031:             Case 9 : Name = "Стрелец"
00032:             Case 10 : Name = "Козерог"
00033:             Case 11 : Name = "Водолей"
00034:             Case 12 : Name = "Рыбы"
00035:         End Select
00036:     End Function
00037:     Public Shared Function ImageName(ByVal ForDate As DateTime) As String
00038:         Dim DayNumber As Integer = Number(ForDate)
00039:         Select Case DayNumber
00040:             Case 1 : ImageName = "~/Images/small_oven.gif"
00041:             Case 2 : ImageName = "~/Images/small_telets.gif"
00042:             Case 3 : ImageName = "~/Images/small_bliznets.gif"
00043:             Case 4 : ImageName = "~/Images/small_rak.gif"
00044:             Case 5 : ImageName = "~/Images/small_lev.gif"
00045:             Case 6 : ImageName = "~/Images/small_deva.gif"
00046:             Case 7 : ImageName = "~/Images/small_vesi.gif"
00047:             Case 8 : ImageName = "~/Images/small_scorp.gif"
00048:             Case 9 : ImageName = "~/Images/small_strelets.gif"
00049:             Case 10 : ImageName = "~/Images/small_kozerog.gif"
00050:             Case 11 : ImageName = "~/Images/small_vodoley.gif"
00051:             Case 12 : ImageName = "~/Images/small_ribi.gif"
00052:         End Select
00053:     End Function
00054: End Class


Comments ( )
<00>  <01>  <02>  <03>  <04>  <05>  <06>  <07>  <08>  <09>  <10>  <11>  <12>  <13>  <14>  <15>  <16>  <17>  <18>  <19>  <20>  <21>  <22>  <23
Link to this page: //www.vb-net.com/asp2/5/default.htm
<SITEMAP>  <MVC>  <ASP>  <NET>  <DATA>  <KIOSK>  <FLEX>  <SQL>  <NOTES>  <LINUX>  <MONO>  <FREEWARE>  <DOCS>  <ENG>  <CHAT ME>  <ABOUT ME>  < THANKS ME>