(ASP.NET) ASP NET (2010 год)

Этюды на ASP.NET. Вложенный (nested) Datalist.

У меня на сайте есть много описаний различных техник работ с сетками - например Типовые сеточные формы. Там я показывал как можно обойти отсутствие свойства TAG у обьектов микрософтовской обьектной модели ASP.NET с помощью размещения в сетках дополнительных HydenField. У меня есть много примеров с _EVENTTARGET, с которым у ASP.NET тоже большие проблемы. Когда от формы с клиента приходит постбек - вам нужно всегда знать именно в Page_Load от какого активного элемента %lt;input идет постбек с формы - например для того, чтобы инициализирвать динамические контролы (Динамическое добавление контролов).

К сожалению, в микрософтовской объектной модели нет ни явного указания на элемент, сгенерировший постбек, нет нормальных тегов у контролов. Как, впрочем, нет и самих контролов в обычном понимании этого слова - универсальных элементов любого Web-проекта, компилируемых в отдельном проекте с хорошим визуальным дизайн-таймовым интерфейсом изготовления этих универсальных компонентов. Поэтому приходится извращаться по разному - каждый раз решая те же самые типовые задачки и борясь с теми же самыми недостатками обьектной модели ASP.NET.

Здесь я покажу технику решения наших типичных асповских проблем несколько в другой технике (без _EVENTTARGET и HydenFields) на примере вложенных (Nested) Datalist. В плане техники программирования этот топик является также продолжением не только топика Типовые сеточные формы, но и топика Выражения привязки и топика Пейджер для DataList.

Это крошечный фрагмент кода из реального моего проекта - http://arenda.votpusk.ru/. Я покажу достаточно универсальный фрагмент - работы с валютами и счетами пользователей. Это фрагмент у меня повторяется буквально десятки раз. Самый мой крупный и долго работающий проект - с этим фрагментом Дигиталшоп (процедуры, пользовательский интерфейс.). А семантически этот топик является продолжение топика Шлюзы к платежным системам интернет-денег.


