Вы здесь

bBrowser: private investigations (Dire Straits)

private investigationsЯ долго думал, как оформить свой материал: в виде записи в дневнике (блоге) или отдельной статьёй. Обычно, всю «текучку» я оформляю лёгкими заметками, понимая, что актуально сейчас – через время – никому не надо. А вот более «долгоиграющие» вещи пишу в виде статей.

 
Но здесь задумался… Ведь, разговор пойдёт об ошибках и неудобствах, а они – через какое-то время – будут разработчиком устранены. Да, после каждого обновления библиотеки bBrowser приходится тщательно отсматривать изменения. И удалять ставшие ненужными прежние «прокладки». Так и должно быть!
 
А, с другой стороны – подымая эту тему, я показываю свой метод работы, подход к решению проблем, свои взгляды. Т.е., даю инструмент своему читателю. А это – намного серьёзнее, поэтому...
 
Объект исследования – bBrowser v.3.0.9.109.
 
 
Как всегда, буду говорить только то, что знаю, и делать так, как умею! Можете лучше, точнее – всегда «пожалуйста», жду Ваших критически горячих и обоснованных комментариев. Приступим. :))
 
Как инструмент, bBrowser возник достаточно давно, постепенно вытесняя конкурентов и самоделки. Но, я знаю несколько людей, которые с удовольствием продолжают использовать и развивать собственные разработки, ничуть не уступающие лидеру. Но, это я отклонился от темы. Возвращаюсь :)). Поэтому bBrowser «болеет», как болезнями «роста», так и – «хроническими» заболеваниями. Тут нужны уточнения.
 
Можно выделить несколько групп ошибок:
  • Грубые ошибки разработчиков: заявляется одно – а делается (или не делается) другое.
  • Мягкие ошибки, возникающие из-за того, что разработчики упрощают задачи, предполагая, что пользователи будут работать строго по правилам.
  • Непредвиденные ошибки, как правило, вносимые самой средой или инструментом разработки (для bBrowser – это ошибки из CA-Visual Objects).
 
Болезни «роста» (я ими тоже подвержен) обычно возникают при скоростной разработке проекта, когда внимание переносится то на одну группу задач, то на другую. Внимание рассеивается, и отдельные вещи могут упускаться из виду. Тут надёжный помощник – план разработки (проект) и чёткое ему следование. Невзирая на давление со стороны заказчика. Т.к. сам заказчик зачастую не может компетентно решать, что сейчас важно, а что – нет. Известная тема, что не дело программиста «учить жизни» заказчика, и – наоборот.
 
Немцы достаточно скрупулезные товарищи, поэтому грубых ошибок почти нет. У них всегда есть план и они «морально устойчивы» в плане отклонения от схем. Основная масса ошибок состоит в том, что они не всегда внимательно отслеживают (или учитывают) поведение пользователей. Т.е., в большинстве случаев – обычное упрощение, из-за которого страдает надёжность библиотеки.
 
Из всего комплекта библиотеки я использую в полной мере всего три элемента:
  • bBrowser Class
  • bArrayServer Class
  • bIcon Class
 
bIcon Class использую совсем немного, ничего существенного по нему не скажу. Хотя замечания есть. Поэтому основные замечания будут по классам «bBrowser» и «bArrayServer».
 
Проблемы bBrowser-а:
  1. Поддержка (отключение) тем Windows.
  2. Указание размеров габаритных элементов управления (контролов), типа ComboBox, при редактировании в ячейке.
  3. Отсутствие обработки события выбора «мышкой» в элементе ComboBox (и ему подобных) при редактировании в ячейке.
  4. Раскраска.
  5. Неполная поддержка AdoRecordSet.
Проблемы bArrayServer-а:
  1. Ошибки при создании индекса (ордера).
  2. Ошибки при удалении (очистке) ордера.
  3. Ошибки при копировании в файл.
  4. Недоработка в методе FieldPut.
  5. Ошибки получения информации об ордере.
Это не всё. Но, для начала хватит. :))
 
 
bBrowser.
 
1. Поддержка (отключение) тем Windows.
 
По поводу работы с темами Windows я достаточно обстоятельно рассказал в своём материале под названием «Visual Styles: CA-Visual Objects, bBrowser и все-все-все…»
Смысл своего решения я свёл к использованию «продвинутой» функции:
 
