Мой первый фото-слайдер на Flex 4
To view this page ensure that Adobe Flash Player version 10.0.0 or greater is installed.
В начале 2011-го года я познакомился с платформой Flex и эту страничку я бы хотел посвятить своему первому Flex-компоненту. Возможно мой взгляд на веши будет полезен многим ASP.NET-программистам. Но вначале я бы хотел остановиться на теме фотослайдинга вообще и почему прочие применяемые технологии тут (как и во многих других областях) существенно уступают компоненту на Flex.
В принципе фото-слайдеры можно писать на яваскрипте самому, но врядли эта трудоемкая работа экономически эффективна. Скорее всего выгоднее использовать какой-то готовый фреймворк, чтобы облегчить кодинг. Наиболее широкоизвестных фреймворков два - JQUERY и ASP.NET AJAX. До того, как я решился сделать компонент на Flex, я писал на обоих этих фремворках.
Сначала я расскажу о своих фотосладерах на Microsoft AJAX. Такие мои фото-слайдеры вы можете увидеть например на сайте вотпуска (надеюсь они там проживут долго). Тыкнув со странички http://arenda.votpusk.ru/ в обьявление любого пользователя сайта, вы попадете на страничку описания конкретного обьекта конкретного пользователя. Внизу этой странички - несколько фоток. Тыкните в любую из них - и на страничке http://arenda.votpusk.ru/Object_foto.aspx?Object=XXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX&i=Y вы попадете в мой слайдер на Microsoft AJAX.
В упрощенном виде разметка этой странички выглядит вот так:
1: <form id="form1" runat="server">
2: <div style="margin: 20px">
3: <asp:ScriptManager ID="ScriptManager1" runat="server">
4: </asp:ScriptManager>
5: <asp:Label ID="lKurCn" runat="server"></asp:Label><br>
6: <asp:HyperLink runat="server" ID="TitleHyperLink" Target="_blank">
7: <asp:Label ID="lTitle" runat="server" Font-Bold="True" Font-Names="Arial" Font-Size="14px"></asp:Label></asp:HyperLink>
8: <table style="width: 100%;" cellpadding="0" cellspacing="0">
9: <tr>
10: <td valign="top">
11: <asp:UpdatePanel ID="UpdatePanel2" runat="server" RenderMode="Inline" UpdateMode="Conditional">
12: <ContentTemplate>
13: <asp:Label runat="server" ID="ImageTitle" CssClass="txnn"></asp:Label>
14: <br>
15: <br>
16: <table cellpadding="0" cellspacing="0" align="left">
17: <tr>
18: <td align="left" valign="top" height="30px">
19: <asp:LinkButton runat="server" ID="LinkBack1">предыдущая</asp:LinkButton>
20: </td>
21: <td valign="top">
22: <center>
23: <asp:HyperLink ID="BigImageButton" ToolTip="исходный размер" Target="_blank" runat="server"
24: ImageUrl="~/Images/lu.gif"></asp:HyperLink><asp:HyperLink ID="BigImageButton2" ToolTip="исходный размер"
25: Target="_blank" runat="server">исходный размер</asp:HyperLink>
26: </center>
27: </td>
28: <td align="right" valign="top">
29: <asp:LinkButton ID="Link1Forward1" runat="server">следующая</asp:LinkButton>
30: </td>
31: </tr>
32: <tr>
33: <td align="center" valign="middle" height="560px" bgcolor="#E7E3E7" width="560px"
34: colspan="3">
35: <asp:ImageButton ID="ImageButton1" runat="server" BorderWidth="10px" BorderColor="White"
36: BorderStyle="Solid" />
37: </td>
38: </tr>
39: </table>
40: </ContentTemplate>
41: <Triggers>
42: <asp:AsyncPostBackTrigger ControlID="Link1Forward1" EventName="Click" />
43: <asp:AsyncPostBackTrigger ControlID="LinkBack1" EventName="Click" />
44: <asp:AsyncPostBackTrigger ControlID="ImageButton1" EventName="Click" />
45: </Triggers>
46: </asp:UpdatePanel>
47: </td>
48: <td width="150" valign="bottom" align="right">
49: <asp:Panel runat="server" ID="panFoto">
50: <table width="120" cellpadding="0" cellspacing="0">
51: <tr>
52: <td align="center" valign="middle" height="25px" bgcolor="#E7E3E7">
53: <asp:LinkButton ID="ToRight" runat="server">
54: <asp:Image ID="Image1" runat="server" ImageUrl="Images/st_top.gif" ImageAlign="Middle">
55: </asp:Image></asp:LinkButton>
56: </td>
57: </tr>
58: <tr>
59: <td height="5px">
60: </td>
61: </tr>
62: <tr>
63: <td valign="top">
64: <asp:UpdatePanel ID="UpdatePanel1" runat="server" RenderMode="Inline" UpdateMode="Conditional">
65: <ContentTemplate>
66: <asp:DataList ID="DataList1" runat="server" DataKeyField="ID" DataMember="DefaultView"
67: CellPadding="0" HorizontalAlign="Center" RepeatColumns="1">
68: <FooterStyle Font-Bold="True" ForeColor="White" />
69: <ItemTemplate>
70: <%-- <asp:HiddenField ID="HiddenField1" runat="server" Value='<%# Eval("ID") %>' />--%>
71: <table cellpadding="0" cellspacing="0">
72: <tr>
73: <td colspan="3">
74: <img alt="" src="http://www.imgvotpusk.ru/Images/1.gif" width="120px" height="1px" />
75: </td>
76: </tr>
77: <tr>
78: <td>
79: <img alt="" src="http://www.imgvotpusk.ru/Images/1.gif" height="125px" width="1px" />
80: </td>
81: <td align="center" valign="top">
82: <asp:UpdatePanel ID="UpdatePanel3" runat="server" RenderMode="Inline" UpdateMode="Conditional">
83: <ContentTemplate>
84: <table bgcolor="#e7e3e7" style="width: 120px; height: 120px;">
85: <tr>
86: <td valign="middle" align="center">
87: <asp:ImageButton ID="SlideImageButton" runat="server" OnClick="SlideImageButton_Click" />
88: </td>
89: </tr>
90: </tbody>
91: </table>
92: </ContentTemplate>
93: <Triggers>
94: <asp:AsyncPostBackTrigger ControlID="SlideImageButton" EventName="Click" />
95: </Triggers>
96: </asp:UpdatePanel>
97: </td>
98: <td>
99: <img alt="" src="http://www.imgvotpusk.ru/Images/1.gif" height="120px" width="1px" />
100: </td>
101: </tr>
102: <tr>
103: <td colspan="3">
104: <img alt="" src="http://www.imgvotpusk.ru/Images/1.gif" width="120px" height="1px" />
105: </td>
106: </tr>
107: </table>
108: </ItemTemplate>
109: <AlternatingItemStyle HorizontalAlign="Center" VerticalAlign="Top" />
110: <ItemStyle HorizontalAlign="Center" VerticalAlign="Top" />
111: <HeaderStyle Font-Bold="True" ForeColor="White" />
112: </asp:DataList>
113: </ContentTemplate>
114: <Triggers>
115: <asp:AsyncPostBackTrigger ControlID="ToLeft" EventName="Click" />
116: <asp:AsyncPostBackTrigger ControlID="ToRight" EventName="Click" />
117: </Triggers>
118: </asp:UpdatePanel>
119: </td>
120: </tr>
121: <tr>
122: <td align="center" valign="middle" height="25px" bgcolor="#E7E3E7">
123: <asp:LinkButton ID="ToLeft" runat="server" Style="display: inline">
124: <asp:Image ID="Image2" runat="server" ImageUrl="Images/st_bot.gif"></asp:Image></asp:LinkButton>
125: </td>
126: </tr>
127: </table>
128: </asp:Panel>
129: </td>
130: </tr>
131: </table>
132: <asp:Label ID="lErr" runat="server" Text="" ForeColor="Red"></asp:Label>
133: <asp:SqlDataSource ID="GetObjectImages" runat="server" ConnectionString="<%$ ConnectionStrings:SQLServer_ConnectionStrings %>"
134: SelectCommand="select ROW_NUMBER() OVER (order by orderby,i) as [ROW_NUMBER], * from [ObjImage] with (nolock) where (ToObject=@id) and (IsModerBan is null) order by orderby">
135: <SelectParameters>
136: <asp:QueryStringParameter Name="id" Type="String" QueryStringField="Object" />
137: </SelectParameters>
138: </asp:SqlDataSource>
139: <asp:SqlDataSource ID="GetObjectImagesAdm" runat="server" ConnectionString="<%$ ConnectionStrings:SQLServer_ConnectionStrings %>"
140: SelectCommand="select ROW_NUMBER() OVER (order by orderby,i) as [ROW_NUMBER], * from [ObjImage] with (nolock) where (ToObject=@id) order by orderby">
141: <SelectParameters>
142: <asp:QueryStringParameter Name="id" Type="String" QueryStringField="Object" />
143: </SelectParameters>
144: </asp:SqlDataSource>
145: <asp:SqlDataSource ID="GetObjectInfo" runat="server" ConnectionString="<%$ ConnectionStrings:SQLServer_ConnectionStrings %>"
146: SelectCommand="select * from [AllObjects] with (nolock) where id=@id">
147: <SelectParameters>
148: <asp:QueryStringParameter Name="id" Type="String" QueryStringField="Object" />
149: </SelectParameters>
150: </asp:SqlDataSource>
151: </div>
152: </form>
Как видите, несмотря на весь рекламный трындеж об облегчении жизни программистам с помощью AJAX - даже разметка этой странички выглядит устрашающе (хотя конечно ручная работа с AJAX в еще более простой форме выглядит еще более устрашающе). А ведь эта страничка не стоит и выеденного яйца! Просто копеечный фотосладер, прокрутчик фоток.
Однако html-разметка - это лишь вершина айсберга. Посмотрите как выглядит код этой странички и поймите, что это в основном вызов сторонних программ, (упакованных в базовой страничке и библиотеке, которые тысячекратно превышают показанный здесь обьем кода и о которых даже вспомнить без содрогания страшно).
1:
2: Partial Class Object_foto
3: Inherits System.Web.UI.Page
4:
5: Public Property ThisTypePageLoad() As TypeOfPageLoad
6: Get
7: Return ViewState("TypePageLoad")
8: End Get
9: Set(ByVal value As TypeOfPageLoad)
10: ViewState("TypePageLoad") = value
11: End Set
12: End Property
13:
14: 'Длина полосы с фото
15: Public ReadOnly Property ShowCount() As Integer
16: Get
17: Return 4
18: End Get
19: End Property
20:
21: Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
22: Dim ObjectChecker As New CheckContext()
23: ObjectChecker.CheckObjectContext(AddressOf ErrorParameters, _
24: AddressOf ObjectNotFound, _
25: AddressOf UserOwnerNotFound, _
26: AddressOf CurrUserNotFound, _
27: AddressOf ProfileNotFound, _
28: AddressOf RightToHimself, _
29: AddressOf LeftToHimself, _
30: AddressOf AdminToHimself, _
31: AddressOf AdminToLeftUser, _
32: AddressOf AdminToRightUser, _
33: AddressOf LeftToLeftUser, _
34: AddressOf RightToRightUser, _
35: AddressOf RightToLeftUser, _
36: AddressOf LeftToRightUser, _
37: AddressOf AnonToLeftUser, _
38: AddressOf AnonToRightUser)
39: If Not IsPostBack Then
40: Dim DV2 As Data.DataView
41: Select Case CType(ViewState("TypePageLoad"), TypeOfPageLoad)
42: Case TypeOfPageLoad.AdminToHimself, TypeOfPageLoad.AdminToLeftUser, TypeOfPageLoad.AdminToRightUser, TypeOfPageLoad.LeftToHimself, TypeOfPageLoad.RightToHimself
43: DV2 = GetObjectImagesAdm.Select(New DataSourceSelectArguments)
44: Case TypeOfPageLoad.AnonToLeftUser, TypeOfPageLoad.AnonToRightUser, TypeOfPageLoad.LeftToRightUser, TypeOfPageLoad.LeftToLeftUser, TypeOfPageLoad.RightToRightUser, TypeOfPageLoad.RightToLeftUser
45: DV2 = GetObjectImages.Select(New DataSourceSelectArguments)
46: Case Else
47: Exit Sub
48: End Select
49: '
50: If DV2 IsNot Nothing Then
51: If DV2.Count > 0 Then
52:
53:
54: If Request.QueryString("i") IsNot Nothing Then
55: If Request.QueryString("i") <> "" Then
56: EnterNum(DV2)
57: Else
58: DefaultNum(DV2)
59: End If
60: Else
61: DefaultNum(DV2)
62: End If
63: '
64: Dim DV3 As Data.DataView
65: Select Case CType(ViewState("TypePageLoad"), TypeOfPageLoad)
66: Case TypeOfPageLoad.AdminToHimself, TypeOfPageLoad.AdminToLeftUser, TypeOfPageLoad.AdminToRightUser, TypeOfPageLoad.LeftToHimself, TypeOfPageLoad.RightToHimself
67: DV3 = GetObjectImagesAdm.Select(New DataSourceSelectArguments)
68: Case Else
69: DV3 = GetObjectImages.Select(New DataSourceSelectArguments)
70: End Select
71: '
72: If DV3.Count <> 0 Then
73: Dim DvShow As New Data.DataTable("Show")
74: DvShow = DV3.ToTable.Clone
75: For I = 0 To Math.Min(ShowCount - 1, DV3.Count - 1)
76: DvShow.ImportRow(DV2.ToTable.Rows(I))
77: Next
78: ViewState("StartIndex") = 0
79: DataList1.DataSource = DvShow
80: DataList1.DataBind()
81: Else
82: panFoto.Enabled = False
83:
84: panFoto.Visible = False
85:
86: End If
87:
88: End If
89: End If
90: '
91: Dim Dv1 As Data.DataView = GetObjectInfo.Select(New DataSourceSelectArguments)
92: If Dv1 IsNot Nothing Then
93: If Dv1.Count > 0 Then
94: lTitle.Text = Dv1(0)("Name") & " (" & Dv1(0)("ObjType_Name") & ")"
95: lKurCn.Text = Dv1(0)("Country_Name") & " | " & Dv1(0)("Kurort_Name")
96: TitleHyperLink.NavigateUrl = "Object_info_all.aspx?Object=" & Request.QueryString("Object")
97: Me.Title = lTitle.Text & " | " & ImageTitle.Text
98: ImageButton1.AlternateText = ImageTitle.Text
99: ImageButton1.ToolTip = ImageTitle.Text
100: End If
101: End If
102:
103:
104: End If
105: End Sub
106:
107: Sub EnterNum(ByVal DV2 As Data.DataView)
108: 'номер рисунка конкретно указан
109: Dim CurrentRecordsetIndex As Integer = CInt(Request.QueryString("i"))
110: ViewState("CurrentRecordsetIndex") = CurrentRecordsetIndex
111: ImageTitle.Text = DV2(CurrentRecordsetIndex)("Name")
112: ImageButton1.ImageUrl = "GetFoto.ashx?id=" & DV2(CurrentRecordsetIndex)("ID").ToString & "&W=500"
113: BigImageButton.NavigateUrl = "GetFoto.ashx?id=" & DV2(CurrentRecordsetIndex)("ID").ToString
114: BigImageButton2.NavigateUrl = BigImageButton.NavigateUrl
115: Me.Title = lTitle.Text & " | " & ImageTitle.Text
116: End Sub
117:
118: Sub DefaultNum(ByVal DV2 As Data.DataView)
119: 'номер рисунка не указан
120: Dim CurrentRecordsetIndex As Integer = 0
121: ViewState("CurrentRecordsetIndex") = 0
122: ImageTitle.Text = DV2(CurrentRecordsetIndex)("Name")
123: ImageButton1.ImageUrl = "GetFoto.ashx?id=" & DV2(CurrentRecordsetIndex)("ID").ToString & "&W=500"
124: BigImageButton.NavigateUrl = "GetFoto.ashx?id=" & DV2(CurrentRecordsetIndex)("ID").ToString
125: BigImageButton2.NavigateUrl = BigImageButton.NavigateUrl
126: Me.Title = lTitle.Text & " | " & ImageTitle.Text
127: End Sub
128:
129: Sub ErrorParameters()
130: lErr.Text = "Ошибка. ErrorParameters"
131: End Sub
132: Sub ObjectNotFound()
133: lErr.Text = "Ошибка. ObjectNotFound"
134: End Sub
135: Sub UserOwnerNotFound()
136: lErr.Text = "Ошибка. UserOwnerNotFound"
137: End Sub
138: Sub CurrUserNotFound()
139: lErr.Text = "Ошибка. CurrUserNotFound"
140: End Sub
141: Sub ProfileNotFound()
142: lErr.Text = "Ошибка. ProfileNotFound"
143: End Sub
144: Sub RightToHimself()
145: ThisTypePageLoad = TypeOfPageLoad.RightToHimself
146: End Sub
147: Sub LeftToHimself()
148: ThisTypePageLoad = TypeOfPageLoad.LeftToHimself
149: End Sub
150: Sub AdminToHimself()
151: ThisTypePageLoad = TypeOfPageLoad.AdminToHimself
152: End Sub
153: Sub AdminToLeftUser()
154: ThisTypePageLoad = TypeOfPageLoad.AdminToLeftUser
155: End Sub
156: Sub AdminToRightUser()
157: ThisTypePageLoad = TypeOfPageLoad.AdminToRightUser
158: End Sub
159: Sub LeftToLeftUser()
160: ThisTypePageLoad = TypeOfPageLoad.LeftToLeftUser
161: End Sub
162: Sub RightToRightUser()
163: ThisTypePageLoad = TypeOfPageLoad.RightToRightUser
164: End Sub
165: Sub RightToLeftUser()
166: ThisTypePageLoad = TypeOfPageLoad.RightToLeftUser
167: End Sub
168: Sub LeftToRightUser()
169: ThisTypePageLoad = TypeOfPageLoad.LeftToRightUser
170: End Sub
171: Sub AnonToLeftUser()
172: ThisTypePageLoad = TypeOfPageLoad.AnonToLeftUser
173: End Sub
174: Sub AnonToRightUser()
175: ThisTypePageLoad = TypeOfPageLoad.AnonToRightUser
176: End Sub
177:
178: ''' <summary>
179: ''' Это работает асинхронно - без полного постбека
180: ''' </summary>
181: Protected Sub ToLeft_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles ToLeft.Click
182: If ViewState("StartIndex") Is Nothing Then
183: Throw New Exception("Ошибка логики AJAX. ToLeft.Click")
184: End If
185: 'запрошена прокрутка назад по рекордсету
186: Dim DvShow As New Data.DataTable("Show")
187: '
188: Dim DV2 As Data.DataView
189: Select Case CType(ViewState("TypePageLoad"), TypeOfPageLoad)
190: Case TypeOfPageLoad.AdminToHimself, TypeOfPageLoad.AdminToLeftUser, TypeOfPageLoad.AdminToRightUser, TypeOfPageLoad.LeftToHimself, TypeOfPageLoad.RightToHimself
191: DV2 = GetObjectImagesAdm.Select(New DataSourceSelectArguments)
192: Case Else
193: DV2 = GetObjectImages.Select(New DataSourceSelectArguments)
194: End Select
195: '
196: DvShow = DV2.ToTable.Clone
197: If DV2 IsNot Nothing Then
198:
199:
200: If DV2.Count > ShowCount Then
201: If ViewState("StartIndex") - 1 < 0 Then
202: ViewState("StartIndex") = DV2.Count - 1
203: Else
204: ViewState("StartIndex") = ViewState("StartIndex") - 1
205: End If
206: Dim I As Integer = CInt(ViewState("StartIndex"))
207: While True
208: DvShow.ImportRow(DV2.ToTable.Rows(I))
209: If DvShow.Rows.Count >= ShowCount Then Exit While
210: If I > DV2.Count - 2 Then Exit While
211: I += 1
212: End While
213: If DvShow.Rows.Count < ShowCount Then
214: For I = 0 To ShowCount - DvShow.Rows.Count - 1
215: DvShow.ImportRow(DV2.ToTable.Rows(I))
216: Next
217: End If
218: Else
219:
220: For I = 0 To DV2.Count - 1
221: DvShow.ImportRow(DV2.ToTable.Rows(I))
222: Next
223: ViewState("StartIndex") = 0
224: End If
225: DataList1.DataSource = DvShow
226: DataList1.DataBind()
227: UpdatePanel1.Update()
228:
229: End If
230: End Sub
231:
232: ''' <summary>
233: ''' Это работает асинхронно - без полного постбека
234: ''' </summary>
235: Protected Sub ToRight_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles ToRight.Click
236: If ViewState("StartIndex") Is Nothing Then
237: Throw New Exception("Ошибка логики AJAX. ToRight.Click")
238: End If
239: 'запрошена прокрутка вперед
240: Dim DvShow As New Data.DataTable("Show")
241: '
242: Dim DV2 As Data.DataView
243: Select Case CType(ViewState("TypePageLoad"), TypeOfPageLoad)
244: Case TypeOfPageLoad.AdminToHimself, TypeOfPageLoad.AdminToLeftUser, TypeOfPageLoad.AdminToRightUser, TypeOfPageLoad.LeftToHimself, TypeOfPageLoad.RightToHimself
245: DV2 = GetObjectImagesAdm.Select(New DataSourceSelectArguments)
246: Case Else
247: DV2 = GetObjectImages.Select(New DataSourceSelectArguments)
248: End Select
249: '
250: DvShow = DV2.ToTable.Clone
251: If DV2 IsNot Nothing Then
252: If DV2.Count > ShowCount Then
253: If ViewState("StartIndex") + 1 < DV2.Count Then
254: ViewState("StartIndex") = ViewState("StartIndex") + 1
255: Else
256: ViewState("StartIndex") = 0
257: End If
258: Dim I As Integer = CInt(ViewState("StartIndex"))
259: While True
260: DvShow.ImportRow(DV2.ToTable.Rows(I))
261: If DvShow.Rows.Count >= ShowCount Then Exit While
262: If I > DV2.Count - 2 Then Exit While
263: I += 1
264: End While
265: If DvShow.Rows.Count < ShowCount Then
266: For I = 0 To ShowCount - DvShow.Rows.Count - 1
267: DvShow.ImportRow(DV2.ToTable.Rows(I))
268: Next
269: End If
270: Else
271: For I = 0 To DV2.Count - 1
272: DvShow.ImportRow(DV2.ToTable.Rows(I))
273: Next
274: ViewState("StartIndex") = 0
275: End If
276: DataList1.DataSource = DvShow
277: DataList1.DataBind()
278: UpdatePanel1.Update()
279:
280: End If
281: End Sub
282:
283: Protected Sub DataList1_ItemDataBound(ByVal sender As Object, ByVal e As System.Web.UI.WebControls.DataListItemEventArgs) Handles DataList1.ItemDataBound
284: If e.Item.DataItem IsNot Nothing Then
285: '
286: Dim CurrentUser As MembershipUser = Membership.GetUser(HttpContext.Current.User.Identity.Name)
287: Dim SlideImageButton As ImageButton = CType(e.Item.FindControl("SlideImageButton"), ImageButton)
288: SlideImageButton.AlternateText = e.Item.DataItem("Name").ToString
289: SlideImageButton.ToolTip = e.Item.DataItem("Name").ToString
290: SlideImageButton.CommandArgument = e.Item.DataItem("i")
291: '
292: Select Case CType(ViewState("TypePageLoad"), TypeOfPageLoad)
293: Case TypeOfPageLoad.AdminToHimself, TypeOfPageLoad.AdminToLeftUser, TypeOfPageLoad.AdminToRightUser, TypeOfPageLoad.AdminToHimself, TypeOfPageLoad.LeftToHimself, TypeOfPageLoad.RightToHimself
294: SlideImageButton.ImageUrl = "GetFoto.ashx?id=" & e.Item.DataItem("ID").ToString & "&W=100" & "&x=1"
295: Case Else
296: SlideImageButton.ImageUrl = "GetFoto.ashx?id=" & e.Item.DataItem("ID").ToString & "&W=100"
297: End Select
298: '
299: End If
300: End Sub
301:
302: ''' <summary>
303: ''' Это работает асинхронно - без полного постбека
304: ''' </summary>
305: Protected Sub Link1Forward1_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles Link1Forward1.Click, ImageButton1.Click
306: If ViewState("CurrentRecordsetIndex") Is Nothing Then
307: Throw New Exception("Ошибка логики AJAX. Link1Forward1_Click")
308: End If
309: Dim CurrentRecordsetIndex As Integer = ViewState("CurrentRecordsetIndex")
310:
311: Dim DV2 As Data.DataView
312: Select Case CType(ViewState("TypePageLoad"), TypeOfPageLoad)
313: Case TypeOfPageLoad.AdminToHimself, TypeOfPageLoad.AdminToLeftUser, TypeOfPageLoad.AdminToRightUser, TypeOfPageLoad.LeftToHimself, TypeOfPageLoad.RightToHimself
314: DV2 = GetObjectImagesAdm.Select(New DataSourceSelectArguments)
315: Case TypeOfPageLoad.AnonToLeftUser, TypeOfPageLoad.AnonToRightUser, TypeOfPageLoad.LeftToRightUser, TypeOfPageLoad.LeftToLeftUser, TypeOfPageLoad.RightToRightUser, TypeOfPageLoad.RightToLeftUser
316: DV2 = GetObjectImages.Select(New DataSourceSelectArguments)
317: Case Else
318: Exit Sub
319: End Select
320:
321: If DV2 IsNot Nothing Then
322: If DV2.Count > 0 Then
323: '
324: If CurrentRecordsetIndex >= DV2.Count - 1 Then
325: 'впереди по рекордсету рисунков уже нет
326: ViewState("CurrentRecordsetIndex") = 0
327: Else
328: ViewState("CurrentRecordsetIndex") = CurrentRecordsetIndex + 1
329: End If
330: '
331: ImageTitle.Text = DV2(ViewState("CurrentRecordsetIndex"))("Name")
332: ImageButton1.ImageUrl = "GetFoto.ashx?id=" & DV2(ViewState("CurrentRecordsetIndex"))("ID").ToString & "&W=500"
333: BigImageButton.NavigateUrl = "GetFoto.ashx?id=" & DV2(ViewState("CurrentRecordsetIndex"))("ID").ToString
334: BigImageButton2.NavigateUrl = BigImageButton.NavigateUrl
335: Me.Title = lTitle.Text & " | " & ImageTitle.Text
336: '
337: End If
338: End If
339: UpdatePanel2.Update()
340: End Sub
341:
342: ''' <summary>
343: ''' Это работает асинхронно - без полного постбека
344: ''' </summary>
345: Protected Sub LinkBack1_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles LinkBack1.Click
346: If ViewState("CurrentRecordsetIndex") Is Nothing Then
347: Throw New Exception("Ошибка логики AJAX. LinkBack1_Click")
348: End If
349: Dim CurrentRecordsetIndex As Integer = ViewState("CurrentRecordsetIndex")
350:
351: Dim DV2 As Data.DataView
352: Select Case CType(ViewState("TypePageLoad"), TypeOfPageLoad)
353: Case TypeOfPageLoad.AdminToHimself, TypeOfPageLoad.AdminToLeftUser, TypeOfPageLoad.AdminToRightUser, TypeOfPageLoad.LeftToHimself, TypeOfPageLoad.RightToHimself
354: DV2 = GetObjectImagesAdm.Select(New DataSourceSelectArguments)
355: Case TypeOfPageLoad.AnonToLeftUser, TypeOfPageLoad.AnonToRightUser, TypeOfPageLoad.LeftToRightUser, TypeOfPageLoad.LeftToLeftUser, TypeOfPageLoad.RightToRightUser, TypeOfPageLoad.RightToLeftUser
356: DV2 = GetObjectImages.Select(New DataSourceSelectArguments)
357: Case Else
358: Exit Sub
359: End Select
360:
361: If DV2 IsNot Nothing Then
362: If DV2.Count > 0 Then
363: If CurrentRecordsetIndex = 0 Then
364: 'сзади по рекордсету рисунков уже нет
365: ViewState("CurrentRecordsetIndex") = DV2.Count - 1
366: Else
367: ViewState("CurrentRecordsetIndex") = CurrentRecordsetIndex - 1
368: End If
369: '
370: ImageTitle.Text = DV2(ViewState("CurrentRecordsetIndex"))("Name")
371: ImageButton1.ImageUrl = "GetFoto.ashx?id=" & DV2(ViewState("CurrentRecordsetIndex"))("ID").ToString & "&W=500"
372: BigImageButton.NavigateUrl = "GetFoto.ashx?id=" & DV2(ViewState("CurrentRecordsetIndex"))("ID").ToString
373: BigImageButton2.NavigateUrl = BigImageButton.NavigateUrl
374: Me.Title = lTitle.Text & " | " & ImageTitle.Text
375: End If
376: End If
377: UpdatePanel2.Update()
378: End Sub
379:
380: ''' <summary>
381: ''' Это работает асинхронно - без полного постбека
382: ''' </summary>
383: Protected Sub SlideImageButton_Click(ByVal sender As Object, ByVal e As System.Web.UI.ImageClickEventArgs)
384: Dim SlideImageButton As ImageButton = CType(sender, ImageButton)
385: Dim DV2 As Data.DataView
386: Select Case CType(ViewState("TypePageLoad"), TypeOfPageLoad)
387: Case TypeOfPageLoad.AdminToHimself, TypeOfPageLoad.AdminToLeftUser, TypeOfPageLoad.AdminToRightUser, TypeOfPageLoad.LeftToHimself, TypeOfPageLoad.RightToHimself
388: DV2 = GetObjectImagesAdm.Select(New DataSourceSelectArguments)
389: Case TypeOfPageLoad.AnonToLeftUser, TypeOfPageLoad.AnonToRightUser, TypeOfPageLoad.LeftToRightUser, TypeOfPageLoad.LeftToLeftUser, TypeOfPageLoad.RightToRightUser, TypeOfPageLoad.RightToLeftUser
390: DV2 = GetObjectImages.Select(New DataSourceSelectArguments)
391: Case Else
392: Exit Sub
393: End Select
394:
395: If DV2 IsNot Nothing Then
396: If DV2.Count > 0 Then
397: For i = 0 To DV2.Count - 1
398: If DV2(i)("i") = SlideImageButton.CommandArgument Then
399: ViewState("CurrentRecordsetIndex") = i
400: ImageTitle.Text = DV2(i)("Name")
401: Me.Title = lTitle.Text & " | " & ImageTitle.Text
402: ImageButton1.ImageUrl = "GetFoto.ashx?id=" & DV2(i)("ID").ToString & "&W=500"
403: BigImageButton.NavigateUrl = "GetFoto.ashx?id=" & DV2(i)("ID").ToString
404: BigImageButton2.NavigateUrl = BigImageButton.NavigateUrl
405: UpdatePanel2.Update()
406: End If
407: Next
408:
409: End If
410: End If
411:
412: End Sub
413:
414: End Class
Вот так облегчение придумали нам c ASP.NET AJAX! Причем я написал эту страничку столь компактно, насколько это было возможно! Извращался как мог для упрощения - калбеками например (строки 129-188 - в них производится модификация операций, выполняемой базовой страничкой. CallBack - это ассемблерная терминология, в терминологии бейсика - это делегаты). Все же основные операции странички вынесены в базовую страничку - этими кальеками лишь иногда слегка модифицируюся операции базовой странички. Но на код УЖЕ страшно смотреть. А страничка, страничка-то ведь какая копеечная!
Есть некий альтернативный подход - с JQUERY. Его вы тоже можете увидеть на титуле Вотпуска. Программирование в JQUERY выглядит на порядок проще (если не считать необходимость глубокого понимания JQUERY). Даже на уровне разметки все выглядит гораздо приличнее.
1: <table cellspacing="0" cellpadding="0" border="0" width="100%" class="listing_media">
2: <tbody>
3: <tr>
4: <td nowrap="" align="left" width="16px" style="padding-top: 4px;">
5: <a target="_new" href="#" style="" id="aleft_foto">
6: <img height="120px" border="0" width="16px" alt="" src="http://www.votpusk.ru/images/s_left.gif">
7: </a>
8: </td>
9: <td class="list">
10: <div class="oh">
11: <div class="oh2">
12: <table cellspacing="0" cellpadding="0" align="center" width="100%" id="acont_foto"
13: style="margin-left: 0pt;">
14: <tbody>
15: <tr>
16: <td>
17: <div align="center" class="item" style="width: 122.2px;">
18: <div class="item2">
19: <p style="padding: 0pt; margin: 0pt;">
20: <a target="_new" href="http://foto.votpusk.ru/foto_slide.aspx?i=B0358B2C-FB56-45C9-A889-E0C7614F8F2E&j=741A036B9764B3B448D08E528441ACA5F76C">
21: <img style="border: 1px outset rgb(0, 0, 0);" alt="" src="http://foto.votpusk.ru/GetImage.ashx?j=741A036B9764B3B448D08E528441ACA5F76C&w=100"></a></p>
22: </div>
23: </div>
24: </td>
25: <td>
26: <div align="center" class="item" style="width: 122.2px;">
27: <div class="item2">
28: <p style="padding: 0pt; margin: 0pt;">
29: <a target="_new" href="http://foto.votpusk.ru/foto_slide.aspx?i=B0358B2C-FB56-45C9-A889-E0C7614F8F2E&j=548643D4BB6A50FEEA19F58AD8D93BABF76C">
30: <img style="border: 1px outset rgb(0, 0, 0);" alt="" src="http://foto.votpusk.ru/GetImage.ashx?j=548643D4BB6A50FEEA19F58AD8D93BABF76C&w=100"></a></p>
31: </div>
32: </div>
33: </td>
34: <td>
35: <div align="center" class="item" style="width: 122.2px;">
36: <div class="item2">
37: <p style="padding: 0pt; margin: 0pt;">
38: <a target="_new" href="http://foto.votpusk.ru/foto_slide.aspx?i=B0358B2C-FB56-45C9-A889-E0C7614F8F2E&j=91CB41DC4F8962BE04DECA005191382FF76C">
39: <img style="border: 1px outset rgb(0, 0, 0);" alt="" src="http://foto.votpusk.ru/GetImage.ashx?j=91CB41DC4F8962BE04DECA005191382FF76C&w=100"></a></p>
40: </div>
41: </div>
42: </td>
43: <td>
44: <div align="center" class="item" style="width: 122.2px;">
45: <div class="item2">
46: <p style="padding: 0pt; margin: 0pt;">
47: <a target="_new" href="http://foto.votpusk.ru/foto_slide.aspx?i=B0358B2C-FB56-45C9-A889-E0C7614F8F2E&j=CF89E4E80527BC3EACFBDBB87DD76314F76C">
48: <img style="border: 1px outset rgb(0, 0, 0);" alt="" src="http://foto.votpusk.ru/GetImage.ashx?j=CF89E4E80527BC3EACFBDBB87DD76314F76C&w=100"></a></p>
49: </div>
50: </div>
51: </td>
52: <td>
53: <div align="center" class="item" style="width: 122.2px;">
54: <div class="item2">
55: <p style="padding: 0pt; margin: 0pt;">
56: <a target="_new" href="http://foto.votpusk.ru/foto_slide.aspx?i=B0358B2C-FB56-45C9-A889-E0C7614F8F2E&j=76941513E4451DA248EF22046B9383CDF76C">
57: <img style="border: 1px outset rgb(0, 0, 0);" alt="" src="http://foto.votpusk.ru/GetImage.ashx?j=76941513E4451DA248EF22046B9383CDF76C&w=100"></a></p>
58: </div>
59: </div>
60: </td>
61: </tr>
62: </tbody>
63: </table>
64: </div>
65: </div>
66: </td>
67: <td nowrap="" align="right" width="22px" style="padding-top: 4px;">
68: <a target="_new" href="#" style="" id="aright_foto">
69: <img height="120px" border="0" width="16px" alt="" src="http://www.votpusk.ru/images/s_right.gif">
70: </a>
71: </td>
72: </tr>
73: </tbody>
74: </table>
75:
76: <script type="text/javascript">
77: //<![CDATA[
78: aslider.get('foto').items = 5;
79: aslider.get('foto').itemWidth = 116;
80: aslider.get('foto').mixData = true;
81: aslider.get('foto').data = [
82: ['http://foto.votpusk.ru/foto_slide.aspx?i=B0358B2C-FB56-45C9-A889-E0C7614F8F2E&j=07659DC409352E8EF66FC5B85591575DF76C', 'http://foto.votpusk.ru/GetImage.ashx?j=07659DC409352E8EF66FC5B85591575DF76C&w=100'],
83: ['http://foto.votpusk.ru/foto_slide.aspx?i=15BDE1C6-F956-4646-AC45-C6DE0D788D30&j=1D59EAC4182232CBAE02F20768D3C055F76C', 'http://foto.votpusk.ru/GetImage.ashx?j=1D59EAC4182232CBAE02F20768D3C055F76C&w=100'],
84: ['http://foto.votpusk.ru/foto_slide.aspx?i=B0358B2C-FB56-45C9-A889-E0C7614F8F2E&j=F0A606B6B46BD48B3B45AEE3D54F4E22F76C', 'http://foto.votpusk.ru/GetImage.ashx?j=F0A606B6B46BD48B3B45AEE3D54F4E22F76C&w=100'],
85: ['http://foto.votpusk.ru/foto_slide.aspx?i=B0358B2C-FB56-45C9-A889-E0C7614F8F2E&j=0350B95F150992F01D1CFBD7E005E83DF76C', 'http://foto.votpusk.ru/GetImage.ashx?j=0350B95F150992F01D1CFBD7E005E83DF76C&w=100'],
86: ['http://foto.votpusk.ru/foto_slide.aspx?i=B0358B2C-FB56-45C9-A889-E0C7614F8F2E&j=A2AC3B2EC98609143025DB6E20D5D945F76C', 'http://foto.votpusk.ru/GetImage.ashx?j=A2AC3B2EC98609143025DB6E20D5D945F76C&w=100'],
87: ['http://foto.votpusk.ru/foto_slide.aspx?i=B0358B2C-FB56-45C9-A889-E0C7614F8F2E&j=F105E7904B7862AF0F4EB67D5DBFBAAEF76C', 'http://foto.votpusk.ru/GetImage.ashx?j=F105E7904B7862AF0F4EB67D5DBFBAAEF76C&w=100'],
88: ['http://foto.votpusk.ru/foto_slide.aspx?i=B0358B2C-FB56-45C9-A889-E0C7614F8F2E&j=52D5C12F43096609C3B7934B9AFB2907F76C', 'http://foto.votpusk.ru/GetImage.ashx?j=52D5C12F43096609C3B7934B9AFB2907F76C&w=100'],
89: ['http://foto.votpusk.ru/foto_slide.aspx?i=B0358B2C-FB56-45C9-A889-E0C7614F8F2E&j=741A036B9764B3B448D08E528441ACA5F76C', 'http://foto.votpusk.ru/GetImage.ashx?j=741A036B9764B3B448D08E528441ACA5F76C&w=100'],
90: ['http://foto.votpusk.ru/foto_slide.aspx?i=B0358B2C-FB56-45C9-A889-E0C7614F8F2E&j=76941513E4451DA248EF22046B9383CDF76C', 'http://foto.votpusk.ru/GetImage.ashx?j=76941513E4451DA248EF22046B9383CDF76C&w=100'],
91: ['http://foto.votpusk.ru/foto_slide.aspx?i=B0358B2C-FB56-45C9-A889-E0C7614F8F2E&j=548643D4BB6A50FEEA19F58AD8D93BABF76C', 'http://foto.votpusk.ru/GetImage.ashx?j=548643D4BB6A50FEEA19F58AD8D93BABF76C&w=100'],
92: ['http://foto.votpusk.ru/foto_slide.aspx?i=B0358B2C-FB56-45C9-A889-E0C7614F8F2E&j=602F60803EC8FBF23E6CC740B184F9E3F76C', 'http://foto.votpusk.ru/GetImage.ashx?j=602F60803EC8FBF23E6CC740B184F9E3F76C&w=100'],
93: ['http://foto.votpusk.ru/foto_slide.aspx?i=B0358B2C-FB56-45C9-A889-E0C7614F8F2E&j=3E7143DD0025C28AEA488F7581A751EBF76C', 'http://foto.votpusk.ru/GetImage.ashx?j=3E7143DD0025C28AEA488F7581A751EBF76C&w=100'],
94: ['http://foto.votpusk.ru/foto_slide.aspx?i=B0358B2C-FB56-45C9-A889-E0C7614F8F2E&j=CF89E4E80527BC3EACFBDBB87DD76314F76C', 'http://foto.votpusk.ru/GetImage.ashx?j=CF89E4E80527BC3EACFBDBB87DD76314F76C&w=100'],
95: ['http://foto.votpusk.ru/foto_slide.aspx?i=B0358B2C-FB56-45C9-A889-E0C7614F8F2E&j=91CB41DC4F8962BE04DECA005191382FF76C', 'http://foto.votpusk.ru/GetImage.ashx?j=91CB41DC4F8962BE04DECA005191382FF76C&w=100'],
96: ['http://foto.votpusk.ru/foto_slide.aspx?i=B0358B2C-FB56-45C9-A889-E0C7614F8F2E&j=301CE3B19EB22CC934968CFC6FF3E6A2F76C', 'http://foto.votpusk.ru/GetImage.ashx?j=301CE3B19EB22CC934968CFC6FF3E6A2F76C&w=100'],
97: ];
98: aslider.get('foto').init();
99: //]]>
100: </script>
Вроде при этом подходе есть небольшое облегчение программирования. Как видите - тут просто вызов проги http://www.votpusk.ru/java_script/slider.js. Но на эту прогу даже смотреть страшно - это миллионы строк кода. И что мы наблюдаем в итоге? 211 реквестов к серверу и страничку весом более мегабайта (из которых 157 килобайт (15%) занимает только загрузка этой адской проги slider.js на миллион строк кода:
При этом данная прога на JQUERY даже не работает в Web-сервисом и не читает реально самые топовые фотки из базы - как в моем первом фрагменте на MS AJAX. Чтобы прикрутить сюда считывание реально лучшего контента сайта - придется немного повозиться. Для начала надо сделать Web-сервис, который будет выдавать результат в формате JSON (в ASP.NET 2 предустановленного шаблона для такого сервиса нет, только в ASP.NET 4), потом на страничке Язваскриптом принять и распарсить JSON функций $.getJSON. И лишь потом сформировать вызовы aslider.
Остановимся и призадумаемся. У нас есть миллион строк исходного текста на JAVASCRIPT, который надо медленно-медленно в один поток интерпритировать браузером. Что для этого нужно? Адская мощность процессора и разрешение на интерпритацию.
На моем мощном девелоперском кампе при зажатии кнопки прокрутки фото-слайдера на JQUERY процессор сразу улетает в 100%. Жаль у меня нет более дохлого кампутера - выполнится ли эта прокрутка вообще?
И второй еще более важный момент - работа JQUERY (как и любого JAVASRIPT) должна быть специально разрешена. Причем даже для трастовой зоны!
Вот так технология, едрить в коромысло! Или самому сломать себе мозг на MS AJAX, или вызвать миллион строк чужого кода, прочитать 157 килобайт каждый раз и при КАЖДОМ рефреше проинтерпритировать весь этот миллион строк кода (начиная с прохода лексического анализатора). Да еще и разрешение должно быть специальное выставлено в браузере чтобы это все работало!
Дело еще и в том, что разработка ASP.NET, увы, так и не была доведена до конца и однажды сделанный визуальный контрол нельзя вырезать на страничке нажать кнопочку Save as dll и навсегда для себя сохранить это решение (разметку и алгоритмы под этой разметкой). То есть прогу на ASP.NET надо КАЖДЫЙ раз писать заново. Есть некие возможности по распространения DLL с сайтами на ASP.NET (вот например моя публично распространяемая DLL с целым ASP.NET-сайтом), но этот способ не касается отдельных контролов, которые все равно надо готовить вручную (вообще без дизафнера). И при всяком применении контрола все равно увязывать структуру постбеков контрола со структурой постбеков самой странички. А этого-то как раз так не хочется делать!
Все ожидали, что разработка ASP.NET будет доведена хотя бы до состояния шестого бейсика (1996-1998 годов). В Бейсике мы могли выделять любой фрагмент любой формы, нажал одну кнопку и вуаля - этот фрагмент оказывался сохраненным в файле OCX. И в дальнейшем в любом проекте можно было просто drag-and-drop'ом перетащить этот OCX на любую форму и этот OCX работал без малейших доработок и воспоминаний что в нем было внутри. Любой следующий проект можно было строить из своих предыдущих наработок. Когда в 2002-м году появилась ASP.NET 1.1 она была настолько ублюдочной, что падала раз по двадцать на дню. Всем стало понятно, что это только некий промежуточный вариант. Все стали ждать NET 2.0, надеясь что в нем доведут ASP.NET до нужной кондиции. Однако в NET 2.0 только выбрали баги, добавили Generic-коллекции и наконец-то доделали визуальный редактор DataSet (с ADO.NET наконец-то стало удобно работать). Все стали ждать выхода следующей версии - но там оказалась полнейшая и никому не нужная ерунда - WCF, перенесли библиотеку AJAX из дополнительных компонентов на панель инструментов, малоценный LINQ и прочая чушь. Половина ASP.NET программистов, которая все это осознала - сразу ушла на Яву. Я тоже как только увидел NET 3.5 - тоже поставил себе NetBeans и решил задачку на Яве (надо собраться с силами и описать ее когда-нибудь). А потом MS вообще аббалдела - наняла бригаду индусов, которые вообще никогда не слышали об ASP.NET и заказала им делать ASP.NET MVC и распространять со своего сайта PHP. ASP.NET программисты стали разбегаться кто-куда и рынок, занимаемый ASP.NET сократился до 0,4% от общего рынка web-приложений. После того, как я увидел, что именно сделали в NET 4.0 - я ужаснулся. Там оказался какой-то безумный Entity Framework (хотя для работы с PosrgreSQL нужна лишь крошечная обертка на 20 строчек сохраняющая Singlton-коннект, а для работы с MySQL не нужно вообще ничего, кроме провайдра/драйвера СУБД) и ни к селу ни к городу прикрученный к ядру ASP.NET UrlRewriting (который и так в любой момент бепрепятственно можно было добавить к любому ASP.NET приложению в виде OpenSource-библиотеки). Тут по-моему не выдержали нервы даже у самых стойких асперов. Когда я 2005-м году переходил с первого на второй фреймворк ASP.NET, я сидел на форуме SQL.Ru и задавал там вопросы по второму фреймворку (ну и отвечал иногда тоже). В день в разделе ASP.NET крутились сотни, иногда даже тысячи обсуждаемых вопросов. И в разделе форума про ASP.NET крутились десятки тысяч проггеров. Когда я в 2010-м году что-то спросил на том же форуме про какую-то очередную ублюдочную индустскую придумку Castle из MVC - мой вопрос так и оставался единственным в течении недели. Иначе говоря, людей в разделе ASP.NET на всех форумах просто не стало. И это несмотря на адскую пропадаганду микрософта и даже несмотря на то, что - как я писал об этом не раз - именно микрософтовкие проги изучаются в школе начиная с третьего класса (и без знания их даже не выдается аттестат зрелости).
Что же делать? Работать-то как-то надо. И так хочется иметь для сайта откомпилированный компонент, который бы удовлетворял самым элементарным требованиям:
- Работал и изготавливался бы отдельно и независимо от странички, не был бы связан со структурой ее постбеков странички (никак не влиял на нее и сам не зависел от нее)
- Был бы откомпилированным байт-кодом и не требовал бы интрепритации браузером исходника начиная с лексичексого разбора
- Чтобы откомпилированный байт-код был существенно компактнее исходного текста
- Чтобы программить в среде этой виртуальной машины можно было без выворачивания своего мозга наизнанку
- Чтобы компонент работал независимо от серверной технологии и независимо от серверной операционной системы
- Чтобы виртуальная машина интрепритации этого байт-кода была установлена на всех (или почти всех) кампутерах (независимо от браузера и операционной системы пользователя сайта)
С некоторой натяжкой можно считать что и JAVA-аплеты являются удовлетворяющей этим элементарным требованиям технологией. Однако небольшие размышления о возможностях графических и анимационных приводят к пониманию преимуществ Flex-платформы. Тем более Флеш-плеер установлен у 99,1% пользователей интернета, что существенно превышает процент пользователей у которых установлена и нормально работает JRE-машина.
Вот так, собственно я пришел к полезности Flex-программирования для Web-платформы - Знакомство с Adobe Flex 4. Смешно, но получается, что Adobe реализовала многие ожидания программистов, которые ждали-ждали реализации вышеперечисленных элементарных возможностей от билогейтсовской империи, да так и не дождались.
Ниже описанный автономный компонет, как вы понимаете, может быть размещен на страничке, сделанной на любой серверной технологии, от PHP и JAVA до ASP.NET и ASP.NET MVC.
Итак, перейдем наконец к рассмотрению моего кода на Flex. Это код того же самого фото-слайдера, который перед этим был уже дважды описан выше на технологиях JQUERY и MS AJAX. И вот как выглядит этот же фотослайдер на Flex (прошу строго не судить - это моя первая проба пера на Flex):
1: <?xml version="1.0" encoding="utf-8"?>
2: <s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
3: xmlns:s="library://ns.adobe.com/flex/spark"
4: xmlns:mx="library://ns.adobe.com/flex/mx"
5: applicationComplete="application1_applicationCompleteHandler(event)"
6: minWidth.Image_Load="740" minHeight.Image_Load="125"
7: width.Image_Load="740" height.Image_Load="125"
8: minWidth.Debug="500" minHeight.Debug="500" >
9:
10: <fx:Script>
11:
12: <![CDATA[
13: import flash.net.navigateToURL;
14: import mx.collections.ArrayCollection;
15: import mx.controls.Image;
16: import mx.core.UIComponent;
17: import mx.events.FlexEvent;
18: import mx.rpc.events.ResultEvent;
19: import spark.components.BorderContainer;
20: import spark.core.NavigationUnit;
21:
22:
23: [Bindable]
24: public var BestFoto:mx.collections.ArrayCollection ;
25: public var params:Object;
26:
27: protected function application1_applicationCompleteHandler(event:FlexEvent):void
28: {
29: BestFoto= new mx.collections.ArrayCollection();
30: params = mx.core.FlexGlobals.topLevelApplication.parameters;
31: BestFotoNumber.url=params.BestFotoNumber
32: currentState="Image_Load";
33: try
34: {
35: BestFotoNumber.send();
36: }
37: catch(error:SecurityError)
38: {
39: currentState="Debug";
40: return;
41: }
42: if (params.DebugService=="1"){
43: currentState="Debug";
44: }
45: }
46:
47: private function BestFotoNumberReadEnd(event:mx.rpc.events.ResultEvent):void
48: {
49: var i:int;
50: BestFoto = event.result.BestFotoNumbers.BestFoto;
51: for (i=0;i<BestFoto.list.length;i++) {
52: //trace ("BestFoto " + i + " is " + BestFoto[i].j);
53: BestFoto[i].id=params.ClickBaseUrl.toString()+"?i="+BestFoto[i].id+"&j="+BestFoto[i].j;
54: BestFoto[i].j=params.BaseUrl.toString()+BestFoto[i].j;
55: }
56: if (params.DebugService=="1"){
57: DataGrid1.dataProvider = BestFoto;
58: }
59: else{
60: currentState="Image_Load";
61: HGroup1.visible=false;
62:
63: if (BestFoto.list.length>7){
64: //первые пять видимых добавляем сразу чтобы они были видны, остальные потом
65: AddMyElement(0,5);
66: var BorderContainerTmp:BorderContainer = HGroup1.getElementAt(4) as BorderContainer;
67: var ImageTmp:Image= BorderContainerTmp.getElementAt(0) as Image;
68: //предполагается что последний выданный реквест все-таки завершится последним
69: ImageTmp.addEventListener("complete",Image5_completeHandler);
70: }
71: else {
72: AddMyElement(0,BestFoto.list.length);
73: HGroup1.visible=true;
74: }
75: }
76: }
77:
78: protected function Image5_completeHandler(event:Event):void
79: {
80: AddMyElement(6,BestFoto.list.length);
81: HGroup1.visible=true;
82: }
83:
84: protected function AddMyElement (From:int, To:int):void {
85: var j:int;
86: for (j=From;j<To;j++) {
87:
88: var NewBorderContainer:BorderContainer = new BorderContainer;
89: NewBorderContainer.width = 115;
90: NewBorderContainer.height = 115;
91: NewBorderContainer.setStyle("backgroundColor", 0xF3EEF4);
92: NewBorderContainer.addEventListener(MouseEvent.ROLL_OVER,NewBorderContainer_MouseOverHandler);
93: NewBorderContainer.addEventListener(MouseEvent.MOUSE_OUT,NewBorderContainer_MouseOutHandler);
94:
95: var NewImage:Image = new Image;
96: NewImage.verticalCenter=5;
97: NewImage.horizontalCenter=0;
98: NewImage.width=100;
99: NewImage.height=100;
100: NewImage.addEventListener(MouseEvent.CLICK,NewImage_clickHandler);
101: NewImage.addEventListener(MouseEvent.ROLL_OVER,NewImage_MouseOverHandler);
102: NewImage.addEventListener(MouseEvent.MOUSE_OUT,NewImage_MouseOutHandler);
103: NewImage.source=BestFoto[j].j;
104: NewImage.data=BestFoto[j].id;
105: NewBorderContainer.addElement(NewImage);
106:
107: HGroup1.addElement(NewBorderContainer);
108: }
109: }
110:
111: protected function scrollToThere(dir:uint):void {
112: var value:Number = HGroup1.getHorizontalScrollPositionDelta(dir);
113: if (value != 0) {
114: pth.valueBy = value;
115: anim.play();
116: }
117: else
118: {
119: if(dir == NavigationUnit.PAGE_RIGHT)
120: {
121: value = HGroup1.getHorizontalScrollPositionDelta(NavigationUnit.HOME);
122: pth.valueBy = value;
123: anim.play();
124: }
125: if(dir == NavigationUnit.PAGE_LEFT)
126: {
127: value = HGroup1.getHorizontalScrollPositionDelta(NavigationUnit.END);
128: pth.valueBy = value;
129: anim.play();
130: }
131: }
132: }
133:
134: protected function NewImage_clickHandler(event:MouseEvent):void
135: {
136: var Image1:Image=event.currentTarget as Image;
137: var url:String = Image1.data.toString();
138: var request:flash.net.URLRequest = new URLRequest(url);
139: try {
140: navigateToURL(request, "_blank");
141: }
142: catch (e:Error) {
143: return;
144: }
145: }
146:
147: protected function NewBorderContainer_MouseOverHandler (e:MouseEvent):void {
148: var Image1:Image=e.currentTarget.getElementAt(0)
149: Image1.buttonMode = true;
150: Image1.useHandCursor = true;
151: Image1.width=110;
152: Image1.height=110;
153: Image1.verticalCenter=5;
154: Image1.horizontalCenter=0;
155: }
156:
157: protected function NewBorderContainer_MouseOutHandler(event:MouseEvent):void
158: {
159: var Image1:Image=event.currentTarget.getElementAt(0);
160: Image1.width=100;
161: Image1.height=100;
162: Image1.verticalCenter=5;
163: Image1.horizontalCenter=0;
164: }
165:
166:
167: protected function NewImage_MouseOverHandler(event:MouseEvent):void
168: {
169: var Image1:Image=event.currentTarget as Image
170: Image1.width=110;
171: Image1.height=110;
172: Image1.verticalCenter=5;
173: Image1.horizontalCenter=0;
174: }
175:
176: protected function NewImage_MouseOutHandler(event:MouseEvent):void
177: {
178: var Image1:Image=event.currentTarget as Image
179: Image1.width=100;
180: Image1.height=100;
181: Image1.verticalCenter=5;
182: Image1.horizontalCenter=0;
183: }
184:
185: ]]>
186: </fx:Script>
187:
188: <s:states>
189: <s:State name="Debug"/>
190: <s:State name="Image_Load"/>
191: </s:states>
192:
193: <fx:Declarations>
194: <!-- Place non-visual elements (e.g., services, value objects) here -->
195: <s:HTTPService id="BestFotoNumber" result="BestFotoNumberReadEnd(event)" />
196:
197: <s:Animate id="anim" target="{HGroup1}" duration="500">
198: <s:motionPaths>
199: <s:SimpleMotionPath id="pth" property="horizontalScrollPosition" />
200: </s:motionPaths>
201: </s:Animate>
202: </fx:Declarations>
203:
204: <s:layout.Image_Load>
205: <s:HorizontalLayout gap="10" verticalAlign="middle"/>
206: </s:layout.Image_Load>
207:
208: <mx:DataGrid id="DataGrid1" width="100%" height="100%" includeIn="Debug" editable="false" x="0" y="0">
209: <mx:columns>
210: <mx:DataGridColumn dataField="j" headerText="SmallImageURL"/>
211: <mx:DataGridColumn dataField="id" headerText="ClickURL"/>
212: </mx:columns>
213: </mx:DataGrid>
214:
215: <s:Button label="<"
216: click="scrollToThere(NavigationUnit.PAGE_LEFT);"
217: width="30" includeIn="Image_Load"/>
218:
219: <s:HGroup id="HGroup1" includeIn="Image_Load" x="52" y="101" width="650" height="125" gap="10" verticalAlign="middle" variableColumnWidth="false"
220: clipAndEnableScrolling="true" columnWidth="120" >
221: </s:HGroup>
222:
223: <s:Button label=">"
224: click="scrollToThere(NavigationUnit.PAGE_RIGHT);"
225: width="30" includeIn="Image_Load"/>
226:
227: </s:Application>
Как видите, код плеера настолько простой, что его даже неловко комментировать. Его даже сравнивать невозможно с крученым кодом ASP.NET. Когда плеер загружается, проверяется режим работы. Если он не debug, то плеер переходит рабочее состояние и дергает webservice (строки 27-45). Когда загрузка первых пяти видимых на фасаде рисунков завершена (строки 47-76), начинается загрузка тех рисунков, которые сразу не видны на фасаде. Так достигается временной лаг для реквестов на сервер, чтобы канал пропустил все, страничка все успела отрисовать, чтобы сервер успел отработать.
В откомпилированном виде весь этот код занимает 50 Килобайт, то есть втрое меньше, чем javascript-библиотека фотосладера. Не говоря уже о том, что это исполняемый байт-код, а не букавки Яваскрипта, которые браузер должен анализировать и превращать в исполняемые команды.
Этот код был бы еще вдвое наверное проще если бы я не вложил в этот свой компонент две фишки - задержку считывания рисунков (которые не видны сразу при загрузке контрола) и режим отладки. Код читает лучшие рисунки сайта, поэтому у него есть режим показа - что именно сайт ему отдает для показа в качестве лучших рисунков сайта - DebugService="1":
Этот параметр DebugService указывается в запускающем скрипте слайдера - в максимальном виде, предлагаемым компанией Adobe (позволяющей догрузить Флеш-плеер на те кампутеры, где нет флеш-плеера) этот враппер выглядит вот так:
1: <!-- Enable Browser History by replacing useBrowserHistory tokens with two hyphens -->
2: <!-- BEGIN Browser History required section -->
3: <link rel="stylesheet" type="text/css" href="history/history.css" />
4: <script type="text/javascript" src="history/history.js"></script>
5: <!-- END Browser History required section -->
6:
7: <script type="text/javascript" src="swfobject.js"></script>
8: <script type="text/javascript">
9: <!-- For version detection, set to min. required Flash Player version, or 0 (or 0.0.0), for no version detection. -->
10: var swfVersionStr = "10.0.0";
11: <!-- To use express install, set to playerProductInstall.swf, otherwise the empty string. -->
12: var xiSwfUrlStr = "playerProductInstall.swf";
13: var flashvars = {};
14: flashvars.BestFotoNumber="http://foto.votpusk.ru/BestFotoNumber.ashx";
15: flashvars.DebugService="0";
16: flashvars.BaseUrl="http://foto.votpusk.ru/GetImage.ashx?w=100%26j=";
17: flashvars.ClickBaseUrl="http://foto.votpusk.ru/foto_slide.aspx";
18: var params = {};
19: params.quality = "high";
20: params.bgcolor = "#ffffff";
21: params.allowscriptaccess = "sameDomain";
22: params.allowfullscreen = "true";
23: var attributes = {};
24: attributes.id = "Test1";
25: attributes.name = "Test1";
26: attributes.align = "middle";
27: swfobject.embedSWF(
28: "Test1.swf", "flashContent",
29: "100%", "100%",
30: swfVersionStr, xiSwfUrlStr,
31: flashvars, params, attributes);
32: <!-- JavaScript enabled so display the flashContent div in case it is not replaced with a swf object. -->
33: swfobject.createCSS("#flashContent", "display:block;text-align:left;");
34: </script>
35:
36:
37: <!-- SWFObject's dynamic embed method replaces this alternative HTML content with Flash content when enough
38: JavaScript and Flash plug-in support is available. The div is initially hidden so that it doesn't show
39: when JavaScript is disabled.
40: -->
41: <div id="flashContent">
42: <p>
43: To view this page ensure that Adobe Flash Player version
44: 10.0.0 or greater is installed.
45: </p>
46: <script type="text/javascript">
47: var pageHost = ((document.location.protocol == "https:") ? "https://" : "http://");
48: document.write("<a href='http://www.adobe.com/go/getflashplayer'><img src='"
49: + pageHost + "www.adobe.com/images/shared/download_buttons/get_flash_player.gif' alt='Get Adobe Flash player' /></a>" );
50: </script>
51: </div>
52:
53: <noscript>
54: <object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" width="100%" height="100%" id="Test1">
55: <param name="movie" value="Test1.swf" />
56: <param name="FlashVars" value="BestFotoNumber='http://foto.votpusk.ru/BestFotoNumber.ashx'&DebugService='0'&BaseUrl='http://foto.votpusk.ru/GetImage.ashx?w=100%26j='&ClickBaseUrl='http://foto.votpusk.ru/foto_slide.aspx'" />
57: <param name="quality" value="high" />
58: <param name="bgcolor" value="#ffffff" />
59: <param name="allowScriptAccess" value="sameDomain" />
60: <param name="allowFullScreen" value="true" />
61: <!--[if !IE]>-->
62: <object type="application/x-shockwave-flash" data="Test1.swf" width="100%" height="100%">
63: <param name="quality" value="high" />
64: <param name="bgcolor" value="#ffffff" />
65: <param name="allowScriptAccess" value="sameDomain" />
66: <param name="allowFullScreen" value="true" />
67: <!--<![endif]-->
68: <!--[if gte IE 6]>-->
69: <p>
70: Either scripts and active content are not permitted to run or Adobe Flash Player version
71: 10.0.0 or greater is not installed.
72: </p>
73: <!--<![endif]-->
74: <a href="http://www.adobe.com/go/getflashplayer">
75: <img src="http://www.adobe.com/images/shared/download_buttons/get_flash_player.gif" alt="Get Adobe Flash Player" />
76: </a>
77: <!--[if !IE]>-->
78: </object>
79: <!--<![endif]-->
80: </object>
81: </noscript>
Как видите, это скрипт поддерживает браузеры, которые уже ушли в историю и стали экспонатами музея (скрипты из history). Кроме того он грузит скрипт swfobject, который можно разметить вначале странички и который более ли менее гарантирует что в любом современном браузере в теге <div id="flashContent"> окажется вызов флеш-плеера, подобный вызову плеера в строках 54-80 (который написан для описанного выше случая, когда в браузере пользователя не установлена галка разрешения работы Javascript (или просто сайт не находится в доверенной зоне безопасности этого пользователя интернета). Обратите внимание на такую мелочь - параметры во flashvars передаются в urlencode-виде, то есть амперсант передан как %26.
Как вы видите, здесь в параметрах у меня также указано имя сервиса (фактически это просто XML-файл, формируемый сервером), который отдает фото-слайдеру перечень лучших рисунков сайта для показа слайдером.
Как же создать такой XML-файл наиболее простым путем? Его можно было сделать прямо в SQL - как я описал это на страничке Этюды на ASP2. Делаем RSS-канал на одной SQL-процедуре, а можно обойтись и без процедуры и создать XML прямо кодом ASP.NET:
1: <%@ WebHandler Language="VB" Class="BestFotoNumber" %>
2:
3: Imports System
4: Imports System.Web
5:
6: Public Class BestFotoNumber : Implements IHttpHandler
7:
8: Public Sub ProcessRequest(ByVal context As HttpContext) Implements IHttpHandler.ProcessRequest
9: Try
10: Dim CN1 As New Data.SqlClient.SqlConnection(System.Configuration.ConfigurationManager.ConnectionStrings("LocalSqlServer").ConnectionString)
11: CN1.Open()
12: Dim CMD1 As Data.SqlClient.SqlCommand
13: CMD1 = New Data.SqlClient.SqlCommand("SELECT TOP 100 dbo.PP8(UserData_i),UserData_ToUser FROM [vOtpusk_].[dbo].[Best_Foto]", CN1)
14: context.Response.ContentType = "text/xml"
15: context.Response.Write("<?xml version='1.0' encoding='UTF-8'?>")
16: context.Response.Write("<BestFotoNumbers>" & vbCrLf)
17: Dim DR1 As Data.SqlClient.SqlDataReader = CMD1.ExecuteReader
18: While DR1.Read
19: context.Response.Write("<BestFoto>")
20: context.Response.Write("<j>" & DR1(0) & "</j>" & vbCrLf)
21: context.Response.Write("<id>" & DR1(1).ToString & "</id>" & vbCrLf)
22: context.Response.Write("</BestFoto>" & vbCrLf)
23: End While
24: context.Response.Write("</BestFotoNumbers>" & vbCrLf)
25: Catch ex As Exception
26: context.Response.ContentType = "text/plain"
27: context.Response.Write(ex.Message)
28: End Try
29:
30: End Sub
31:
32: Public ReadOnly Property IsReusable() As Boolean Implements IHttpHandler.IsReusable
33: Get
34: Return False
35: End Get
36: End Property
37:
38: End Class
Этот хандлер просто сериализует в XML вот такой рекордсет:
Обратите внимание, что система безопасности флеш-плеера такова, что в корень этого поддомена вотпуска необходимо положить файлик с разрешением - с какого домена флеш-плеерам можно дергать этот XML-файлик http://foto.votpusk.ru/BestFotoNumber.ashx:
1: <?xml version="1.0"?>
2: <!DOCTYPE cross-domain-policy SYSTEM "http://www.macromedia.com/xml/dtds/cross-domain-policy.dtd">
3: <cross-domain-policy>
4: <allow-access-from domain="*" />
5: </cross-domain-policy>
При желании мой код вы можете гнуть в любую сторону. Например в сторону небольшого усложнения - поставить флажки, что реквест всех видимых рисунков на фасаде закончился и только тогда начинать дергать рисунки невидимые. Я предположил, что завершение чтения всех рисунков ожидать не обязательно - достаточно дождаться последнего реквеста на видимый рисунок. Это конечно упрощенный взгляд на вещи. Вот это подпрыгивание тоже можно заменить анимацией, можно фон добавить, ресайз всего компонента, скины какие-нибудь вместо фона. Можно названия рисунков выдернуть с сервера и тоже показать их на слайдере. Можно сделать чтобы рисунки прокручивались сами по себе, по таймеру, а не по кликам. В общем - простора для расширения этого моего OpenSource Flex компонента хоть отбавляй.
В дальнейшем этот проект превратился в серию проектов - своим путем стал развиваться фотослайдер для компании votpusk.ru, появился и стал развиваться своим путем отдельный проект OpenSource фотослайдера.
Надеюсь, чтение этой странички доставило удовольствие заинтересованным лицам из числа web-программистов. Тем, для кого тут оказалось слишком много буков - просьба не беспокоится.
Еще я хотел добавить, что не всегда jQuery выглядит так криво и убого - как в описанном на этой страничке случае. Иногда реальной альтернативы ей просто нет, например такой случай я описал здесь - AJAX подсказка/автозаполнение на jQuery.
Об инструментах для работы c Flex вы можете почитать подробнее на этой страничке - Знакомство с Adobe Flex 4, а на следующей страничке вы можете почитать Как сделать SOAP/WSDL-вебсервис на ASP.NET/MONO для вызова его из FLEX.
|