Итак, для начала поймем самое простейшее применение Datalist - для отображения одного рекордсета. Рекордсет (и способ его формирвоания) я показал в начале этой странички - Выполняем разворот строк в столбцы в MS SQL и PostgreSQL. - теперь посмотрим как получать такую форму, как вы видите слева:


   1:  <%@ Page Language="VB" MasterPageFile="~/CMN/M2.master" AutoEventWireup="false" CodeFile="admin_arenda_curs.aspx.vb" Inherits="admin_arenda_curs"  %>
   2:   
   3:  <asp:Content ID="Content1" ContentPlaceHolderID="ContentPlaceHolder1" Runat="Server">
   4:  <h4>Курсы валют</h4>
   5:   
   6:      <asp:DataList ID="DataList1" runat="server" DataMember="DefaultView" 
   7:          DataSourceID="GetCursValut">
   8:      <HeaderTemplate>
   9:                 Из валюты
  10:              </td><td>
  11:                 В валюту
  12:              </td><td>
  13:                 Курс
  14:      </HeaderTemplate>
  15:          <ItemTemplate>
  16:              <asp:Label ID="lFromName" runat="server" Text='<%# Eval("FromVal_Name") %>' ></asp:Label>
  17:              </td><td>
  18:              <asp:Label ID="lToName" runat="server" Text='<%# Eval("ToVal_Name") %>' ></asp:Label>
  19:              </td><td>
  20:              <asp:TextBox ID="txCurs" runat="server" Text='<%# Eval("Kurs") %>' Width="80px"></asp:TextBox>
  21:          </ItemTemplate>
  22:      </asp:DataList>
  23:      <asp:Button ID="Button1" runat="server" Text="Set" Width="80px" />
  24:      <br /><asp:Label ID="lErr1" runat="server" ForeColor="Red" ></asp:Label>
  25:   
  26:      
  27:      <asp:SqlDataSource ID="GetCursValut" runat="server" ConnectionString="<%$ ConnectionStrings:Arenda_ConnectionStrings %>"
  28:          SelectCommand="select * from CursValut">
  29:      </asp:SqlDataSource>
  30:   
  31:      <asp:SqlDataSource ID="UpdateCurs" runat="server" ConnectionString="<%$ ConnectionStrings:Arenda_ConnectionStrings %>"
  32:          SelectCommand="Update dbo.Kurs set Kurs=@Curs where Valuta=@ToValuta and FromValuta=@FromValuta">
  33:          <SelectParameters>
  34:              <asp:Parameter Name="Curs" Type="Decimal" />
  35:              <asp:Parameter Name="ToValuta" Type="Int32" />
  36:              <asp:Parameter Name="FromValuta" Type="Int32" />
  37:          </SelectParameters>
  38:      </asp:SqlDataSource>
  39:   
  40:  </asp:Content>
  41:   
   1:  Partial Class admin_arenda_curs
   2:      Inherits System.Web.UI.Page
   3:   
   4:      Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
   5:          If Not IsPostBack Then
   6:              DataList1.DataBind()
   7:          End If
   8:      End Sub
   9:   
  10:      Protected Sub DataList1_ItemDataBound(ByVal sender As Object, ByVal e As System.Web.UI.WebControls.DataListItemEventArgs) Handles DataList1.ItemDataBound
  11:          If e.Item.DataItem IsNot Nothing Then
  12:              Dim txCurs As TextBox = CType(e.Item.FindControl("txCurs"), TextBox)
  13:              txCurs.ToolTip = e.Item.DataItem("FromVal_i") & ";" & e.Item.DataItem("ToVal_i")
  14:          End If
  15:      End Sub
  16:   
  17:      Protected Sub Button1_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles Button1.Click
  18:          lErr1.Text = ""
  19:          Try
  20:              For Each OneRow As WebControls.DataListItem In DataList1.Items
  21:                  Dim OneTextBox As TextBox = OneRow.FindControl("txCurs")
  22:                  Dim NewValue As Decimal = OneTextBox.Text
  23:                  Dim X() As String = OneTextBox.ToolTip.Split(";")
  24:                  Dim FromVal_i As Integer = CInt(X(0))
  25:                  Dim ToVal_i As Integer = CInt(X(1))
  26:                  UpdateCurs.SelectParameters("Curs").DefaultValue = NewValue
  27:                  UpdateCurs.SelectParameters("FromValuta").DefaultValue = FromVal_i
  28:                  UpdateCurs.SelectParameters("ToValuta").DefaultValue = ToVal_i
  29:                  UpdateCurs.Select(New DataSourceSelectArguments)
  30:              Next
  31:          Catch ex As Exception
  32:              lErr1.Text = ex.Message
  33:          End Try
  34:          DataList1.DataBind()
  35:      End Sub
  36:  End Class

Как видите, это простейший способ использования DataList - даже без пейджинга. Теперь посмотрим на гораздо более поучительный примерчик:


