C++ Builder
| Главная | Уроки | Статьи | FAQ | Форум | Downloads | Литература | Ссылки | RXLib | Диски |

 
Обработка ошибок, при вводе
Лена
Отправлено: 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 делаете проверку

На "ты" smile.gif

Я там вписываю то же что и в 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 исключение. smile.gif
Просто мне удалось избавиться от исключения 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 все нормально — исключения нет. Просто думала избавиться от него и в период разработки. smile.gif

Отредактировано Лена — 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



Огромное спасибо! Теперь все понятно!

Вернуться в Работа с базами данных в C++Builder