I long thought where to place the material: in the blog or in the section of articles. Usually, I do small notes, arguing: «well, now it is actual, and through time – it is necessary for nobody». And only more serious things – I make out in the form of articles.
And here has reflected … After all, conversation will go about mistakes and inconveniences, and they – through any time – will be eliminated by the developer. I will tell in confidence that after each exit of updating for bBrowser I carefully look through changes, in search of the corrected. To remove, become already unnecessary, my additions and corrections.
But, opening this subject, I show my method of work, the approach to the solution of problems, the views. I.e., I give the tool to the reader. And it – is much more serious, therefore – hold article!
Object of research – bBrowser v.3.0.9.109.
It is my first translated article in English. My English is very bad. Therefore, I have simplified my text. Here you will not find beautiful English speech. But, I hope that despite machine translation – to you everything will be clear.
As always, I will say only that I know and I will do that I am able! I would like, that you have attentively read my article, have estimated and have made critical comments. Let's begin! :))
As the tool, библиотеека bBrowser has appeared long ago, gradually forcing out competitors and homemade products. But, I know some people who with pleasure continue to use and develop own development which do not concede to the leader. But, it I have digressed.
I come back. :)). Therefore in bBrowser library there are the mistakes which have arisen because of high speed of development, and because of simple forgetfulness of programmers.
High speed of development usually leads to loss of attention and some worst study of a task. Attention that leads to that some important things don't become is lost or not best method of the solution of the task is selected. Here the safe assistant – the development plan (project) and accuracy of its execution. Despite of pressure from the customer. Since the customer often can't competently decide that is now important, and that - No.
Germans work rather scrupulous therefore gross errors normally don't allow. They always have a plan and they "are morally steady" in respect of a deviation from diagrams. The main source of errors - underestimation of behavior of users. I.e., in most cases – normal simplification because of which reliability of library suffers.
From bBrowser library fully I use only three classes:
I use the class bIcon very little. Therefore, I will tell nothing about bIcon. But, notes are. My article will be devoted to finishings for the classes "bBrowser" and "bArrayServer".
Problems in the class bBrowser:
Problems in the class bArrayServer:
It not all. But, for while will suffice. :))
bBrowser.
On this matter I in details told in the material under the name: «Visual Styles: CA-Visual Objects, bBrowser и все-все-все…». My decision is to use of the improved function:
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
// At first we work with means of bBrowser
bvsEnableVisualStyles( lEnable )
// It isn't necessary, since already made by means of bvsEnableVisualStyles
// EnableAppVisualTheme( lEnable )
// Compulsorily we do as in CA-Vo (since in bBrowser it - in a different way) :))
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( ) ) // For CA-Vo 28
RETURN ( IsThemeEnabled( ) ) // For CA-Vo 27
Unfortunately, for my English-speaking friends, I for the present don't know when I can make transfer of this and other articles. I will try to make it as soon as possible.
Probably, many when editing data in the browser used enhanced features of editing. The class bBrowser allows to do it. For example: oColumn:ViewValueAs := #ComboBox
I too with pleasure use ComboBox. There are also other large controls which aren't located in the sizes of a cell, and reveal in case of the editing beginning. And here the main shortcoming – impossibility to leave from standard height ( height=80 )…
(Attention!!! This situation is watched only when you have no Manifest module. If to add a line: «RESOURCE CREATEPROCESS_MANIFEST_RESOURCE_ID RC_RT_MANIFEST %appwizdir%\cctl6.man» – this problem won't be shown.)
The best method of the decision – to apply method redefinition. So I created the class successor:
CLASS msBrowser INHERIT bBrowser
// Pencil 07.12.2007
Within this class we can add missing variables or directly change the sizes of created controls. I foresee that developers of bBrowser through any time will add missing parameters. Therefore, I didn't begin to risk. I defined the necessary sizes in the EditCreate method:
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
// Changed sorting
// iStyle := WS_CHILD+WS_TABSTOP+CBS_AUTOHSCROLL
iStyle := WS_CHILD+WS_TABSTOP+CBS_SORT+CBS_AUTOHSCROLL
ENDIF
IF symClass = #ComboBox
// Pencil 22.06.2010
// Extended the dropdown list
// 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 ***
*******
Let's continue talk about ComboBox uses as an editing element. Probably, some colleagues noted that if it will reveal up and will superimpose itself titles of columns, that, after "mouse" click (a choice of suitable point) the list will be closed. But, on the screen there will be a defect in the form of a rectangular spot (on title of columns and above if the list revealed above columns). And it will remain, while you won't make compulsory updating of data of a window, or you will need to click once again on a cell.
Why?
In CA-Visual Objects (and in SP3 version 2.8) in general there is no processing of an event of a choice by "mouse" of an element of the dropping-out list. And developers of bBrowser didn't begin to correct it in the library. It leads to that after click by "mouse" work proceeds while you don't end it, having created an event of the completion of editing.
Let's correct:
METHOD EventHandler( oEvent ) CLASS msBrowser
// Pencil 13.02.2012
// The click of "mouse" according to the ComboBox list – means a choice
// Earlier, the choice was fixed only by ENTER key press
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 )
// We check object: it is ComboBox?
IF .NOT. oChild == NULL_OBJECT .AND.;
IsInstanceOf( oChild, #ComboBox )
// CBN_SELENDOK - Specifies that the choice is made in the opened list
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 we edit
IF SELF:InEdit()
IF .NOT. SELF:EditClose() // We finish
SELF:EditCancel() // We interrupt...
ENDIF
ENDIF
RETURN SELF:oCommandOwner:EventReturnValue
ENDIF
ENDIF
ENDIF
ENDIF
RETURN ( SUPER:EventHandler( oEvent ) )
What do I understand as it? These are problems in case of a coloring of lines and columns in the browser. Normally the coloring becomes by means of ColorCondition (lower an example from bBrowser description):
// define color condition and add it to the browser
oColorCondition := bColorCondition{"Server:BIRTHDAY<CToD('01/01/60')",;
odbsCUSTOMER,;
Color{COLORRED}}
oBrowser:ColorCondition:Add(oColorCondition)
To us say that it to make very simply: Server:BIRTHDAY. Even if this field isn't described.
Rather simply. It works… But, if there are a lot of fields (more than three) if there is a lot of different difficult coloring, in the program starts to be buggy. Thus errors unclear. And directly to define a problem - it is difficult.
So in what a problem? And a problem that string expression reveals by means of macro substitution: SELF:uCondition := &("{|Server, Column, Row| "+uCondition+"}")
And in Ca-Vo - there are restrictions. Whose here problems (Ca-Vo or bBrowser) – me doesn't interest. But, I would recommend to developers: do the correct examples not to accustom programmers to bad style to programming. Fields in servers should be described in advance or to apply the FieldGet method:
oColorCondition := bColorCondition{ "Server:FieldGet(#KOL) = 0 .AND. Server:FieldGet(#REZ) = 0", oBrowser:Server,, oBrush }
oBrowser:ColorCondition:Add( oColorCondition )
If to write here so, errors won't be! It is not dependent on number of fields and a level of complexity of a coloring.
In bBrowser AdoRecordSet support is declared. Not to be unfounded in the statement – I will give a small slice from 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 ***
*******
As we see, AdoRecordSet (and not only) are supported as the server for bBrowser. That's wonderful! But, programmers forgot to correct the code that it used standard methods of the class AdoRecordSet. I found it when decided to apply the library «Библиотека CA-Visual Objects для MySQL». It is difficult to understand, why developers didn't familiarize with the list of the standard AdoRecordSet methods...
But, that the mechanism earned, I had to write missing methods: DbStruct, Deleted, FieldPos, FieldSpec, FieldSym, GoBottom, GoTo, GoTop, LastRec, Notify, RecCount, RecNo, RegisterClient, ResetNotification, Skip, SuspendNotification, UnRegisterClient, Used.
bArrayServer.
At the heart of bArrayServer - very old idea of the class ArrayServer, i.e. operation possibility with an array, as with the normal server. Simply and conveniently. But, there is a mass of "reefs". It because a normal array very not typified thing. Absolutely there are no warranties that in the necessary column there will be data of the declared type. Besides, programmers have a big temptation to expand the list of supported types. For example, in SDK bArrayServer it is visible that authors added the types "U" (indefinite) and «O» (object)… But, if you something expand, prepare the solution of problems.
The CreateOrder method is applied to creation of the order. Sorting of data, as one would expect, becomes by means of the ASort function. But, in CA-Vo this function badly works, if it is offered to sort data with NIL, objects or heterogeneous data! Well, and where protection against it? Hey, developers!
Let's consider this method still a little bit… We see that it is similar on with the same name of the class DbServer (it conveniently). But, in case of warrant creation – it is created by the unnamed!.. I.e., it is given, but empty. Why so to do?
Certainly, the order can give a name by means of the OrderInfo method (to it we still will return). But, it is inconvenient. The best method – to redefine a method:
CLASS msArrayServer INHERIT bArrayServer
// Pencil 03.12.2008
METHOD CreateOrder( cOrderName, uExpression, uForCondition, uWhileCondition,;
lDescend, cbEval, iInterval ) CLASS msArrayServer
// Pencil 10.12.2008
// METHOD CreateOrder(uExpression, uForCondition, uWhileCondition,;
// lDescend, cbEval, iInterval) CLASS bArrayServer
**********************************
*** the piece was eaten by mice ***
*******
// Name
// 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
// aktuellen Datensatz einsortieren
iValueCount += 1
// We catch an error in advance
// auValue[iValueCount, BASOI_KEYLIST_VALUE] := Eval(uExpressionBlock, SELF)
uValue := Eval( uExpressionBlock, SELF )
IF IsNil( uValue )
oError:Description := ;
"In the order (" + cExpression + ") will turn out NIL value :" + CRLF +;
" there is no specified field or NIL in data."
BREAK oError
ENDIF
IF ValType( uValue ) == "O"
oError:Description := ;
" In the order (" + cExpression + ") there is a sorting of objects." + CRLF +;
" bArrayServer doesn't sort it..."
BREAK oError
ENDIF
auValue[ iValueCount, BASOI_KEYLIST_VALUE ] := uValue
**********************************
*** the piece was eaten by mice ***
*******
Here, somewhere so … And the main thing – is closer to syntax of the appropriate DbServer method.
Here too it is interesting. Information on orders is stored in an array. In attempt of closing of the order which number doesn't get to value range in an array – becomes nothing, but FALSE value returns. It, in general, correctly, but isn't so inconvenient. Because to this order occurs nothing, and we with it can't make anything. Moreover, if number of the order quits for the existing range is an appreciable error, an error of the programmer. My judgement that in this case – it is necessary to return TRUE:
METHOD ClearOrder( iOrderNo ) CLASS msArrayServer
// Pencil 13.10.2009
// We correct: if the order isn't present, then, it is cleared!
// 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
So it is pleasant to me more!
Here (in the CopyToFile method) I don't accept at once three things:
· At the beginning, situation processing when as parameter nonexistent fields are transferred is made. But, it isn't finished – the developer obviously hurried.
· The situation isn't processed, when objects are in the field stored (and it allows bArrayServer!)
· When copying data in DBF the situation when this file should be in the OEM coding isn't processed. There it is written only to ANSI!
We correct:
**********************************
*** 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 ***
*******
I understand that the Reader waits from me complete listing of my alterations in methods. It is fair. But, believe me – the text will turn out huge (it already and so – rather big)! And if you understand CA-Vo, also those bastings with interest will suffice you that I provided. So, god to you in the help! At you all to turn out!
I remind, in ArrayServer all of us time fight for compliance of data to the declared structure. Plus, NIL values (if you are going to do the order) are very undesirable.
Let's complete the FieldPut method:
oFieldSpec := SELF:FieldSpec( uField )
IF .NOT. oFieldSpec == NULL_OBJECT
cType := oFieldSpec:ValType
// Pencil 19.05.2012
***
// Add control of admissible types of data
IF InList( cType, "C", "M", "D", "L", "N", "O" )
// Let's add check on NIL
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
It concerns the OrderInfo method. Here I have only two remarks:
Let's redefine one more method:
METHOD OrderInfo( iInfoType, uReserved, uOrder, uValueNew ) CLASS msArrayServer
// Pencil 19.05.2012
// We correct OrderInfo method work
//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
// We learn to work with a string name of a field
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
// We correct a mistake with DBOI_NUMBER
IF iInfoType == DBOI_NUMBER
RETURN wOrder
ENDIF
RETURN ( SUPER:OrderInfo( iInfoType, uReserved, wOrder, uValueNew ) )
Hurrah! You at all don't represent to yourselves, as I was tired, while all this wrote. If I knew in advance that I will spend so much time – probably, would spit and refused. But, after all – finished work. I hope, it is useful to you.
20.05.2012 г. Pencil (Карандаш).
http://mustang-soft.com/en/node/70
Theme by Danetsoft and Danang Probo Sayekti inspired by Maksimer
Comments
It's really a great and
Thanks!
Thanks!
Unfortunately, your site has no relation to this article (bBrowser and Ca-Vo). Advertizing of outside sites - here is forbidden. Therefore, the manager deleted your link.
I think this is one of the
D. Good job, cheers