Структуру данных, которую я пытаюсь отобразить на этой форме вы можете посмотреть в топике Выполняем разворот строк в столбцы в MS SQL и PostgreSQL..


   1:  <%@ Page Language="VB" MasterPageFile="~/CMN/M2.master" AutoEventWireup="false" CodeFile="admin_arenda_cost.aspx.vb" Inherits="admin_arenda_cost" %>
   2:   
   3:  <asp:Content ID="i" ContentPlaceHolderID="ContentPlaceHolder1" Runat="Server">
   4:   
   5:      <h4>Стоимость операций</h4>
   6:   
   7:      <asp:DataList ID="DataList1" runat="server" DataKeyField="i" 
   8:          DataMember="DefaultView" DataSourceID="GetAction">
   9:          <ItemTemplate>
  10:              <asp:Label ID="lActionName" runat="server" Text='<%# Eval("Name") %>' ToolTip='<%# Eval("i") %>'></asp:Label>
  11:              <asp:DataList ID="DataList2" runat="server" DataKeyField="i" 
  12:                  DataMember="DefaultView" DataSourceID="GetAccountType">
  13:                  <ItemTemplate>
  14:                      <asp:Label ID="AccountTypeName" runat="server" Text='<%# Eval("Name") %>'></asp:Label>
  15:                      </td><td>
  16:                      <asp:TextBox ID="AccountTypeCost" runat="server" Width="50"  ToolTip='<%# GetToolTip(Container.DataItem("i")) %>' Text='<%# GetValue1(Container.DataItem("i")) %>' ></asp:TextBox>
  17:                  </ItemTemplate>
  18:              </asp:DataList>
  19:              <br />
  20:          </ItemTemplate>
  21:      </asp:DataList>
  22:      <asp:Button ID="Button1" runat="server" Text="Set" Width="80" />
  23:      <br />
  24:      <asp:Label ID="lErr1" runat="server" ForeColor="Red" ></asp:Label>
  25:      <asp:SqlDataSource ID="GetAction" runat="server" 
  26:          ConnectionString="<%$ ConnectionStrings:Arenda_ConnectionStrings %>" 
  27:          SelectCommand="select * from vOtpusk_Arenda.dbo.ActionType order by vOtpusk_Arenda.dbo.ActionType.OrderBy asc">
  28:      </asp:SqlDataSource>
  29:      
  30:      <asp:SqlDataSource ID="GetAccountType" runat="server"
  31:              ConnectionString="<%$ ConnectionStrings:Arenda_ConnectionStrings %>" 
  32:          SelectCommand="select * from vOtpusk_Arenda.dbo.AccountType order by vOtpusk_Arenda.dbo.AccountType.OrderBy asc">
  33:      </asp:SqlDataSource>
  34:   
  35:      <asp:SqlDataSource ID="GetActionCost" runat="server"
  36:              ConnectionString="<%$ ConnectionStrings:Arenda_ConnectionStrings %>" 
  37:          SelectCommand="select * from ActionCost where ToCostType=@ToCostType and ToActionType=@ToActionType">
  38:              <SelectParameters>
  39:                  <asp:Parameter Name="ToCostType" Type="Int32" />
  40:                  <asp:Parameter Name="ToActionType" Type="Int32" />
  41:              </SelectParameters>      
  42:      </asp:SqlDataSource>
  43:        
  44:      
  45:      <asp:SqlDataSource ID="SetActionCost" runat="server"
  46:              ConnectionString="<%$ ConnectionStrings:Arenda_ConnectionStrings %>" 
  47:          SelectCommand="SetActionCost" SelectCommandType="StoredProcedure">
  48:              <SelectParameters>
  49:                  <asp:Parameter Name="ToCostType" Type="Int32" />
  50:                  <asp:Parameter Name="ToActionType" Type="Int32" />
  51:                  <asp:Parameter Name="Cost" Type="Int32" />
  52:              </SelectParameters>      
  53:        </asp:SqlDataSource>  
  54:        
  55:  </asp:Content>
  56:   
  57:   

Видите в чем ключик этого кода? Оба ключа для одновления базы я вписал не в HiddensFiels, не в копию, хранимую в Session, связанную с отображением на форме по номеру строки, не клиенским кодом, а напрямую в tooltip. И этим решил основную убогость ASP.NET - отсутсвие четкого понимания от кого идет постбек на ранних этапах построения странички и отсутсвия нормальных тегов на со ссылками на внутренние структуры данных у клиентских обьектов странички.

