Visual Basic, .NET, ASP, VBScript
 

   
   
     

Форум - Работа с данными

Страница: 1 |

 

  Вопрос: DataRepeater и др. Добавлено: 28.12.06 16:10  

Автор вопроса:  Иван | Web-сайт: www.harami.ru
Дорогие друзья,
Упорно искал в Сети инструкцию, как заставить DataRepeater не только отображать данные, но и сохранять внесенные изменения, и не нашел нигде (если не считать совета сделать кнопку Update и нажимать ее каждый раз). Говорят, на диске MSDN есть пример. Если у кого есть, не сбросите ли мне на мыло?
Пока же сижу-мучаюсь. Приглашаю познакомиться с тем, что придумал (см. ниже), — но буду исключительно благодарен за любые другие идеи, поскольку моя еще сыровата.
И еще вопрос: если текст-бокс (числовой, Single) не связан с данными из БД, то он в ряде случаев не реагирует на функцию Format — это так? Например, я хочу, чтоб всегда отображалось 4 знака после десятичной запятой; в текст-бокс tbx1 введено значение 1.43. Применяем Format(tbx1.Text, "0.0000"). Если поле связано с данными, то получим 1.4300; а если не связано, то нули потеряем все равно. Можно ли с этим справиться?

=======================================================
DATA REPEATER
В моем проекте много форм, в некоторых есть DataRepeater (для краткости буду называть его Лентой), в некоторых нет. Данные качает UserControl, в котором размещены кнопки перехода по записям и т.п. Этот юзер-контрол один во всех формах, везде называется uscDATA. У него есть булиновское свойство IsLenta. Если в форме есть Лента, то на стадии Load форма присваивает этому свойству значение True. Ленты во всех формах называются drpLenta.

Итак, требуется создать Ленту, состоящую из трех полей для ввода текста (неважно, что это будет, текст-боксы или комбо-боксы): Автор, Название книги и Год издания — два поля String и одно Integer.
Сначала создаем таблицу в базе данных. Три поля: fldAuthor, fldBook, fldYear. Все поля обязательны для заполнения, не допускают Null и пустых строк и ограничены по количеству символов (напр., Автор не более 15 символов). Но мы все равно поля помечаем как необязательные и допускающие пустые строки. Условия прописываем только в графе "Условие на значение" (на примере поля автор):
Not Is Null And <> "" And Len([fldAuthor])<=15
А в графе "Сообщение об ошибке" пишем то, что будет у нас появляться в MsgBox:
В поле «Автор» введены неверные данные. (Поле является обязательным для заполнения и может содержать не более 15 символов)
======================================================
Таблица готова. Теперь создаем юзер-контрол ctlLIB (Public) с текст-боксами tbxAuthor, tbxBook и комбо-боксом dlbYear. (В комбо-бокс можно вводить данные из списка и вручную.) Создаем свойства для связи: pAuthor, pBook и pYear, прописываем инструкции чтения и изменения свойств — короче, делаем все, что делается для связи юзер-контрола с DataRepeater через RepeaterBindings.
Но для того, чтобы Лента могла не только отображать данные, но и сохранять изменения, сделаем еще две вещи:
1) Создадим коллекцию наших полей в разделе Declarations:

Public colCTL As Collection

Эта коллекция должна быть заполнена на стадии загрузки контрола:

Private Sub UserControl_Initialize()
    colCTL.Add tbxAuthor, "pAuthor"
    colCTL.Add tbxBook, "pBook"
    colCTL.Add dlbYear, "pYear"
End Sub

Обратите внимание: имена индексов в коллекции должны быть точно такими же, как и названия свойств.

2) Создаем булиновское свойство контрола IsChanged. Оно будет приобретать значение True, когда мы внесем изменение в какое-либо из полей. В противном случае лента будет обновлять данные всякий раз, когда мы просто перемещаемся по записям.
Это не так просто, как кажется, поскольку мы не можем использовать для этой цели событие Change: ведь это событие происходит и при простом перемещении по записям. Приходится использовать событие KeyPress, а для комбо-бокса еще и Click. (Здесь нужны валидаторы, чтобы не было реакции на нажатие клавиш F1, F2, Home, End и т.п., а заодно можно выполнить проверку допустимости вводимого символа. У меня для этого есть специальная функция, я
просто передаю ей номер режима и ссылку на поле, и она возвращает True, если нажата запрещенная клавиша. Но это уже другая тема). По-хорошему, следует также защититься от ввода неверных данных через буфер обмена.

Контрол готов. Сохраняем его как ocx, размещаем в Ленте и устанавливаем RepeaterBindings. Мы можем перемещаться по записям, но пока не можем их изменять: при перемещении на новую запись Лента будет сбрасывать измененные значения и восстанавливать старые.
======================================================

