Выражения привязки контролов к данным в ASP2
Выражения привязки... Литература, увы, отвечает на этот вопрос очень скромно. Обычно ограничиваются рассказом о том, что привязать можно одиночное значение или множественное значение к контролам, которые отображают повторяющиеся значения.
После прочтения буквариков вот эта картинка вводит в полный ступор начинающих (не исключая и меня в момент ознакомления с ASP2):
Что же надо ввести сюда? Для этого придется осознать несколько концепций ASP2...
Когда привязка выполняется путем указания свойства DataSource и свойства DataMember, то это происходит за кулисами дизайнера и нужная строка появляется в тексте ASPX сама, однако, в некоторых случах (как на рисунке надо вводить эти выражения самостоятельно). Кроме того, привязка может быть выполнена и к бизнес-обьектам, а для этого тоже надо самостоятельно реализовывать дополнительные методы для привязки (помимо метода ICollection, который и так сам по себе имеется у всех коллекций, словарей, ДатаРидера, DataView (ДатаТабле)) .
Кроме, того сами по себе выражения могут быть достаточно сложными и могут быть не только в виде чисто выражения привязки:
<tagprefix:tagname property="<%# data-binding expression %>" runat="server" />
- or -
literal text <%# data-binding expression %>
но и в виде просто выражения, напиминающее макроопределение ,который напрямую встраивается в текст страницы в виде текста.
<asp:SqlDataSource ID="SqlDataSource1" runat="server" ConnectionString="<%$ ConnectionStrings:SH_Forum %>" SelectCommand="GetGroupsWithTopic" SelectCommandType="StoredProcedure"></asp:SqlDataSource>
Как видите, в этом формате доступ к данным производится через синтаксис "обьект:свойство". Почитать больше про этот последний метод можно в разделе System.Web.Compilation.ExpressionBuilder. А мы вернемся к собственно к составлению выражений привязки.
Для начала два слова о механизме расчета выражения привязки. На этапе компиляции - <%# data-binding expression %> заменяется на строчный элемент - System.Web.UI.DataBoundLiteralControl, а на этапе исполнения - страница вызывает метод DataBind. Он рекурсивно вызывается для каждого контрола. Конкретный контрол перебирает все элементы коллекции ICollections контрола, который указан у него в DataSource или DataSourceID - указанное выражение вычисляется, из этого обьекта выбирается столбец с именем второго параметра, и далее результат сериализизуется в строку (как указано в третьем параметре) и шаблон контрола вставляется на страничку с рассчитанным выражением (уже преобразованным в строку).
Как пишут в MSDN - "Because this method performs late-bound evaluation, using reflection at run time, it can cause performance to noticeably slow compared to standard ASP.NET data-binding syntax." - привязка через эти выражения - метод достаточно медленный и работает на ходу через Reflaction.
Для описания свособа преобразования в строку у EVAL есть третий аргумент - который может быть использован достаточно хитро:
<a href='<%# DataBinder.Eval(Container.DataItem, "title_id", "Page1.aspx?id={0}") %>' >
Здесь, опять же, выражение {0} не так просто, как может показаться на первый взгяд. Это стандартнай метод форматирования строк в ASP2:
C | Displays numeric values in currency format. |
D | Displays numeric values in decimal format. |
E | Displays numeric values in scientific (exponential) format. |
F | Displays numeric values in fixed format. |
G | Displays numeric values in general format. |
N | Displays numeric values in number format. |
X | Displays numeric values in hexadecimal format. |
В самих выражениях привязки может быть использовано много чего, вплоть до индексов, но первая сложность при составлении таких выражений - почти всегда используется странный литерал - "Container.DataItem". Причем в MSDN сказано - "If you are binding against the page, the container parameter value should be "Page"". Это конечно, не точно. Слишком абстрактно и непонятно для правильного составления этих выражений и для их отладки.
У элементов с множественной привязкой есть такое замечательное событие - ItemDataBound. Именно к нему я подключаюсь когда хочу проверить - правильно ли я задумал выражение привязки:
Как видите, "Container.DataItem" - это не более, чем e.item.Dataitem, иначе говоря один элемент данных, на который привязан DataList1. Кроме собственно метода привязки DataBinder.eval (или в NET2 сокращенно просто EVAL) есть еще метода привязки - Bind() и Xpath(). Они могут быть использованы на страничке только внутри контекста элемента-источника данных.
Теперь небольшой реалистичный пример с привязкой, который меня самого поражает тем, что практически не содержит ни единой строчки кода. Практически весь он основан исключительно на описанных выше выражениях привязки к данным.
Речь пойдет о форуме с вот такой элементарной структурой данных. Как видите все просто - есть топики, которые ссылаются на подргуппы (подразделы), которые в свою очередь ссылаются на группы (разделы), ну а сами сообщения, естественно, ссылаются на топики.
Для формирования правого нижнего сектора странички с последними темами форума будут использованы две процедуры - GetGroupsWithTopic, которая выдает группы в которых есть активные топики и GetLastTopicForGroup, которая выдает все последние активные топики в группе. Некоторое усложнение этих процедур и наличие коррелированных запросов вместо простых GROUP GY в отборах обьясняется тем, что порядок отображения групп и подгрупп в этом форуме динамически меняются (параметр J в группах и подгруппах) и зависит от количества покупок в том или ином товарном разделе/подразделе. Но это сейчас не столь важно. Важно понять что есть всего две процедуры, которые дают указанные отборы (разделов и топиков в разделах).
Что любопытно, что практически весь этот форум сделан без программирования. Только щелчками в Дизайн-Тайме. Вход/выход (левый верхний угол) - стандартные контролы авторизации, в программировании не нуждающиеся. Меню слева - это тоже контрол, не содержащий какого-либо кода программирования и состоящий из одного единственного элемента Menu, заполненного привязкой. Процедура, формирующая данные для привязки - выглядит так.
С точки зрения дизайна - страничка форума представляет собой четыре контрола - ForumLogin1 - вход/выход, ForumNavigate1 - меню слева, ForumControl1 - поиск по форуму и ForumLast1 - последние активные топики. Этот последний контрол будет рассмотрен подробнее.
Как уже упоминалось, рассматриваемый контрол ForumLast (справа внизу) не содержит кода программирования, а содержит лишь вот такой шаблон - в котором есть текстовая метка и вложенный контрол ForumLastItem. К сожалению, если щелкнуть на EditDatabound - нас ждет полное разочарование:
Только после того как мы введем в это поле ту самую строку привязки, о которой говорилось выше:
мы получим требуемый текст в ASPX-файле:
Что любопытно, что такой картинки как для метки (пониже) мы не получим для собственного контрола (повыше), несмотря даже на то, что во вложенном контроле ForumLastItem определено свойство, которое бы хотелось хотя бы увидеть в дизайнере привязки:
Как видите, номер группы, где есть топики - укладывается во вложенном контроле ForumLastItem в метку, которая специально сделана выше на картинке с форумом видимой. Число в этой невидимой метке служит параметром для процедуры GetLastTopicForGroup, выдающей текущие активные топики в разделе:
Как вы понимаете, сюда тоже придется ввести вручную два выражения привязки, после чего их можно увидеть в ASPX-файле:
Теперь пару слов о страничке, отображающей один-единственный топик. Это конечно, тоже контрол, и конечно, тоже выполненный без программирования, чисто на привязке. Только тут выражение привязки существенно хитрее.
Для того, чтобы попасть в эту страничку, на главной страничке форума я разместил MultiView, который показывает по умолчанию страничку с контролом, ForumLast. Когда же юзер производит переход на конкретный топик, то вот эта развязка на главной странице переключает нас на контрол ForumTopic:
Этот контрол ForumTopic состоит из DataList, который привязан на SqlDataSource ровно таким же образом, как было рассмотрено выше:
и на этом я останавливатся не буду и нескольких меток (заголовок топика и разделы), которые будут привязаны существенно более хитрым образом.
Действительно, додуматься до такого выражения
Text='<%# DataBinder.Eval (ctype(name.Select(New System.Web.UI.DataSourceSelectArguments),System.Data.DataView)(0), "Topic_Name") %>'
нелегко, особенно если вообще не понимаешь, о чем идет речь. Кроме того, студия не дает ровно никакой подсказки для ввода этого выражения в режиме конструктора.
Как видите, для DataList хотя бы открылся текстбокс, куда можно ввести выражение привязки. Для Label и других элементов с одиночной привязкой данных дело обстоит хуже - даже после ввода выражения ровно ничего не поменялось в конструкторе странички, за исключением крошечной желтенькой бочечки рядом со свойством текст, которое и обозначает, что вышеуказанное выражение привязки было введено для свойства текст.
Теперь попробую обьяснить, как именно я построил это выражение. Name - это имя я дал SqlDataSource. В отличие от второго SqlDataSource, который содержит все сообщения топика, в этом SqlDataSource лежит только имя и разделы топика:
поэтому вкладывать это в мощный контрол, поддерживающий привязку множественных значений смысла никакого не было. Это можно было бы выбросить на странички просто в Label. Тут еще один момент, который надо понимать. Сам по себе SqlDataSource работает либо в DataSet-моде и хранит множество данных сразу (примерно как в массиве) - тогда это можно даже привязывать с обновлением. Либо работает в режиме DataReader и хранит ровно одно значение, что экономит память и ускоряет работу. Как вы понимаете - для этой строки нам нужен именно режим DataReader.
Сам по себе этот SqlDataSource я сконфигурил прямо в дизайн-тайме:
А дальше - метод Select контрола SqlDataSource Возвращает обьект DataView и требует New System.Web.UI.DataSourceSelectArguments. Это не параметр для Select'а- это важно, не путайте - а именно некий набор свойств, указывающий, сколько строк вернуть, грубо говоря. Сам параметр для Select'а задается при конфирурировании SqlDataSource:
Ноль здесь означает - первую (и единственную) строку выходного рекордсета (DataView), а Topic_Name, как вы понимаете, означает столбец в этой строке. Поскольку у нас в этом случае нет ДатаСета и контрола, принимающего его к себе и перебирающего записи в нем по ItemDataBound, то это выражение можно не вводить самому, а составить с IntelliSense прямо в Form_Load, а потом скопировать в HTML-разметку.
Text='<%# DataBinder.Eval (ctype(name.Select(New System.Web.UI.DataSourceSelectArguments),System.Data.DataView)(0), "Topic_Name") %>'
Получается вроде немного гиморойно - нам пришлось несколько раз ввести вручную выражение привязки, зато - задумайтесь - какой нехилый форум получился - причем практически без кода, только щелчками мышкой в дизайн-тайме... А сколько бы пришлось наковырять кода с формированием HTML-тегов для такой функциональности в PHP?
Правда, в реальности все несколько сложнее. И программировать все-таки, увы, приходится...
Существует много косвенных примений привязки. Выше упоминалось, что когда выражения привязки становятся непомерно сложными, декларативно можно записать только вызов функции, в которой вернуть прямой HTML-код. Реализовав саму функцию в коде.
Но такой слишком простой вариант рассматривать не интересно, зато я расскажу тут о технике применения функций привязки, которая позволяет КОСВЕННО что-то изменить на страничке, например, активировать и отпозиционировать те или иные контролы.
В этой форме мне требовалось в зависимости от выбора в верхнем комбешнике показать совершенно разные контролы в нижнем, например если в верхнем мы выбираем тип поля - дата, то умолчание в нем устанавливается календариком, а если тип поля String - то умолчание для поля надо устанавливать в текстбоксе. Есть и специальные пользовательские типы полей, умолчания в которых надо устанавливать отдельным комбешником. В сущности речь идет просто о динамическом добавлении контролов, но только здесь я это решил с помощью выражения привязки.
На этой форме, предназначенной для редактирования/добавления карточек(записей в базу), я разместил следующий FormView:
00001: <asp:FormView ID="FormView1" runat="server" DefaultMode="Edit" DataKeyNames="CIAttr_ID" DataMember="DefaultView" DataSourceID="CIAttr"> 00002: <EditItemTemplate> 00003: <table width="100%"> 00004: <tr> 00005: <td> 00006: <table align=center width=600 cellpadding=0 cellspacing=0 border=0> 00007: <tr> 00008: <td width=100>Attr Name</td> 00009: <td><asp:TextBox ID="EAttrName" runat="server" Width="100%" Enabled=False Text='<%# Bind("CIAttr_AttrName", "{0}") %>'></asp:TextBox></td> 00010: </tr> 00011: <tr> 00012: <td>Attr Type</td> 00013: <td><asp:TextBox ID="EAttrType" runat="server" Width="100%" Enabled=False Text='<%# Bind("CIAttr_AttrTypeName1", "{0}") %>' ></asp:TextBox> 00014: <asp:HiddenField ID="EAttrTypeID" runat="server" Value='<%# Bind("CIAttrType_ID", "{0}") %>' /></td> 00015: </tr> 00016: <tr> 00017: <td>DB Field Name</td> 00018: <td><asp:TextBox ID="EFieldName" runat="server" Width="100%" Enabled=false Text='<%# Bind("CIAttr_FieldName", "{0}") %>' > </asp:TextBox></td> 00019: </tr> 00020: <tr> 00021: <td>Description</td> 00022: <td><asp:TextBox ID="EDescription" runat="server" Width="100%" TextMode=MultiLine Height=100 Text='<%# Bind("CIAttr_Description", "{0}") %>' ></asp:TextBox></td> 00023: </tr> 00024: <tr> 00025: <td>NotForHead</td> 00026: <td><asp:TextBox ID="ENotForHead" runat="server" Width="100%" Text='<%# Bind("CIAttr_NotForHead", "{0}") %>' ></asp:TextBox></td> 00027: </tr> 00028: <tr> 00029: <td>PreSelect</td> 00030: <td><asp:TextBox ID="EPreSelect" runat="server" Width="100%" TextMode=MultiLine Height=50 Text='<%# Bind("CIAttr_PreSelect", "{0}") %>' ></asp:TextBox></td> 00031: </tr> 00032: <tr> 00033: <td>Default</td> 00034: <td> 00035: <asp:Literal ID="EDefault" runat="server" Text='<%# EDefaultText( EVAL("CIAttr_Default","{0}") , EVAL("CIAttrType_SQLTypeName","{0}") ) %>'></asp:Literal> 00036: <asp:DropDownList ID="EDropDownList" runat="server" Width="100%" AutoPostBack="True"> 00037: </asp:DropDownList> 00038: <asp:Calendar ID="ECalendar" runat="server"></asp:Calendar> 00039: <asp:TextBox ID="ETextBox" runat="server" Width="100%"></asp:TextBox> 00040: <asp:TextBox ID="ETextArea" runat="server" Height="50px" TextMode="MultiLine" Width="100%"></asp:TextBox></td> 00041: </tr> 00042: <tr> 00043: <td></td> 00044: <td> 00045: <div style="text-align:left; float:left"> 00046: <asp:Label ID="EErr" runat="server" ForeColor="Red"></asp:Label> 00047: </div> 00048: <div style="text-align:right; display:inline; float:right"> 00049: <asp:Button ID="EOk" runat="server" Text="Save" OnClick="EOk_Click" /> 00050: </div> 00051: </td> 00052: </tr> 00053: </table> 00054: </td></tr></table> 00055: 00056: </EditItemTemplate> 00057: <InsertItemTemplate> 00058: <table align=center width=600 cellpadding=0 cellspacing=0 border=0> 00059: <tr> 00060: <td width=100>Attr Name<asp:RequiredFieldValidator ID="RequiredFieldValidator1" runat="server" 00061: ControlToValidate="IAttrName" ErrorMessage="*"></asp:RequiredFieldValidator></td> 00062: <td><asp:TextBox ID="IAttrName" runat="server" Width="100%"></asp:TextBox></td> 00063: </tr> 00064: <tr> 00065: <td>Attr Type</td> 00066: <td><asp:DropDownList ID="IAttrType" runat="server" Width="100%" AutoPostBack="True" OnSelectedIndexChanged="IAttrType_SelectedIndexChanged" DataSourceID="CIAttrType" DataTextField="AttrTypeName1" DataValueField="ID" > 00067: </asp:DropDownList> 00068: <asp:SqlDataSource ID="CIAttrType" runat="server" ConnectionString="<%$ ConnectionStrings:ConnStr %>" SelectCommand="select * from dbo.GetAttrType(@CustomerID)"> 00069: <SelectParameters> 00070: <asp:SessionParameter Name="CustomerID" SessionField="CustomerID" /> 00071: </SelectParameters> 00072: </asp:SqlDataSource> 00073: <asp:HiddenField ID="CIAttrType_Selected_SQLTypeName" runat="server" /> 00074: </td> 00075: </tr> 00076: <tr> 00077: <td>DB Field Name<asp:RequiredFieldValidator ID="RequiredFieldValidator2" runat="server" 00078: ControlToValidate="IFieldName" ErrorMessage="*"></asp:RequiredFieldValidator></td> 00079: <td><asp:TextBox ID="IFieldName" runat="server" Width="100%" ></asp:TextBox></td> 00080: </tr> 00081: <tr> 00082: <td>Description</td> 00083: <td><asp:TextBox ID="IDescription" runat="server" Width="100%" TextMode=MultiLine Height=100></asp:TextBox></td> 00084: </tr> 00085: <tr> 00086: <td>NotForHead<asp:RequiredFieldValidator ID="RequiredFieldValidator3" runat="server" 00087: ControlToValidate="INotForHead" ErrorMessage="*"></asp:RequiredFieldValidator></td> 00088: <td><asp:TextBox ID="INotForHead" runat="server" Width="100%" >0</asp:TextBox></td> 00089: </tr> 00090: <tr> 00091: <td>PreSelect</td> 00092: <td><asp:TextBox ID="IPreSelect" runat="server" Width="100%" TextMode=MultiLine Height=50 ></asp:TextBox></td> 00093: </tr> 00094: <tr> 00095: <td>Default</td> 00096: <td> 00097: <asp:Literal ID="IDefault" Text='<%# InsertInit1() %>' runat="server"></asp:Literal> 00098: <asp:DropDownList ID="IDropDownList" runat="server" Width="100%" AutoPostBack="True"> 00099: </asp:DropDownList> 00100: <asp:Calendar ID="ICalendar" runat="server"></asp:Calendar> 00101: <asp:TextBox ID="ITextBox" runat="server" Width="100%"></asp:TextBox> 00102: <asp:TextBox ID="ITextArea" runat="server" Height="50px" TextMode="MultiLine" Width="100%"></asp:TextBox></td> 00103: </tr> 00104: <tr> 00105: <td></td> 00106: <td> 00107: <div style="text-align:left; float:left"> 00108: <asp:Label ID="IErr" runat="server" ForeColor="Red"></asp:Label> 00109: </div> 00110: <div style="text-align:right; display:inline; float:right"> 00111: <asp:Button ID="IOk" runat="server" Text="Save" OnClick="IOk_Click" /> 00112: </div> 00113: </td> 00114: </tr> 00115: </table> 00116: </InsertItemTemplate> 00117: </asp:FormView>
Как видите, в строке 35 формы определена функция привязки EDefaultText, тело которой находится в коде в строках 29-61. В чем же тут фишка применения функции привязки. А в том, что эта функция не возвращает ничего, что мог бы отобразить литерал EDefault. Внутри этой функции происходит просто перекоючение видимости контролов. Поскольку среди контролов находится календарь, возвращать тут простой HTML никак не получится.
Теперь посмотрим на второе применение функции привязки, которая вызвана в строке 97 и реализована в строках 64-67. В данном случае функция привязки использована для инициализации видимости контролов. А в чем хитрость в этом случае? А в том, что привязки обрабатываются ПО ПОРЯДКУ, и в момент вызова InsertInit1 в строке 91, комбешник, задекларированный в строке 66 формы уже привязан и имеет значение SelectedValue, необходимое для правильного формирования видимости поля Default функцией IAttrType_SelectedIndexChanged.
00001: Partial Class CIAttrCard 00002: Inherits System.Web.UI.Page 00003: Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load ...... 00018: If CIAttrID.Value = 0 Then 00019: 'вставка нового атрибута 00020: FormView1.DefaultMode = FormViewMode.Insert 00021: Else 00022: 'редактирование атрибута 00023: FormView1.DefaultMode = FormViewMode.Edit 00024: If Not IsPostBack Then FormView1.DataBind() 00025: End If 00026: End If 00027: End Sub 00028: 00029: Public Function EDefaultText(ByVal Value As String, ByVal SQLTypeName As String) As String 00030: Dim Cal As System.Web.UI.WebControls.Calendar = CType(FormView1.FindControl("ECalendar"), System.Web.UI.WebControls.Calendar) 00031: Dim Txt As System.Web.UI.WebControls.TextBox = CType(FormView1.FindControl("ETextBox"), System.Web.UI.WebControls.TextBox) 00032: Dim Area As System.Web.UI.WebControls.TextBox = CType(FormView1.FindControl("ETextArea"), System.Web.UI.WebControls.TextBox) 00033: Dim Combo As System.Web.UI.WebControls.DropDownList = CType(FormView1.FindControl("EDropDownList"), System.Web.UI.WebControls.DropDownList) 00034: Select Case SQLTypeName 00035: Case "datetime" 00036: Cal.Visible = True 00037: Txt.Visible = False 00038: Area.Visible = False 00039: Combo.Visible = False 00040: Cal.SelectedDate = IIf(Value = "", Now, Value) 00041: Case "ntext" 00042: Cal.Visible = False 00043: Txt.Visible = False 00044: Area.Visible = True 00045: Combo.Visible = False 00046: Area.Text = Value 00047: Case "integer" 00048: Cal.Visible = False 00049: Txt.Visible = False 00050: Area.Visible = False 00051: Combo.Visible = True 00052: BindRefCombo("EDropDownList", CITypeID.Value) 00053: If Combo.Items.FindByValue(Value) IsNot Nothing Then Combo.Items.FindByValue(Value).Selected = True 00054: Case Else 'decimal(9,2),int,nvarchar(100) 00055: Cal.Visible = False 00056: Txt.Visible = True 00057: Area.Visible = False 00058: Combo.Visible = False 00059: Txt.Text = Value 00060: End Select 00061: End Function 00062: 00063: 'это единственный способ проинициализировать поле IDefault тут - FormView1_DataBinding выполняется в самом начале биндинга и значение в комбешнике DropDownList еще не готово 00064: Public Function InsertInit1() As String 00065: IAttrType_SelectedIndexChanged(Nothing, Nothing) 00066: Return "" 00067: End Function 00068: 00069: Protected Sub IAttrType_SelectedIndexChanged(ByVal sender As Object, ByVal e As System.EventArgs) 00070: Dim Cal As System.Web.UI.WebControls.Calendar = CType(FormView1.FindControl("ICalendar"), System.Web.UI.WebControls.Calendar) 00071: Dim Txt As System.Web.UI.WebControls.TextBox = CType(FormView1.FindControl("ITextBox"), System.Web.UI.WebControls.TextBox) 00072: Dim Area As System.Web.UI.WebControls.TextBox = CType(FormView1.FindControl("ITextArea"), System.Web.UI.WebControls.TextBox) 00073: Dim Combo As System.Web.UI.WebControls.DropDownList = CType(FormView1.FindControl("IDropDownList"), System.Web.UI.WebControls.DropDownList) 00074: Dim CIAttrType = CType(FormView1.FindControl("IAttrType"), System.Web.UI.WebControls.DropDownList) 00075: Select Case CIAttrType.SelectedValue 00076: Case "10003" 'datetime 00077: Cal.Visible = True 00078: Txt.Visible = False 00079: Area.Visible = False 00080: Combo.Visible = False 00081: Case "10001" 'ntext 00082: Cal.Visible = False 00083: Txt.Visible = False 00084: Area.Visible = True 00085: Combo.Visible = False 00086: Case "10004", "10002", "10000" 'decimal(9,2),int,nvarchar(100) 00087: Cal.Visible = False 00088: Txt.Visible = True 00089: Area.Visible = False 00090: Combo.Visible = False 00091: Case Else 00092: Cal.Visible = False 00093: Txt.Visible = False 00094: Area.Visible = False 00095: Combo.Visible = True 00096: If CIAttrType.SelectedValue <> "" Then BindRefCombo("IDropDownList", CIAttrType.SelectedValue) 00097: End Select 00098: 00099: End Sub 00100: 00101: Private Sub BindRefCombo(ByVal DefComboName As String, ByVal CITypeID As Integer) ...... 00112: End Sub 00113: 00114: Protected Sub EOk_Click(ByVal sender As Object, ByVal e As System.EventArgs) 00115: CIAttr.UpdateParameters("Description").DefaultValue = CType(FormView1.FindControl("EDescription"), System.Web.UI.WebControls.TextBox).Text 00116: CIAttr.UpdateParameters("NotForHead").DefaultValue = CType(FormView1.FindControl("ENotForHead"), System.Web.UI.WebControls.TextBox).Text 00117: CIAttr.UpdateParameters("PreSelect").DefaultValue = CType(FormView1.FindControl("EPreSelect"), System.Web.UI.WebControls.TextBox).Text 00118: CIAttr.UpdateParameters("ID").DefaultValue = CIAttrID.Value 00119: Dim SQLTypeName As String 00120: Try 00121: Select Case CType(FormView1.FindControl("EAttrTypeID"), System.Web.UI.WebControls.HiddenField).Value 00122: Case "10000" 'nvarchar(100) 00123: SQLTypeName = "nvarchar(100)" 00124: CIAttr.UpdateParameters("Default").Type = TypeCode.String 00125: CIAttr.UpdateParameters("Default").DefaultValue = Left(CType(FormView1.FindControl("ETextBox"),System.Web.UI.WebControls.TextBox).Text, 100) 00126: Case "10001" 'ntext 00127: SQLTypeName = "ntext" 00128: CIAttr.UpdateParameters("Default").Type = TypeCode.String 00129: CIAttr.UpdateParameters("Default").DefaultValue = CType(FormView1.FindControl("ETextArea"), System.Web.UI.WebControls.TextBox).Text 00130: Case "10002" 'int 00131: SQLTypeName = "int" 00132: CIAttr.UpdateParameters("Default").Type = TypeCode.Int32 00133: CIAttr.UpdateParameters("Default").DefaultValue = CInt(CType(FormView1.FindControl("ETextBox"), System.Web.UI.WebControls.TextBox).Text) 00134: Case "10003" 'datetime 00135: SQLTypeName = "datetime" 00136: CIAttr.UpdateParameters("Default").Type = TypeCode.DateTime 00137: CIAttr.UpdateParameters("Default").DefaultValue = CDate(CType(FormView1.FindControl("ECalendar"), System.Web.UI.WebControls.Calendar).SelectedDate) 00138: Case "10004" 'decimal(9,2) 00139: SQLTypeName = "decimal(9,2)" 00140: CIAttr.UpdateParameters("Default").Type = TypeCode.Decimal 00141: CIAttr.UpdateParameters("Default").DefaultValue = CDec(CType(FormView1.FindControl("ETextBox"), System.Web.UI.WebControls.TextBox).Text) 00142: Case Else 00143: SQLTypeName = "int" 00144: CIAttr.UpdateParameters("Default").Type = TypeCode.Int32 00145: CIAttr.UpdateParameters("Default").DefaultValue = CInt(CType(FormView1.FindControl("EDropDownList"), System.Web.UI.WebControls.DropDownList).SelectedValue) 00146: End Select 00147: Catch ex As Exception 00148: CType(FormView1.FindControl("IErr"), System.Web.UI.WebControls.Label).Text = "Ошибка конвертирования в заданный тип." 00149: Exit Sub 00150: End Try 00151: FormView1.UpdateItem(False) 00152: Response.Redirect("CITypeCard.aspx?id=" & Request.QueryString("typeid")) 00153: End Sub 00154: 00155: Protected Sub IOk_Click(ByVal sender As Object, ByVal e As System.EventArgs) 00156: CIAttr.InsertParameters("AttrTypeID").DefaultValue = CType(FormView1.FindControl("IAttrType"), System.Web.UI.WebControls.DropDownList).SelectedValue 00157: CIAttr.InsertParameters("AttrName").DefaultValue = CType(FormView1.FindControl("IAttrName"), System.Web.UI.WebControls.TextBox).Text 00158: CIAttr.InsertParameters("FieldName").DefaultValue = CType(FormView1.FindControl("IFieldName"), System.Web.UI.WebControls.TextBox).Text 00159: CIAttr.InsertParameters("Created").DefaultValue = Now 00160: CIAttr.InsertParameters("Description").DefaultValue = CType(FormView1.FindControl("IDescription"), System.Web.UI.WebControls.TextBox).Text 00161: CIAttr.InsertParameters("NotForHead").DefaultValue = CType(FormView1.FindControl("INotForHead"), System.Web.UI.WebControls.TextBox).Text 00162: CIAttr.InsertParameters("PreSelect").DefaultValue = CType(FormView1.FindControl("IPreSelect"), System.Web.UI.WebControls.TextBox).Text 00163: Dim SQLTypeName As String 00164: Try 00165: Select Case CType(FormView1.FindControl("IAttrType"), System.Web.UI.WebControls.DropDownList).SelectedValue 00166: Case "10000" 'nvarchar(100) 00167: SQLTypeName = "nvarchar(100)" 00168: CIAttr.InsertParameters("Default").Type = TypeCode.String 00169: CIAttr.InsertParameters("Default").DefaultValue = Left(CType(FormView1.FindControl("ITextBox"), System.Web.UI.WebControls.TextBox).Text, 100) 00170: Case "10001" 'ntext 00171: SQLTypeName = "ntext" 00172: CIAttr.InsertParameters("Default").Type = TypeCode.String 00173: CIAttr.InsertParameters("Default").DefaultValue = CType(FormView1.FindControl("ITextArea"), System.Web.UI.WebControls.TextBox).Text 00174: Case "10002" 'int 00175: SQLTypeName = "int" 00176: CIAttr.InsertParameters("Default").Type = TypeCode.Int32 00177: CIAttr.InsertParameters("Default").DefaultValue = CInt(CType(FormView1.FindControl("ITextBox"), System.Web.UI.WebControls.TextBox).Text) 00178: Case "10003" 'datetime 00179: SQLTypeName = "datetime" 00180: CIAttr.InsertParameters("Default").Type = TypeCode.DateTime 00181: CIAttr.InsertParameters("Default").DefaultValue = CDate(CType(FormView1.FindControl("ICalendar"), System.Web.UI.WebControls.Calendar).SelectedDate) 00182: Case "10004" 'decimal(9,2) 00183: SQLTypeName = "decimal(9,2)" 00184: CIAttr.InsertParameters("Default").Type = TypeCode.Decimal 00185: CIAttr.InsertParameters("Default").DefaultValue = CDec(CType(FormView1.FindControl("ITextBox"), System.Web.UI.WebControls.TextBox).Text) 00186: Case Else 00187: SQLTypeName = "int" 00188: CIAttr.InsertParameters("Default").Type = TypeCode.Int32 00189: CIAttr.InsertParameters("Default").DefaultValue = CInt(CType(FormView1.FindControl("IDropDownList"), System.Web.UI.WebControls.DropDownList).SelectedValue) 00190: End Select 00191: Catch ex As Exception 00192: CType(FormView1.FindControl("IErr"), System.Web.UI.WebControls.Label).Text = "Ошибка конвертирования в заданный тип." 00193: Exit Sub 00194: End Try 00195: Try 00196: FormView1.InsertItem(False) 00197: AlterTable.SelectCommand = "ALTER TABLE [" & ExtTableName.Value & "] ADD [" & CType(FormView1.FindControl("IFieldName"), System.Web.UI.WebControls.TextBox).Text & "] " & SQLTypeName 00198: AlterTable.Select(New DataSourceSelectArguments) 00199: Catch ex As Exception 00200: CType(FormView1.FindControl("IErr"), System.Web.UI.WebControls.Label).Text = "Ошибка " & ex.Message 00201: Exit Sub 00202: End Try 00203: Response.Redirect("CITypeCard.aspx?id=" & Request.QueryString("typeid")) 00204: End Sub 00205: 00206: End Class
Надеюсь, два вышеприведенных примера непрямого использования функций привязки будут полезны для изучения заинтересованными лицами, однако я бы хотел остановиться тут еще на одной небольшой фишечке этой формы.
По идее, формы редактирования записей/карточек должны были бы иметь всего несколько строчек - заполнили параметры (116-119) и записали (151 строка) или заполнили (156-162) и вставили (196). Откуда же столько кода? Ведь, казалось бы, никто не мешает вписать в NVARCHAR(MAX) ЛЮБОЕ УМОЛЧАНИЕ.
Все дело в том, что этим дополнительным кодом я реализовал в SQL хранения умолчаний заведомо неизвестного типа SQL-поля не какие-то суррогатные SQL-типы, а настоящий SQL_VARIANT. Этот тип поля многие не любят из-за его невысокой эффективности и неиндексируемости. Однако в этом проекте весьма крученая архитектура (метаданные/пользовательские_таблы) и применение этого поля тут я считаю оправданным (однако полная аргументация этого утверждения потребует весьма детального описания архитектуры).
Вот, собственно, что получается в SQL в результате этого "лишнего" кода - строки (119-150) и (163-194):
|