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

 
Решение для создания универсального справочника, требуется критика
olegenty
Отправлено: 26.08.2005, 09:07


Ветеран

Группа: Модератор
Сообщений: 2412



суть.
1. простенькие типы для работы с универсальным справочником:
CODE

typedef map<AnsiString, Variant> TValMap; // Result Fields Type
typedef auto_ptr<TValMap> TValMapPtr;     // Result Fields Pointer Type
typedef pair<bool, TValMapPtr> TResPair; // Result Fields Pointer Pair Type
typedef auto_ptr<TResPair> TResPairPtr; // Result Fields Pointer Pair Pointer Type

2. как же это можно использовать. есть некая формёшка, которая по имени справочника выполняет некий запрос к БД и выводит его юзеру. юзер скроллируется, и нет-нет, да выберет какую-нибудь строку. вот что происходит дальше (если он нажал OK):
CODE

TValMapPtr Fields(new TValMap);
TFields *f = ds->DataSet->Fields;
TField *p = NULL;
for (int i = 0; i < f->Count; ++i)
{
   p = (*f)[i];
   (*Fields)[p->FieldName] = p->Value;
}
return TResPairPtr(new TResPair(true, Fields));

а это, если ничего не выбрал:
CODE

return TResPairPtr(new TResPair(false, static_cast<TValMapPtr>(NULL)));

а пользоваться результатом так:
CODE

TResPairPtr r = Callback->LookupDict(dbgRouteSpec->InplaceEditor, "Measure");
if ((*r).first)
{
   TValMapPtr Fields = (*r).second;
   TDataSet *d = dbgRouteSpec->DataSource->DataSet;
   if (d->State != dsEdit) d->Edit();
   d->FieldByName("Short")->AsString      = (*Fields)["Short"];
   d->FieldByName("MeasureID")->AsInteger = (*Fields)["MeasureID"];
}


в результате, проблема с тормозными лукап полями отваливается раз и навсегда, а код вроде достаточно универсален. кроме прочего, избавляет от переменного числа параметров ссылочного типа, для разного числа используемых полей.

а теперь критикуйте (может как-то ещё лучше можно сделать).
AVC
Отправлено: 26.08.2005, 10:08


Ветеран

Группа: Модератор
Сообщений: 1583



Отлично!
Я от lookup отказался много лет назад и использую подобную технологию, правда реализованную "по-старинке" при помощи класса на базе TList и нескольких универсальных форм (функций). Давно чесались руки перевести на stl (но это такой объем работы...). А тут вижу готовую реализацию.

А теперь замечания.
Мне кажется, что pair и далее излишни — можно вернуть NULL при отказе от выбора, или я ошибаюсь?

По собственному опыту могу сказать, что map'а для действительно универсального использования мало. Может map vector'ов? Иногда нужны одноименные поля и поиск "от головы" и "от хвоста". И т.д. и т.д. Но это все для серьезной универсализации. Если будет интересно могу показать перечень методов, которыми за несколько лет "оброс" у меня этот класс.
olegenty
Отправлено: 26.08.2005, 10:29


Ветеран

Группа: Модератор
Сообщений: 2412



у меня тоже был TList, но map приятнее, не нужны проверки на уникальность имени поля, проще в использовании. в обёртке auto_ptr — гарантированность от утечки ресурсов. относительно pair, да, наверное можно и просто NULL, но пока оставлю так, имел я проблему, когда "клиент" получал NULL вместо auto_ptr< ...>, потому что потом auto_ptr вызывает delete, a delete NULL приводило к AV. а вот про
map < AnsiString, vector < Variant>> — это вполне, ты только тогда объясни зачем и как хотел бы использовать (мне пока такого было не надо).

Отредактировано olegenty — 26/08/2005, 11:31
AVC
Отправлено: 26.08.2005, 10:52


Ветеран

Группа: Модератор
Сообщений: 1583



QUOTE

меня тоже был TList, но map приятнее

Когда я его начинал я не использовал stl (из за незнания). Да и до сих пор не очнь активно ее использую (каюсь) smile.gif

QUOTE

map < AnsiString, vector < Variant>> — это вполне, ты только тогда объясни зачем и как хотел бы использовать (мне пока такого было не надо).

Повторюсь — речь не о справочниках.
Иногда нужны одноименные поля и поиск "от головы" и "от хвоста". И т.д. и т.д
Кроме того пары имя — значение (что дает тебе map) маловато. Например к каждому полю я еще храню заголовок, формат, шаблон ввода и тд (всего 11 описаловок) и этого иногда не хватает. Этот класс я использую практически везде и это позволяет мне делать универсальные, самонастраиваущиеся функции. Параметр — указатель на экземпляр, а это означает переменное число параметров типов, которые можно запихнуть в вариант + практически любой возврат. Это, конечно, где-то противоречит концепции ООП, но так удобно.
olegenty
Отправлено: 26.08.2005, 11:07