Для сохранения изменений нам необходимо событие, которое происходит прежде, чем мы переместимся на новую запись. Таких событий в Ленте я не нашел. Будем использовать событие рекордсета WillMove. (Мы помним, что данные нам качает контрол uscDATA, и рекордсет adoRS прописан там же.)
Сначала проверяем, есть ли в форме Лента. Затем проверяем, было ли изменение записи в Ленте. Если True и True, то вызывается функция обновления. Передаем ей ссылки на рекордсет, на форму и на ленту (можно было бы и только на форму, но так меньше писанины). Функция возвратит adStatusOK, если все в порядке, и adStatusCancel, если внесенные изменения противоречат условиям на значения в полях; в этом случае перемещение на другую запись будет отменено.

Private Sub adoRS_WillMove(ByVal adReason As ADODB.EventReasonEnum, adStatus As ADODB.EventStatusEnum, ByVal pRecordset As ADODB.Recordset)
    If IsLenta Then
        If UserControl.Parent.drpLenta.RepeatedControl.IsChanged = True Then
            adStatus = fpUpdLenta(pRecordset, UserControl.Parent, UserControl.Parent.drpLenta)
        End If
    End If
End Sub

А теперь сама функция. (В ней не предусмотрено обновление для чек-боксов, т.к. у меня в проекте их нет):

Public Function fpUpdLenta(aRS As Recordset, AFrm As Form, Lenta As DataRepeater) As Integer
    fpUpdLenta = adStatusOK
    Dim N As Integer, i As Integer, varFlds As String, varPNames As String
'Если в RepeatedControl только текстовые поля, следующую переменную можно объявить как String
    Dim varProps As Variant
'Подсчитываем количество Bindings в Ленте
    N = Lenta.RepeaterBindings.count
'Получаем имена полей, связанных со свойствами, и имена свойств
    For i = 1 To N
        varFlds = Lenta.RepeaterBindings(i).DataField
        varPNames = Lenta.RepeaterBindings(i).PropertyName
'Получаем значение для обновления из коллекции в UserControl
'(Если есть чек-боксы, то надо добавить определение типа элемента: TypeName(colCTL(varPNames)) и соответственно считывать свойство Text или Value)
        varProps = Lenta.RepeatedControl.colCTL(varPNames).Text
'Обновляем поле в Ленте
'Ошибка при некорректных данных воникает здесь!!!
On Error GoTo ErrH
        aRS.Fields(varFlds).Value = varProps
On Error GoTo 0
    Next i
'Пытаемся обновить запись:
On Error GoTo ErrH
    aRS.UpdateBatch adAffectCurrent
On Error GoTo 0
'Меняем отметку об изменении записи на False
    Lenta.RepeatedControl.IsChanged = False
Exit Function
ErrH:
'Здесь будут появляться сообщения об ошибках от Access, в том числе те, которые мы ввели в таблицу
    MsgBox Prompt:=Err.Description, Title:="Ошибка ввода данных", Buttons:=vbOKOnly + vbExclamation
    fpUpdLenta = adStatusCancel
End Function

====================================================
А теперь о проблемах этой функции, над которыми страдаю на данном историческом этапе.
1) В своем настоящем виде она обновляет записи при условии, если все введенные данные корректны. Если же в одном из полей данные неправильны, она восстанавливает прежние значения при переходе на другую запись, не выдавая никакого сообщения об ошибке.
Этой проблемы не возникало бы, если бы мы считывали значения не из полей (tbxAuthor и т.д.):
        varProps = Lenta.RepeatedControl.colCTL(varPNames).Text
— а из свойств (pAuthor и т.д.). Но я пока так и не нашел способа обратиться к этим свойствам в цикле. Можно, конечно, обойтись без цикла и к каждому свойству обратиться, так сказать, персонально. Если бы в проекте была только одна форма с Лентой — так бы и сделал. Можно
и для нескольких форм: ввести Select Case и в зависимости от формы считывать нужные свойства поименно. Но хочется иметь красивую функцию, с циклом.

2) Проблемы с типом данных Variant. Если поле в Ленте Single, можно потерять десятичную запятую.

3) Проблема с Null. Например, поле Integer, но необязательное, может быть и незаполненным. Лента либо упорно вставляет 0, либо выдает сообщение об ошибке.



Ответить

  Ответы Всего ответов: 1  

Номер ответа: 1
Автор ответа:
 Иван



Вопросов: 34
Ответов: 53
 Web-сайт: www.harami.ru
 Профиль | | #1
Добавлено: 28.12.06 21:24
ДОПОЛНЕНИЕ К СОБСТВЕННОМУ ПОСТУ
В перечне проблем функции пункт 1 отменяется ("обновляет записи при условии, если все введенные данные корректны";). Всё прекрасно работает. Просто забыл отследить изменение свойства IsChanged по событию нажатия клавиш Delete и Backspace. (Т.е. надо контролировать не только KeyPress, но и KeyDown).
Пункты 2 и 3 остаются в силе.

Ответить

Страница: 1 |

Поиск по форуму



© Copyright 2002-2011 VBNet.RU | Пишите нам