FUNCTION ms_EnableAppVisualTheme( lEnable := TRUE AS LOGIC,;
                                                            liThemeFlags := -1 AS LONG ) AS LOGIC PASCAL
      // Pencil 09.11.2011
 
// liThemeFlags:
// STAP_ALLOW_NONCLIENT      - Specifies that the nonclient areas of
//          application windows will have visual styles applied.
// STAP_ALLOW_CONTROLS       - Specifies that the common controls
//          used in an application  will have visual styles applied.
// STAP_ALLOW_WEBCONTENT     - Specifies that web content displayed
//          in an application will have visual styles applied.
 
      LOCAL dwStyle AS DWORD
 
// Сначала отрабатываем через bBrowser
      bvsEnableVisualStyles( lEnable )
 
// Уже не надо, т.к. сделали это через bvsEnableVisualStyles
//    EnableAppVisualTheme( lEnable )
 
// Принудительно делаем как в КаВо, т.к. у bBrowser-а оно по-другому :))
      IF liThemeFlags >= 0
            dwStyle := DWORD( _CAST, liThemeFlags )
      ELSE
            IF lEnable
                  dwStyle := _OR( STAP_ALLOW_NONCLIENT, STAP_ALLOW_CONTROLS, STAP_ALLOW_WEBCONTENT )
            ELSE
                  dwStyle := STAP_ALLOW_NONCLIENT
            ENDIF
      ENDIF
 
      SetThemeAppProperties( dwStyle )
 
//    RETURN ( VerifyThemeState( ) )     // Для КаВо 28
      RETURN ( IsThemeEnabled( ) )       // Для КаВо 27
 
 
2. Указание размеров габаритных элементов управления (контролов), типа ComboBox, при редактировании в ячейке.
 
Наверное, многие при правке-вводе данных через браузер использовали более «продвинутые» элементы редактирования. Класс bBrowser позволяет это делать.
Например:  oColumn:ViewValueAs := #ComboBox
 
И я с удовольствием использую ComboBox. Есть и другие объёмные контролы, которые не помещаются в ячейку, распахиваясь при начале редактирования. Основной недостаток – невозможность уйти от стандартной высоты ( height=80 )…
(Внимание!!! Эта ситуация наблюдается только тогда, когда у Вас нет строки манифеста. Если добавить строку: «RESOURCE CREATEPROCESS_MANIFEST_RESOURCE_ID RC_RT_MANIFEST %appwizdir%\cctl6.man» – данная проблема проявляться не будет.)
 
Для выхода из этой ситуации лучший путь – переопределение.
Так я создал свой класс-наследник:
 
CLASS msBrowser INHERIT bBrowser
      // Pencil 07.12.2007
 
В рамках него мы можем либо добавить управляющие переменные или напрямую изменить размеры создаваемых контролов. Я предвижу, что разработчики bBrowser через какое-то время сами что-то добавят. Поэтому не стал рисковать, прикинул обычные необходимые мне размеры и изменил их в методе напрямую:
 
METHOD EditCreate( iColumn, iRow, iRecNo, symClass, iStyle, uSpecial ) CLASS msBrowser
      // Pencil 04.01.2010
 
**********************************
***  the piece was eaten by mice ***
*******
            IF IsNil( iStyle )
 
// Pencil 22.06.2010
// Включил недостающую сортировку
//                iStyle := WS_CHILD+WS_TABSTOP+CBS_AUTOHSCROLL
                  iStyle := WS_CHILD+WS_TABSTOP+CBS_SORT+CBS_AUTOHSCROLL
            ENDIF
 
            IF symClass = #ComboBox
 
// Pencil 22.06.2010
// Удлинил выпадающий список
//                oEdit := ComboBox{SELF, 8001, Point{srcArea.Left, srcArea.Top}, Dimension{srcArea.Right-srcArea.Left, 80}, uSpecial, iStyle}
 
oPoint := Point{ srcArea.Left, srcArea.Top }
oDim := Dimension{ srcArea.Right - srcArea.Left, 200 }
oEdit := ComboBox{ SELF, 8001, oPoint, oDim, uSpecial, iStyle }
 
**********************************
***  the piece was eaten by mice ***
*******
 