Ветеран

Группа: Модератор
Сообщений: 2412



стоп. то, что привёл, возращает значения всего одной строки.
тогда давай так: ты скажи, что должно передаваться, а я изображу это на stl, потом покажем Георгию, он оценит (я с stl всего несколько месяцев, как начал работать. тащусь, как удав по стекловате.)
AVC
Отправлено: 26.08.2005, 11:34


Ветеран

Группа: Модератор
Сообщений: 1583



QUOTE

стоп. то, что привёл, возращает значения всего одной строки

Справедливо, но
1. Полей в этой строке неограниченное количество — я обычно делаю такую штуку
параметр вызова дочерней функции/формы
= входные параметры формы
+ все значения редактируемые этой формой
+ значения всех полей текущей строки основного ds этой формы
2. У меня естественно есть класс массив таких строк
3. пунк 2 не нужен а надо, наверное, vector.

smile.gif Как и DataSet я всегда работаю с одной (текущей) строкой. Операции над выборкой пусть делает сервер.

QUOTE

давай так: ты скажи, что должно передаваться, а я изображу это на stl, потом покажем Георгию

Нее. Мне самому интересно перевести свой класс на stl. Такая практика (правда если доживу).

Возвращаясь к первому топику — по моему дилетанскому мнению там все нормально (pair на твоей совести). Разве что стоит предусмотреть возврат MultiSelect, но это на будущее.

Кстати для твоего примера использования у меня в классе предусмотрены два метода ->GetAllFields и ->PutAllFields со всякими наворотами типа анализа RO или списками полей. Но в простом случае rec->GetAllFields(ds2)->PutAllFields(ds1);
olegenty
Отправлено: 26.08.2005, 12:14


Ветеран

Группа: Модератор
Сообщений: 2412



всё, я поплыл. нифига не понял (почти).
массив значений я никогда не передам, как мап вариантов, на это есть TMemTableEh — передавай, не хочу. мультирекорд засуну туда, и как-то оберну в STL. и редактироваться будет стандартным образом.

но напряг в понимании от того, что у меня нет универсальных форм. ЭТО не поддаётся универсализации (вернее, поддаётся, но я ещё не дорос конструировать на лету структуру формы, с подчинёнными деревьями и прочими вещами, с автораскраской по хаданным правилам и т.д.)
AVC
Отправлено: 26.08.2005, 12:37


Ветеран

Группа: Модератор
Сообщений: 1583



Считаем со справочниками закончили (по крайней меры мы с тобой smile.gif )?

QUOTE

но я ещё не дорос конструировать на лету структуру формы, с подчинёнными деревьями и прочими вещами, с автораскраской по хаданным правилам и т.д.)

Не прибедняйся. Просто ты не ставил такой задачи. Ни чего сложного в этом нет если строить не произвольную форму, а одну из заранее разработанного шаблона (не stl а именно шаблона). Что сложного прочитать какие части нужны, как их покрасить и что делать по заранее определенным событиям?
Первый вариант я реализовал около 2 лет назад и с тех пор "тащусь, как удав по стекловате". Попробуй переформулировать свое приложение и свести его к 5-6 универсальным формам, а уж на них можно так развернуться...!
olegenty
Отправлено: 26.08.2005, 13:13


Ветеран

Группа: Модератор
Сообщений: 2412



я не про такую раскраску. у меня зело накрашенные гриды. красятся из накоторых соображений, в зависимости от значений полей. я ещё пока не придумал, как это реализовать. придумаю — постараюсь предствить форму как набор "фреймов" — по сути, придётся реализовать что-то своего собственного DFM, с предопределёнными обработчиками событий. но это чуть позже.
avc*
Отправлено: 26.08.2005, 13:39


Не зарегистрирован







QUOTE

не про такую раскраску. у меня зело накрашенные гриды. красятся из накоторых соображений, в зависимости от значений полей. я ещё пока не придумал, как это реализовать

И я не про такую. С гридами я сделал так (на уровне идеи)
CODE

Select ...
-- простой варинт
,Decode(NVL(IsActive,0), 0,''Gray'', ''Black'') as Row_FColor

-- Сложный вариант — условие отбора (а может и цвет) выбиратся
-- пользователем, как один из параметров запроса
<#ifNotEmpty pWhatLight_CMD#>
,(Case
  when <#fld pWhatLight_CMD#> then ''red''
  else ''black''
 end
) as Row_FColor
<#/ifNotEmpty pWhatLight_CMD#>
--


Это было по строкам
А так по колонкам
CODE

