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

Выражения привязки контролов к данным в 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:

CDisplays numeric values in currency format.
DDisplays numeric values in decimal format.
EDisplays numeric values in scientific (exponential) format.
FDisplays numeric values in fixed format.
GDisplays numeric values in general format.
NDisplays numeric values in number format.
XDisplays numeric values in hexadecimal format.
и может быть например таким {0:F5}.

В самих выражениях привязки может быть использовано много чего, вплоть до индексов, но первая сложность при составлении таких выражений - почти всегда используется странный литерал - "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):




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