А вот код странички:


   1:  Partial Class admin_arenda_cost
   2:      Inherits System.Web.UI.Page
   3:   
   4:      Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
   5:          If Not IsPostBack Then
   6:              DataList1.DataBind()
   7:          End If
   8:      End Sub
   9:   
  10:      Protected Sub DataList1_ItemDataBound(ByVal sender As Object, ByVal e As System.Web.UI.WebControls.DataListItemEventArgs) Handles DataList1.ItemDataBound
  11:          If e.Item.DataItem IsNot Nothing Then
  12:              Dim CurrentAction As Integer = e.Item.DataItem("i")
  13:              _CurrentAction = CurrentAction
  14:              Dim Datalist2 As DataList = CType(e.Item.FindControl("Datalist2"), DataList)
  15:              Datalist2.DataBind()
  16:   
  17:          End If
  18:      End Sub
  19:   
  20:      Dim _CurrentAction As Integer
  21:      Function GetValue1(ByVal AccountTypeI As Integer) As String
  22:          If _CurrentAction <> 0 And AccountTypeI <> 0 Then
  23:              GetActionCost.SelectParameters("ToCostType").DefaultValue = AccountTypeI
  24:              GetActionCost.SelectParameters("ToActionType").DefaultValue = _CurrentAction
  25:              Dim ActionCost As Data.DataView = GetActionCost.Select(New DataSourceSelectArguments)
  26:              If ActionCost Is Nothing Then
  27:                  Return "000"
  28:              Else
  29:                  If ActionCost.Count = 0 Then
  30:                      Return "000"
  31:                  Else
  32:                      If IsDBNull(ActionCost(0)("Cost")) Then
  33:                          Return "0"
  34:                      Else
  35:                          Return String.Format("{0:###000}", ActionCost(0)("Cost"))
  36:                      End If
  37:                  End If
  38:              End If
  39:          End If
  40:      End Function
  41:   
  42:      Function GetToolTip(ByVal AccountTypeI As Integer) As String
  43:          If _CurrentAction <> 0 And AccountTypeI <> 0 Then
  44:              Return _CurrentAction.ToString & ";" & AccountTypeI.ToString
  45:          End If
  46:      End Function
  47:   
  48:      Protected Sub Button1_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles Button1.Click
  49:          lErr1.Text = ""
  50:          For Each OneKey As String In Request.Form.AllKeys
  51:              If OneKey.Contains("AccountTypeCost") Then
  52:                  Try
  53:                      Dim OneTextBox As TextBox = CType(Me.Form.FindControl(OneKey), TextBox)
  54:                      Dim NewValue As Integer = CInt(OneTextBox.Text)
  55:                      Dim X() As String = OneTextBox.ToolTip.Split(";")
  56:                      Dim CurrentAction As Integer = CInt(X(0))
  57:                      Dim AccountTypeI As Integer = CInt(X(1))
  58:                      SetActionCost.SelectParameters("Cost").DefaultValue = NewValue
  59:                      SetActionCost.SelectParameters("ToCostType").DefaultValue = AccountTypeI
  60:                      SetActionCost.SelectParameters("ToActionType").DefaultValue = CurrentAction
  61:                      SetActionCost.Select(New DataSourceSelectArguments)
  62:                  Catch ex As Exception
  63:                      lErr1.Text = ex.Message
  64:                  End Try
  65:              End If
  66:          Next
  67:          DataList1.DataBind()
  68:      End Sub
  69:  End Class

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

