Лена |
Отправлено: 04.10.2006, 12:02 |
|
Мастер участка
Группа: Участник
Сообщений: 501
|
В базе данных есть колонки с типом timestamp (время и дата). Если пользователь вводит не правильно данные в эти колонки (в гриде), то возникает исключение. Как правильно перехватить и обработать такое исключение:
Связь:
ADOQuery -> TDatasetProvider -> TClientDataset -> TDatasource
Для провайдера написала обработчик, но он не срабатывает:
CODE |
void __fastcall TDataModule2::DataSetProviderGrupUpdateError(
TObject *Sender, TCustomClientDataSet *DataSet, EUpdateError *E,
TUpdateKind UpdateKind, TResolverResponse &Response)
{
ShowMessage("Не правильный ввод значения");
Response = rrSkip;
}
| |
|
olegenty |
Отправлено: 04.10.2006, 12:31 |
|
Ветеран
Группа: Модератор
Сообщений: 2412
|
typedef void __fastcall (__closure *TFieldNotifyEvent)(TField* Sender);
__property TFieldNotifyEvent OnValidate = {read=FOnValidate, write=FOnValidate};
Description
Write an OnValidate event handler to validate changes made to the data in the field, just before the data is written to the current record buffer. The EditMask property allows validation of the data on a character by character basis while it is being entered by the user. OnValidate allows an application to validate the data as a whole.
|
|
Лена |
Отправлено: 04.10.2006, 12:48 |
|
Мастер участка
Группа: Участник
Сообщений: 501
|
Написала для нужного поля, никакой реакции. Все равно исключение:
CODE |
void __fastcall TDataModule2::ClientDataSetGrupstimeValidate(
TField *Sender)
{
ShowMessage("Не правильный ввод значения");
Abort();
}
| |
|
Admin |
Отправлено: 04.10.2006, 15:24 |
|
Владимир
Группа: Администратор
Сообщений: 1190
|
Конечно обычный DBGrid не удобен для
ввода/редактирования значений полей.
Все-же для исключения обычных ошибок ввода служат
специальные DB-компоненты, например для дат -
TDBDateTimeEditEh из библиотеки компонентов EhLIb,
или TDateEdit из библиотеки RxLib.
Или вводят данные в обычных компонентах TDateTimePicker
а после проверки введенных данных уже вручную вставляют
их в базу.
Тоже самое и с другими данными — целыми и дробными числами,
списками возможных значений и пр.
---
Самое простое(но не самое правильное) — взять компонент
ApplicationEvents и обрабатывать любое событие OnException.
CODE |
void __fastcall TForm1::ApplicationEvents1Exception(TObject *Sender,
Exception *E)
{
ShowMessage("Случилась большая беда: "+E->Message);
}
//---------------------------------------------------------------------------
|
наверное более правильным будет вот так:
CODE |
//---------------------------------------------------------------------------
// для некоего поля Area типа TDateTime
// используем обработчик OnSetText
//
void __fastcall TForm1::ClientDataSet1AreaSetText(TField *Sender,
const AnsiString Text)
{
try{
// здесь выполняем любые проверки значения поля
TDateTime dtDate = StrToDate(Text);
ClientDataSet1Area->AsDateTime = dtDate;
}catch(...){
ShowMessage("Неверное значение в поле: 'Area' !");
// Abort(); — Кажется он здесь не нужен.
}
}
//---------------------------------------------------------------------------
|
а задать предварительные ограничения на значения полей
можно в свойстве CustomConstraint проверяемого поля,
например: Area> 1000 and Area < 2850 (для целых чисел)
а в ConstraintErrorMessage — сообщение об ошибке, которое
будет при этом выводиться.
Можно прочитать также здесь:
http://dchumichkin.narod.ru/bookdatabase/glava_06.html
|
|
Лена |
Отправлено: 04.10.2006, 17:14 |
|
Мастер участка
Группа: Участник
Сообщений: 501
|
Что-то в моем случае не удается сделать проверку.
В поле ввода дат ввела маску !00/00/0000;1;_
Реально там такие значения: 08.12.1999
Теперь осталось проверить допустимые значения. Объясните, пожалуйста, почему не срабатывает обработчик OnValidate ниже:
CODE |
void __fastcall TDataModule2::ClientDataSetGrupstimeValidate(
TField *Sender)
{
Word Year, Month, Day;
DecodeDate(Sender->Value, Year, Month, Day);
if(Day > 31 || Day < 1 || Month > 12 || Month < 1)
{
ShowMessage("Не верно");
Abort();
}
}
|
Если поместить в OnSetText, то запись возвращается в исходное положение.
Код:
CODE |
void __fastcall TForm1::ClientDataSet1AreaSetText(TField *Sender,
const AnsiString Text)
{
try{
// здесь выполняем любые проверки значения поля
TDateTime dtDate = StrToDate(Text);
ClientDataSet1Area->AsDateTime = dtDate;
}catch(...){
ShowMessage("Неверное значение в поле: 'Area' !");
// Abort(); — Кажется он здесь не нужен.
}
}
|
Генерирует промежуточное исключение самой IDE.
Как убрать все исключения и почему OnValidate вообще не работает?
Отредактировано Лена — 04.10.2006, 17:16 |
|
Admin |
Отправлено: 04.10.2006, 17:43 |
|
Владимир
Группа: Администратор
Сообщений: 1190
|
QUOTE | Если поместить в OnSetText, то запись возвращается в исходное положение.
|
А приведите код ? Где вы в OnSetText делаете проверку
на допустимость значений.
(в приведенном примере эта проверка выполняется
в блоке try...catch и в случае ошибки — генерится
исключение и выдается сообщение об ошибке.
В этот же try можете засунуть и другие проверки
на возможные значения и в случае ошибки генерить
исключение например throw 1, которое будет
обрабатываться в блоке catch)
CODE |
//---------------------------------------------------------------------------
// для некоего поля Area типа TDateTime
// используем обработчик OnSetText
//
void __fastcall TForm1::ClientDataSet1AreaSetText(TField *Sender,
const AnsiString Text)
{
try{
// здесь выполняем любые проверки значения поля
TDateTime dtDate = StrToDate(Text); // проверка на формат даты
if(dtDate < StrToDate("12.10.2003")) throw 1; // дата меньше указанной — ошибка
ClientDataSet1Area->AsDateTime = dtDate; // всё нормально
}catch(...){
ShowMessage("Неверное значение в поле: 'Area' !");
}
}
//---------------------------------------------------------------------------
|
|
|
Лена |
Отправлено: 04.10.2006, 18:08 |
|
Мастер участка
Группа: Участник
Сообщений: 501
|
>А приведите код ? Где вы в OnSetText делаете проверку
На "ты"
Я там вписываю то же что и в OnValidate:
CODE |
Word Year, Month, Day;
DecodeDate(Sender->Value, Year, Month, Day);
if(Day > 31 || Day < 1 || Month > 12 || Month < 1)
{
ShowMessage("Не верно");
Abort();
}
|
Что касается кода:
CODE |
void __fastcall TForm1::ClientDataSet1AreaSetText(TField *Sender,
const AnsiString Text)
{
try{
// здесь выполняем любые проверки значения поля
TDateTime dtDate = StrToDate(Text); // проверка на формат даты
if(dtDate < StrToDate("12.10.2003")) throw 1; // дата меньше указанной — ошибка
ClientDataSet1Area->AsDateTime = dtDate; // всё нормально
}catch(...){
ShowMessage("Неверное значение в поле: 'Area' !");
}
}
|
то допустим я ввела ошибочно дату 08.14.1988. Поскольку месяца 14 не бывает, то на строке TDateTime dtDate = StrToDate(Text); IDE генерит сразу исключение. Хочется избавиться от исключения которое генерит IDE.
Отредактировано Лена — 04.10.2006, 18:10 |
|
Admin |
Отправлено: 04.10.2006, 18:18 |
|
Владимир
Группа: Администратор
Сообщений: 1190
|
Так а зачем от него избавляться-то ?
Ведь тогда сразу это исключение перехватит catch,
в котором и можно предупредить о неправильном
вводе в конкретном поле.
CODE |
catch(...){
AnsiString s = "Неправильная дата в поле 'Дата': "+Text;
ShowMessage(s);
} |
И никаких других окошек/исключений не будет.
|
|
Admin |
Отправлено: 04.10.2006, 18:26 |
|
Владимир
Группа: Администратор
Сообщений: 1190
|
Sender->Value в OnSetText работать не должно.
Значение полю там еще не присвоено, оно только в Text !
Если значение правильно — его необходимо там присвоить
ClientDataSet1Area->AsDateTime = dtDate;
или для текстового поля скажем
ClientDataSet1Area->AsString = Text;
|
|
Лена |
Отправлено: 04.10.2006, 18:36 |
|
Мастер участка
Группа: Участник
Сообщений: 501
|
Понятно. Оставлю IDE исключение.
Просто мне удалось избавиться от исключения IDE когда требуется обязательный ввод значений. Я реализовала так:
CODE |
void __fastcall TDataModule2::ClientDataSetOrganBeforePost(
TDataSet *DataSet)
{
if(Form1->DBGrid3->Fields[1]->Value.IsNull() || Form1->DBGrid3->Fields[3]->Value.IsNull())
{
ShowMessage("Поля \"Фамилия\" и \"Имя\" должны быть заполнены");
Abort();
}
}
|
Пока пользователь не введет значения будет при переходе на новую строку получать ShowMessage.
Отредактировано Лена — 04.10.2006, 18:38 |
|
Admin |
Отправлено: 04.10.2006, 18:45 |
|
Владимир
Группа: Администратор
Сообщений: 1190
|
"Понятно. Оставлю IDE исключение"
Что за IDE исключение ???
В случае с OnSetText будет показываться окошко только с тем
текстом, что будет в ShowMessage() в catch.
Если выскакивает какое-то еще окошко с исключением, то это
только когда программа запускается из среды C++Builder.
Попробуйте — откомпилите приложение и запустите не из среды
C++Builder, например из Проводника — и увидите, что никаких
лишних других окошек с исключениями не будет.
|
|
Лена |
Отправлено: 04.10.2006, 18:49 |
|
Мастер участка
Группа: Участник
Сообщений: 501
|
Вот то единственное исключение которое происходит до сработки catch. Оно на строке:
TDateTime dtDate = StrToDate(Text);
P.S. Да без среды IDE все нормально — исключения нет. Просто думала избавиться от него и в период разработки.
Отредактировано Лена — 04.10.2006, 18:51
Присоединить изображение
|
|
Admin |
Отправлено: 04.10.2006, 18:51 |
|
Владимир
Группа: Администратор
Сообщений: 1190
|
Так вот об этом я и пишу, что запустив программу не из
среды C++Builder этого исключения не будет !
Только сообщение ShowMessage из catch
|
|
Admin |
Отправлено: 04.10.2006, 18:55 |
|
Владимир
Группа: Администратор
Сообщений: 1190
|
А в период разработки тоже избавиться не сложно — это
галочка в опциях проекта, не помню сейчас где,
и эти исключения тоже не будут появляться из среды разработки.
|
|
Лена |
Отправлено: 04.10.2006, 19:00 |
|
Мастер участка
Группа: Участник
Сообщений: 501
|
Ясно.
P/S/
Для общего развития как можно использовать onValidate событие?
Оно у меня вообще не отробытывает:
CODE |
void __fastcall TDataModule2::ClientDataSetGrupstimeValidate(
TField *Sender)
{
int c = 0; //поставила точку остановки и не поподаю сюда
}
|
Не могу попасть в тело события. |
|
Admin |
Отправлено: 04.10.2006, 19:44 |
|
Владимир
Группа: Администратор
Сообщений: 1190
|
Если я правильно понял: (этот документ уже советовал изучить)
http://dchumichkin.narod.ru/bookdatabase/glava_06.html
то событие OnValidate происходит уже после события OnSetText.
То есть последовательность событий следующая:
1. Начало редактирования — перевод в состояние dsEdit (dsInsert)
2. OnSetText
3. попытка присвоения введенного значения полю
4. OnValidate
5. OnChange
6. Post() данных в базу.
Оба события наступают до Post(), но отличаются тем, что в событии
OnSetText значение полю еще не присвоено, а в OnValidate — уже
присвоено — и в OnValidate можно его проверить и или пропустить
дальше, или отменить изменения — через Abort().
То есть — поскольку значение даты было в корне неверно,
то происходила попытка присвоения этого неправильного
значения полю(пункт 3.) и генерилось исключения, то есть до события
OnValidate дело просто не доходило.
Попробуйте ввести допустимое значения (с точки зрения формата
данных) в это поле и в onValidate событие наступит.
В нем можно будет проверить, например допустимый диапазон дат,
и если что-то не так — отменить ввод (событие Post) через Abort().
CODE |
//---------------------------------------------------------------------------
void __fastcall TForm1::ClientDataSetLAST_NAMEValidate(TField *Sender)
{
if(ClientDataSetLAST_NAME->AsString == "Лена"){
ShowMessage("Имя Лена в данном поле недопустимо !");
Abort();
}
}
//---------------------------------------------------------------------------
|
|
|
Лена |
Отправлено: 04.10.2006, 21:23 |
|
Мастер участка
Группа: Участник
Сообщений: 501
|
Огромное спасибо! Теперь все понятно! |
|