Этюды на ASP.NET. Вложенный (nested) Datalist.
К сожалению, в микрософтовской объектной модели нет ни явного указания на элемент, сгенерировший постбек, нет нормальных тегов у контролов. Как, впрочем, нет и самих контролов в обычном понимании этого слова - универсальных элементов любого 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:
Надеюсь, код этих примерчиков оказался вам полезным. Удачи!
|