3. Отсутствие обработки события выбора «мышкой» в элементе ComboBox (и ему подобных) при редактировании в ячейке.
 
Продолжим тему использования комбобокса в качестве элемента редактирования. Возможно, некоторые заметили, что если он раскроется вверх и перекроет собой заголовки колонок, то, после щелчка «мышки» (выбора подходящего пункта) список, конечно же, схлопнется. Но, артефакт на экране в виде прямоугольного пятна на заголовке колонок и выше (если список раскрылся и выше колонок) – останется. И будет таковым, если не сделать принудительное обновление окна или ещё раз не щёлкнуть на ячейку.
Как же так?
 
В CA-Visual Objects (и в версии 2.8 SP3) вообще отсутствует обработка события выбора «мышкой» элемента выпадающего списка. А разработчики bBrowser-а не стали этого поправлять в своей библиотеке. Это приводит к тому, что после щелчка «мышкой» работа продолжается, пока вы её не окончите, создав событие окончания редактирования.
 
Давайте исправлять:
 
METHOD EventHandler( oEvent ) CLASS msBrowser
      // Pencil 13.02.2012
 
      // Заставляем отрабатывать клик «мышкой» по записи в списке КомбоБокса:
      // Клик мыши – означает выбор
      // Ранее выбор определялся только нажатием клавиши ENTER
 
      LOCAL hWnd AS PTR
      LOCAL oChild AS OBJECT
 
 
      IF oEvent:uMsg == WM_COMMAND
 
            hWnd := PTR( _CAST, oEvent:lParam )
            IF .NOT. hWnd == NULL_PTR
 
                  oChild := __WCGetControlByHandle( hWnd )
 