Многие спотыкаются на этой задачке и решают ее заменяя (из-за указанных недостатков движка ASP.NET) вложенный асповский контрол на ручное формирование тегов. В принципе так поступать тоже можно. Например так я поступил при формировании вот этой странички:




 982:    Protected Sub DataList1_ItemDataBound(ByVal sender As Object, ByVal e As System.Web.UI.WebControls.DataListItemEventArgs) Handles DataList1.ItemDataBound
 983:          If e.Item.DataItem IsNot Nothing Then
 984:              Dim TitleLink1 As HyperLink = CType(e.Item.FindControl("TitleLink1"), HyperLink)
 985:              TitleLink1.NavigateUrl = "Default.aspx?Product_ID=" & e.Item.DataItem("ID").ToString
 986:              Dim Image1 As Image = CType(e.Item.FindControl("Image1"), Image)
 987:              Image1.ImageUrl = "GetImage.ashx?id=" & e.Item.DataItem("ID").ToString
 988:              Dim LinkLiteral1 As Literal = CType(e.Item.FindControl("LinkLiteral1"), Literal)
 989:              LinkLiteral1.Text = "<a onclick=""window.open('','" & e.Item.DataItem("Name") & "','resizable=no,menubar=no,scrollbars=no);""  name='" & e.Item.DataItem("Name") & "' target=" & e.Item.DataItem("Name") & " title='" & e.Item.DataItem("Name") & "' href='GetImage.ashx?id=" & e.Item.DataItem("ID").ToString & "&title=" & e.Item.DataItem("Name") & "' >"
 990:              Dim DescrLiteral1 As Literal = CType(e.Item.FindControl("DescrLiteral1"), Literal)
 991:              DescrLiteral1.Text = e.Item.DataItem("SmallDescription") & "<a href='" & e.Item.DataItem("DetailsURL") & "' target='_blank'>Подробнее</a>"
 992:              'в этот литерал собирается много строк с версиями
 993:              '<tr><td>Цена</td><td>Скачать</td><td>Версия</td><td>Дата</td><td>Замечания</td></tr>
 994:              Dim VersionLiteral1 As Literal = CType(e.Item.FindControl("VersionLiteral1"), Literal)
 995:              Dim ManyRows As New StringBuilder("<tr style=""height: 20px; background-color: #dddddd;""><td>Цена</td><td>Сcылка</td><td>Версия</td><td>Дата</td><td>Замечания</td></tr>")
 996:              GetVersion.SelectParameters("ToProducts").DefaultValue = e.Item.DataItem("i").ToString
 997:              Dim DV2 As Data.DataView = GetVersion.Select(New DataSourceSelectArguments)
 998:              If DV2 IsNot Nothing Then
 999:                  If DV2.Count > 0 Then