vmap := HHC.TFieldsMap(-- Name   Visible  Len  Title           Align  Format  FColor    BColor     BColorLo
HHC.TFieldMap.NewFormaItem('Grid_StaticColumns', 'MeasureDate')
,HHC.TFieldMap.NewItem('MeasureDate',   1, 12, 'Дата',           'c', 'dd.mm.yy', 'Blue', '#F0F0F0', 'White')
,HHC.TFieldMap.NewItem('RecoundID',     0)
,HHC.TFieldMap.NewItem('ORD',           0)
--
,HHC.TFieldMap.NewItem('PAYSUM1',       1,  7, 'Оплата',         'r', 'd2'  )
,HHC.TFieldMap.NewItem('DOLG',          0,  8, 'Долг',           'r', 'd2',  'Red',    '#F0F0F0', 'White')
,HHC.TFieldMap.NewItem('DOLGsNDS',      1,  8, 'Долг|с ндс',     'r', 'd2',  'Red',    '#F0F0F0', 'White')
,HHC.TFieldMap.NewItem('DOLGNDS',       1,  7, 'НДС',            'r', 'd2',  'Black',  '#F0F0F0', 'White')
,HHC.TFieldMap.NewItem('AVANS',         1,  8, 'Аванс',          'r', 'd2',  'Green',  '#F0F0F0', 'White')


А так весь грид
CODE

--HHC.AppM.UESetGridColor(vmap, vGroupName);
--
-- Эту форму буду раскрашивать вручную
--
HHC.p_TFieldsMap_Add(vmap, HHC.TFieldMap.NewFormaItem('Grid_FColor',    '#000000'));
HHC.p_TFieldsMap_Add(vmap, HHC.TFieldMap.NewFormaItem('Grid_BColor',    '#F6FfF6'));
HHC.p_TFieldsMap_Add(vmap, HHC.TFieldMap.NewFormaItem('Grid_BColorLo',  'white'  ));

HHC.p_TFieldsMap_Add(vmap, HHC.TFieldMap.NewFormaItem('Record_ExtendInfo_FColor',   '#0000Ff'));
HHC.p_TFieldsMap_Add(vmap, HHC.TFieldMap.NewFormaItem('Record_ExtendInfo_BColor',   '#FfFfF0'));
HHC.p_TFieldsMap_Add(vmap, HHC.TFieldMap.NewFormaItem('Record_ExtendInfo_BColorLo', 'white'  ));

HHC.p_TFieldsMap_Add(vmap, HHC.TFieldMap.NewFormaItem('SubTitle_FColor',   '#000000'));
HHC.p_TFieldsMap_Add(vmap, HHC.TFieldMap.NewFormaItem('SubTitle_BColor',   '#FfFfFf'));
HHC.p_TFieldsMap_Add(vmap, HHC.TFieldMap.NewFormaItem('SubTitle_BColorLo', 'white'  ));
--


А так, что делать по нажатию клавиш
CODE

HHC.p_TFieldsMap_Add(vmap, HHC.TFieldMap.Go_AxRec ('AxPing',                 'Ping'));
HHC.p_TFieldsMap_Add(vmap, HHC.TFieldMap.Go_AxRec ('SVLanSub_CheckStateAll', 'Кто в сети',          '', 'HCSVISOR', 0,0));


А так какие и как считать суммы
CODE

HHC.p_TFieldsMap_Add(vmap, HHC.TFieldMap.NewSumItem('Account', 'Count', '0'));
HHC.p_TFieldsMap_Add(vmap, HHC.TFieldMap.NewSumItem('PotC1', 'Sum', '0'));


и т.д. (это куски того самого скрипта) на PLSql
olegenty
Отправлено: 26.08.2005, 13:50


Ветеран

Группа: Модератор
Сообщений: 2412



PlSQL — тёмный лес, но идея яснее ясного.
olegenty
Отправлено: 26.08.2005, 13:55


Ветеран

Группа: Модератор
Сообщений: 2412



я хочу сделать по-другому. так не нравится, потому что 50% получаемой от сервера информации в этом случае — служебная. поэтому на сервере хочу хранить только некоторым образом описанный алгоритм раскраски. а на клиенте для алгоритмов иметь универсальный и быстрый разборщик, который будет запускаться в OnDrawColumnCell грида.
AVC
Отправлено: 26.08.2005, 13:57


Ветеран

Группа: Модератор
Сообщений: 1583



Маленькое добавление. (упустил smile.gif )
Не смотри на HHC.TFieldMap.New...
У меня это просто способ добавить строку в динамический создаваемый курсор. На Oracle это легко сделать через объект.
AVC
Отправлено: 26.08.2005, 14:04


Ветеран

Группа: Модератор
Сообщений: 1583



QUOTE

не нравится, потому что 50% получаемой от сервера информации в этом случае — служебная

Откуда?
Скрипт читается и анализируется один раз при старте формы, а в рабочий запрос можно (именно можно а не нужно) добавть три поля Row_FColor, Row_BColor и Row_BColorLo. Если их нет, то используются правила колонки — сетки — шаблона формы.
А закон раскраски в этом случае я могу определить очень гибко именно для конкретно сейчас выполняемого запроса.

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