// Проверяем объект: КомбоБох ли это?
                  IF .NOT. oChild == NULL_OBJECT .AND.;
                        IsInstanceOf( oChild, #ComboBox )
 
// CBN_SELENDOK - Указывает, что выбор сделан в выпадающем списке,
//                      в то время как он был раскрыт. Он должен быть принят.
                        IF HiWord( oEvent:wParam ) == CBN_SELENDOK
 
                        // der Owner mu? das Event verarbeiten, da ansonsten bei einer
                        // Combobox nicht der Value richtig gesetzt wird
                             SELF:oCommandOwner:Dispatch( oEvent )
 
                             // Если редактируем
                             IF SELF:InEdit()
 
                                   IF .NOT. SELF:EditClose() // Заканчиваем
 
                                         SELF:EditCancel() // Иначе - прерываем ...
                                   ENDIF
                             ENDIF
 
                             RETURN SELF:oCommandOwner:EventReturnValue
                        ENDIF
                  ENDIF
            ENDIF
      ENDIF
 
      RETURN ( SUPER:EventHandler( oEvent ) )
 
 
4. Раскраска.
 
Что я под этим понимаю? Раскраска строк и колонок в браузере. Обычно это делается с помощью ColorCondition (ниже пример из описания bBrowser):
// define color condition and add it to the browser
oColorCondition := bColorCondition{"Server:BIRTHDAY<CToD('01/01/60')",;
                                                                       odbsCUSTOMER,;
                                                                       Color{COLORRED}}
oBrowser:ColorCondition:Add(oColorCondition)
 
Нас соблазняют простотой обращения к серверу, типа Server:BIRTHDAY, даже если это поле не описано. Что ж… Достаточно просто. Работает. Но, если полей много (больше трёх), если много разной сложной раскраски, то программа чудесным образом начинает «сыпаться». При этом ошибки «одна чудесней другой», так что сразу понять, где же «порылась собака» - трудно.
 
Так в чём же проблема? А проблема в том, что строковое выражение раскрывается макроподстановкой. Не верите – посмотрите в SDK! А в Ca-Vo макроподстановка имеет свои ограничения. Чьи тут проблемы – Ca-Vo или bBrowser – меня не интересует. Но, я бы порекомендовал разработчикам: пишите примеры честнее, не надо приучать «пользователей» к «плохому». Поля в серверах надо либо заранее описывать, либо использовать прямой метод FieldGet():
 
oColorCondition := bColorCondition{ "Server:FieldGet(#KOL) = 0 .AND. Server:FieldGet(#REZ) = 0", oBrowser:Server,, oBrush }
 
oBrowser:ColorCondition:Add( oColorCondition )
 
Если писать вот так, то ошибок не будет! Не зависимо от числа полей и степени сложности раскраски.
 
 
5. Неполная поддержка AdoRecordSet.
 
В bBrowser заявлена поддержка AdoRecordSet. Чтобы не быть голословным в своём утверждении – приведу маленький кусочек из SDK:
 
METHOD Use(oServer, auField, auOpen, auFormat) CLASS bBrowser
// =========================================================
// Beschreibung: Aktuellen Server entfernen und neuen Server setzen.
//
// Syntax:  bBrowser:Use(oServer, [auField], [auOpen], [acHeader | onaFormat])
//
// Historie:      07.10.1998  JB    Methode implementiert
//          09.12.2005  JB    Vorhandene Spaltenobjekte werden zerstцrt.
//          10.02.2009  JB    Optimierung implementiert, sodass der aktuelle Datensatz
//                                 immer vollstдndig sichtbar ist.
//          21.09.2009  JB    Beim ServerType #DBase wird der Filter vermerkt.
//          17.10.2009  JB    Ermittlung des Status fьr die Infowerte korrigiert.
// =========================================================
 
**********************************
***  the piece was eaten by mice ***
*******
 
IF Empty(SELF:symServerType)
 
IF IsInstanceOf(SELF:oServer, #DBServer) .OR.;
(IsInstanceOf(SELF:oServer, #bCacheServer) .AND.;
IsInstanceOf(SELF:oServer:DataSource, #DBServer))
 
            SELF:symServerType := #Dbase
 
ELSEIF IsInstanceOf(SELF:oServer, #SQLSelect) .OR.;
(IsInstanceOf(SELF:oServer, #bCacheServer) .AND.;
IsInstanceOf(SELF:oServer:DataSource, #SQLSelect))
 
            SELF:symServerType := #SQL
 
ELSEIF IsInstanceOf(SELF:oServer, #ADOServer) .OR.;
(IsInstanceOf(SELF:oServer, #bCacheServer) .AND.;
IsInstanceOf(SELF:oServer:DataSource, #ADOServer))
 
            SELF:symServerType := #SQL
 
ELSEIF IsInstanceOf(SELF:oServer, #ADORecordSet) .OR.;
(IsInstanceOf(SELF:oServer, #bCacheServer) .AND.;
IsInstanceOf(SELF:oServer:DataSource, #ADORecordSet))
 
      SELF:symServerType := #SQL
      ENDIF
ENDIF
 
**********************************
***  the piece was eaten by mice ***
*******
 
Как видим, AdoRecordSet (и не только) поддерживаются в качестве сервера для bBrowser.
Это замечательно! Но, «нерадивые» программисты забыли скорректировать свой код под стандартные методы AdoRecordSet. Я обнаружил это, когда решил применить свою наработку «Библиотека CA-Visual Objects для MySQL». Наверное, разработчикам было просто лень (или неохота) глянуть стандартный список методов AdoRecordSet...
 
Но, чтобы это заработало, мне пришлось «дорисовать» недостающие методы: DbStruct, Deleted, FieldPos, FieldSpec, FieldSym, GoBottom, GoTo, GoTop, LastRec, Notify, RecCount, RecNo, RegisterClient, ResetNotification, Skip, SuspendNotification, UnRegisterClient, Used.
 
Легко видеть, что здесь используются как устаревшие (LastRec), так и некоторые избыточные методы … Почему сделано так – не знаю. Мне было недосуг раздумывать «о высоком». Я сделал, как сделал. :))
 
 
bArrayServer.
 
1. Ошибки при создании индекса (ордера).
 
В основе bArrayServer-а лежит очень старая идея ArrayServer-а, т.е. возможность работы с массивом, как с обычным сервером. Просто и удобно. Но, здесь есть масса «подводных камней». И основная – то, что обычный массив очень не типизированная вещь. Абсолютно нет никаких гарантий, что в заявленном столбце будут однотипные данные. А ещё есть большой искус расширить список поддерживаемых типов. Например, в SDK bArrayServer видно, что авторы добавили типы «U» (неопределённый) и «O» (объект)… Но, если ты чего-то расширяешь, то готовь решение проблем.
 
Для создания ордера применяется метод CreateOrder(). Сортировка данных, как и следовало ожидать, делается с помощью функции ASort(). А в CA-Vo эта функция спотыкается, если предлагается сортировать данные с NIL, объекты или разнородные данные! Ну, и где защита от этого? Ау, разработчики!
 
Рассмотрим этот метод ещё немножко… Мы видим, что метод весьма похож на одноимённый из класса DbServer (и это правильно). Но, при создании ордера – ему не даётся имя!.. Т.е., даётся, но пустое. Зачем так? Давайте пока это спишем на забывчивость разработчика. Можно, конечно имя потом присвоить через метод OrderInfo() (к нему мы ещё вернёмся), но это не выход. Выход – переопределение метода. Поступаю, как обычно:
 
CLASS msArrayServer INHERIT bArrayServer
      // Pencil 03.12.2008
      // Для правки некоторых "приколов" bArrayServer-а
 
METHOD CreateOrder( cOrderName, uExpression, uForCondition, uWhileCondition,;
                  lDescend, cbEval, iInterval ) CLASS msArrayServer
 
// METHOD CreateOrder(uExpression, uForCondition, uWhileCondition,;
//                lDescend, cbEval, iInterval) CLASS bArrayServer
 
      // Pencil 10.12.2008
      // Исправляем ошибки индексации
 
**********************************
***  the piece was eaten by mice ***
*******
 
// Даём имя ордеру
//    SELF:auOrder[SELF:iOrderCount, BASOI_NAME]     := ""
 
      IF !IsNil( cOrderName ) .AND. IsString( cOrderName )
 
            cOrderName := AllTrim( cOrderName )
            cNameOrder := Upper( cOrderName )
      ELSE
            cNameOrder := ""
      ENDIF
      SELF:auOrder[ SELF:iOrderCount, BASOI_NAME ]   := cNameOrder
 
**********************************
***  the piece was eaten by mice ***
*******
 
// FOR-Bedingung ausfьhren
IF .NOT. lForCondition .OR. Eval(uConditionBlock, SELF)
 
      // aktuellen Datensatz einsortieren
      iValueCount += 1
 
// Ловим ошибку заранее
//    auValue[iValueCount, BASOI_KEYLIST_VALUE] := Eval(uExpressionBlock, SELF)
 
      uValue := Eval( uExpressionBlock, SELF )
      IF IsNil( uValue )
 
oError:Description := ;
"В индексе (" + cExpression + ") получется NIL-значение :" + CRLF +;
"нет указанного поля или NIL в данных."
 
            BREAK oError
      ENDIF
 
      IF ValType( uValue ) == "O"
 
oError:Description := ;
      "В индексе (" + cExpression + ") идёт сортировка объектов." + CRLF +;
                  "bArrayServer это не сортирует ..."
 
            BREAK oError
      ENDIF
 
      auValue[ iValueCount, BASOI_KEYLIST_VALUE ] := uValue
 
**********************************
***  the piece was eaten by mice ***
*******
 
Вот, где-то так… А главное – ближе к синтаксису соответствующего метода DbServer-а.
 
 
2. Ошибки при удалении (очистки) ордера.
 
Тут тоже интересно. Информация об ордерах хранится в массиве. При попытке закрытия ордера, номер которого не попадает в диапазон значений в массиве – ничего не делается, но возвращается значение FALSE, что, в общем-то, правильно, но не очень неудобно. Потому, как с этим ордером ничего не происходит, и ничего мы с ним сделать не сможем. Более того, если номер ордера выходит за существующий диапазон – это явная ошибка, ошибка программиста. И я считаю, что в этом случае – надо возвращать TRUE.
 
METHOD ClearOrder( iOrderNo ) CLASS msArrayServer
      // Pencil 13.10.2009
      // Исправляем: если индекса нет, то значит, он очищен!
 
      // angegebene Sortierung lцschen
      IF SELF:iOrderCount = 0
 
//          RETURN FALSE
            RETURN TRUE
 
      ELSEIF IsNumeric( iOrderNo )
 
            IF iOrderNo = 0
                  iOrderNo := SELF:iOrderNo
            ENDIF
 
            IF .NOT. Between( iOrderNo, 1, SELF:iOrderCount )
 
//                RETURN FALSE
                  RETURN TRUE
            ENDIF
      ENDIF
 
**********************************
***  the piece was eaten by mice ***
*******
 
Так мне нравится больше!
 
 
3. Ошибки при копировании в файл.
 
Здесь (в методе CopyToFile) меня не устраивают сразу три вещи:
  • Есть начальная обработка ситуации, когда в качестве параметра передаются несуществующие поля. Но, это не доводится до конца – разработчик явно спешил.
  • Не обрабатывается ситуация, когда в поле хранятся объекты (а bArrayServer это допускает!)
  • При копировании в DBF не обрабатывается ситуация, когда этот файл должен быть в OEM-кодировке. Туда пишется только в ANSI!
Исправляем:
 
**********************************
***  the piece was eaten by mice ***
*******
 
      FOR iPos:=1 UPTO iSize
            IF IsNumeric(auFieldID[iPos]) .AND. auFieldID[iPos]>0 .AND.;
auFieldID[iPos] <= SELF:FCount
 
                  iField := auFieldID[iPos]
            ELSEIF IsString(auFieldID[iPos])
                  iField := SELF:FieldPos(auFieldID[iPos])
            ELSEIF IsSymbol(auFieldID[iPos])
                  iField := SELF:FieldPos(auFieldID[iPos])
            ELSEIF IsInstanceOfUsual(auFieldID[iPos], #DataField)
                  iField := SELF:FieldPos(auFieldID[iPos]:Name)
            ELSE
                  iField := 0
            ENDIF
 
// Pencil 14.02.2009
***
//          IF iField>0
//                oDataField := SELF:aDataFields[iField]
//                oFieldSpec := oDataField:FieldSpec
//                IF Instr(oFieldSpec:ValType, "CDLN") .OR.;
//                      (oFieldSpec:ValType="M" .and. symFormat=#DBF)
//
//                      AAdd(auField, {iField, oFieldSpec:ValType,;
//                           oFieldSpec:Length, oFieldSpec:Decimals})
//                ENDIF
//          ENDIF
***
            IF iField = 0
 
auTemp := { iField, "U", 0, 0 }
AAdd( auField, auTemp )
            ELSE
 
                  oDataField := SELF:aDataFields[ iField ]
                  oFieldSpec := oDataField:FieldSpec
                  IF Instr( oFieldSpec:ValType, "CDLN" ) .OR.;
( oFieldSpec:ValType = "M" .AND. symFormat = #DBF )
 
                        AAdd( auField, { iField, oFieldSpec:ValType,;
oFieldSpec:Length, oFieldSpec:Decimals } )
                  ELSE
 
auTemp := { iField, "U", 0, 0 }
AAdd( auField, auTemp )
                  ENDIF
            ENDIF
 
**********************************
***  the piece was eaten by mice ***
*******
 
      ELSEIF symFormat=#DBF
 
// Pencil 30.01.2009
***
lAnsiDBF := odbsTarget:Info( DBI_ISANSI )
***
 
            odbsTarget:SuspendNotification()
            auValue := ArrayCreate(iFieldCount)
            WHILE .NOT. SELF:EoF .AND. iRecCount>0 .AND.;
(IsNil(uWhileCondition) .OR. Eval(uWhileCondition, SELF))
 
                  IF IsNil(uForCondition) .OR. Eval(uForCondition, SELF)
 
                        FOR iField:=1 UPTO iFieldCount
 
// Pencil 14.02.2009
***
//                           auValue[iField] := SELF:FIELDGET(auField[iField, 1])
IF auField[ iField, 1 ] > 0
 
      uValue := SELF:FIELDGET( auField[ iField, 1 ] )
 
// OEM, DOS
      IF .NOT. lAnsiDBF .AND. auField[ iField, 2 ] == "C" .AND. IsString( uValue )
            auValue[ iField ] := Ansi2Oem( uValue )
      ELSE
            auValue[ iField ] := uValue
      ENDIF
ENDIF
***
                        NEXT
                        lDeleted := SELF:Deleted
 
                        // export current record
                        IF odbsTarget:Append()
 
                             FOR iField := 1 UPTO iFieldCount
 
// Pencil 14.02.2009
***
//    odbsTarget:FIELDPUT(auField[iField, 1], auValue[iField])
IF auField[ iField, 1 ] > 0
      odbsTarget:FIELDPUT( iField, auValue[ iField ] )
ENDIF
***
 
**********************************
***  the piece was eaten by mice ***
*******
 
Я понимаю, что Читатель ждёт от меня полные выкладки (листинг) моих переделок в методах. Это справедливо. Но, поверьте мне – текст получится огромным (он уже и так – немаленький)! А если Вы соображаете в CA-Vo, то Вам с лихвой хватит и тех намёток, что я предоставил. Так что, бог Вам в помощь! У Вас всё получиться!
 
 
4. Недоработка метода FieldPut.
 
Напоминаю, в ArrayServer-ах мы всё время боремся за соответствие данных заявленной структуре. Плюс, очень нежелательны значения NIL (если Вы собираетесь делать ордер).
 
Доделаем метод FieldPut:
 
      oFieldSpec := SELF:FieldSpec( uField )
      IF .NOT. oFieldSpec == NULL_OBJECT
 
            cType := oFieldSpec:ValType
 
// Pencil 19.05.2012
***
// Контроль полученных типов
            IF InList( cType, "C", "M", "D", "L", "N", "O" )
 
// Доводим uValue «до ума»
                  IF uValue == NIL
 
                        IF cType == "C" .OR. cType == "M"
                             uValue := NULL_STRING
                        ELSEIF cType == "D"
                             uValue := NULL_DATE
                        ELSEIF cType == "L"
                             uValue := FALSE
                        ELSEIF cType == "N"
                             uValue := 0
                        ELSEIF cType == "O"
                             uValue := NULL_OBJECT
                        ENDIF
                  ENDIF
 
 
5. Ошибки получения информации об ордере.
 
Это касательно метода OrderInfo(). Здесь у меня всего два замечания:
  • В качестве параметра нельзя передать имя ордера (только номер!)
  • При попытке узнать DBOI_NUMBER – возвращает не номер указанного ордера в списке ордеров, а номер текущего, что полностью расходится с работой аналогичного метода DbServer-а.
Займёмся в очередной раз переопределением метода:
 
METHOD OrderInfo( iInfoType, uReserved, uOrder, uValueNew ) CLASS msArrayServer
      // Pencil 19.05.2012
      // Исправляем работу метода OrderInfo
 
//METHOD OrderInfo(iInfoType, uReserved, iOrderNo, uValueNew) CLASS bArrayServer
//METHOD OrderInfo( kOrderInfoType, oFSIndex, uOrder, uOrdVal ) CLASS DbServer
//FUNCTION DBORDERINFO    (nOrdinal,cBagName, uOrder, xNewVal)
 
      LOCAL wOrder AS DWORD
      LOCAL cOrder AS STRING
 
      IF     IsNil( uOrder )
 
            wOrder := SELF:iOrderNo
 
// Учим работать со строковым именем поля
      ELSEIF IsString( uOrder )
 
            cOrder := AllTrim( uOrder )
            IF .NOT. cOrder == NULL_STRING
 
                  cOrder := Upper( cOrder )
                  wOrder := AScan( SELF:auOrder, { |x| x[ BASOI_NAME ] == cOrder } )
            ELSE
                  wOrder := 0
            ENDIF
 
      ELSEIF IsLong( uOrder ) .AND. Between( uOrder, 1, SELF:iOrderCount )
 
            wOrder := uOrder
      ELSE
            wOrder := 0
      ENDIF
 
// Исправляем ошибку с DBOI_NUMBER (когда почему-то возвращается SELF:iOrderNo,
// т.е. текущий, а не указанный ордер ...)
      IF iInfoType == DBOI_NUMBER
            RETURN wOrder
      ENDIF
 
      RETURN ( SUPER:OrderInfo( iInfoType, uReserved, wOrder, uValueNew ) )
 
Ура! Вы даже себе не представляете, как я устал, пока всё это написал. Знал бы, что потрачу столько времени – наверное, плюнул бы и отказался. Ан нет – дописал. Вот какой я молодец! Надеюсь, Вам это пригодится. Осталось добавить: сам себя не похвалишь – никто не похвалит!
 
 
 
20.05.2012 г.   Карандаш.
 

Theme by Danetsoft and Danang Probo Sayekti inspired by Maksimer