1000:                      For i As Integer = 0 To DV2.Count - 1
1001:                          ManyRows.Append("<tr  style='background-color: #ffffff; vertical-align:top'><td>")
1002:                          ManyRows.Append(String.Format("{0:N2}", DV2(i)("Cost")))
1003:                          ManyRows.Append("</td><td>")
1004:                          ManyRows.Append("<a href=GetExe.ashx?Product_ID=")
1005:                          ManyRows.Append(e.Item.DataItem("ID").ToString)
1006:                          ManyRows.Append("&Version=")
1007:                          ManyRows.Append(DV2(i)("AssemblyVersion"))
1008:                          ManyRows.Append(">Скачать</a>")
1009:                          ManyRows.Append("</td><td>")
1010:                          ManyRows.Append(DV2(i)("AssemblyVersion"))
1011:                          ManyRows.Append("</td><td>")
1012:                          ManyRows.Append(CDate(DV2(i)("Date")).ToShortDateString)
1013:                          ManyRows.Append("</td><td>")
1014:                          If HttpContext.Current.User.Identity.IsAuthenticated Then
1015:                              If Roles.IsUserInRole("HostingAdmin") Then
1016:                                  ManyRows.Append("<a style='border:0px' href='DownloadLog.aspx?Product_ID=")
1017:                                  ManyRows.Append(e.Item.DataItem("ID").ToString)
1018:                                  ManyRows.Append("&i=")
1019:                                  ManyRows.Append(DV2(i)("i").ToString())
1020:                                  ManyRows.Append("'><img style='border:0px' src='images/Log.gif'></a>")
1021:                                  '
1022:                                  ManyRows.Append("<a style='border:0px' href='ChangeVersion.aspx?Product_ID=")
1023:                                  ManyRows.Append(e.Item.DataItem("ID").ToString)
1024:                                  ManyRows.Append("&i=")
1025:                                  ManyRows.Append(DV2(i)("i").ToString())
1026:                                  ManyRows.Append("'><img style='border:0px' src='images/Edit.gif'></a>")
1027:                              End If
1028:                          End If
1029:                          ManyRows.Append(DV2(i)("Note"))
1030:                          ManyRows.Append("</td></tr>")
1031:                          ManyRows.AppendLine()
1032:                      Next
1033:                      VersionLiteral1.Text = ManyRows.ToString
1034:                  End If
1035:              End If
1036:              Dim BugTrackerLink1 As HyperLink = CType(e.Item.FindControl("BugTrackerLink1"), HyperLink)
1037:              Dim UpButton1 As ImageButton = CType(e.Item.FindControl("UpButton1"), ImageButton)
1038:              Dim DownButton1 As ImageButton = CType(e.Item.FindControl("DownButton1"), ImageButton)
1039:              Dim AddVersionButton1 As ImageButton = CType(e.Item.FindControl("AddVersionButton1"), ImageButton)
1040:              Dim ChangeImage1 As ImageButton = CType(e.Item.FindControl("ChangeImage1"), ImageButton)
1041:              Dim EditDescrButton1 As ImageButton = CType(e.Item.FindControl("EditDescrButton1"), ImageButton)
1042:              Dim GoForumLink As Literal = CType(e.Item.FindControl("GoForumLink1"), Literal)
1043:              Dim GoForumImage As Image = CType(e.Item.FindControl("GoForumImage"), Image)
1044:              Dim Download_Count As Literal = CType(e.Item.FindControl("Download_Count"), Literal)
1045:              If IsDBNull(e.Item.DataItem("ForumTopic")) Then
1046:                  GoForumImage.ImageUrl = "Images/NoForum.gif"
1047:                  GoForumLink.Text = "<a href='#'>"
1048:              Else
1049:                  GoForumImage.ImageUrl = "Images/Forum.gif"
1050:                  GoForumLink.Text = "<a onclick=""window.open('','Forum','resizable=no,menubar=no,scrollbars=no,width=600,height=700');""  name='Forum' target='Forum' title='forum.vb-net.com' href=""//forum.vb-net.com/Forum.aspx?id=" & e.Item.DataItem("ForumTopic").ToString & """ > "
1051:              End If
1052:              BugTrackerLink1.NavigateUrl = "http://bug.vb-net.com/OneProject.aspx?Product_ID=" & e.Item.DataItem("ID").ToString
1053:              If HttpContext.Current.User.Identity.IsAuthenticated Then
1054:                  If Roles.IsUserInRole("HostingAdmin") Then
1055:                      AddVersionButton1.PostBackUrl = "LoadVersion.aspx?Product_ID=" & e.Item.DataItem("ID").ToString
1056:                      ChangeImage1.PostBackUrl = "ChangeScreen.aspx?Product_ID=" & e.Item.DataItem("ID").ToString
1057:                      UpButton1.CommandName = e.Item.DataItem("ID").ToString
1058:                      DownButton1.CommandName = e.Item.DataItem("ID").ToString
1059:                      EditDescrButton1.PostBackUrl = "ChangeDescr.aspx?Product_ID=" & e.Item.DataItem("ID").ToString
1060:                  Else
1061:                      AddVersionButton1.Visible = False
1062:                      ChangeImage1.Visible = False
1063:                      UpButton1.Visible = False
1064:                      DownButton1.Visible = False
1065:                      EditDescrButton1.Visible = False
1066:                  End If
1067:              Else
1068:                  AddVersionButton1.Visible = False
1069:                  ChangeImage1.Visible = False
1070:                  UpButton1.Visible = False
1071:                  DownButton1.Visible = False
1072:                  EditDescrButton1.Visible = False
1073:              End If
1074:              Dim DownloadCount As Data.DataView
1075:              GetDownloadCount.SelectParameters("Product_ID").DefaultValue = e.Item.DataItem("ID").ToString
1076:              DownloadCount = GetDownloadCount.Select(New DataSourceSelectArguments)
1077:              If DownloadCount IsNot Nothing Then
1078:                  If DownloadCount.Count > 0 Then
1079:                      If DownloadCount(0)("Download_Count") IsNot Nothing Then
1080:                          Download_Count.Text = "Скачано " & DownloadCount(0)("Download_Count") & " раз"
1081:                      End If
1082:                  End If
1083:              End If
1084:          End If
1085:      End Sub
1086:   

Надеюсь, код этих примерчиков оказался вам полезным. Удачи!



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/NestedDatalist/index.htm
<SITEMAP>  <MVC>  <ASP>  <NET>  <DATA>  <KIOSK>  <FLEX>  <SQL>  <NOTES>  <LINUX>  <MONO>  <FREEWARE>  <DOCS>  <ENG>  <CHAT ME>  <ABOUT ME>  < THANKS ME>