Visibility of work areas, variables, viewing contents of objects

Public support forum for peer to peer support with related to the Visual Objects and Vulcan.NET products
User avatar
wriedmann
Posts: 3748
Joined: Mon Nov 02, 2015 5:07 pm
Location: Italy

Visibility of work areas, variables, viewing contents of objects

Post by wriedmann »

Hi Thomas,
yes, they are the references of your application.
Personally, I would add a call to the debugging functions somewhere in your menu, so you can call then whenever you like - even when the application is running on another machine than yours.
But of course, if you add that library to the references, you have to deliver the relative DLL together with your application.
Wolfgang
Wolfgang Riedmann
Meran, South Tyrol, Italy
wolfgang@riedmann.it
https://www.riedmann.it - https://docs.xsharp.it
ThomasWYale
Posts: 43
Joined: Thu Jun 20, 2019 5:19 pm

Visibility of work areas, variables, viewing contents of objects

Post by ThomasWYale »

Hi, Chris.

I tried it and it works. DbgShowMemvars produces an RTE, but the only other downside is that DbgShowGlobals and I think I may have a solution for that.

I was thinking of building a dialog, using one or more global variables or objects as arguments, and constructing a class which uses Object2Array() along with IVarListGetInfo(), IVarList() and IVarListClass(). This class implemented in the dialog would traverse the array and add treeview items with data derived from these functions. It would check the type with IsString(), IsSymbol(), IsLogic(), et al to display the values, iterate the element if IsArray(), and in cases of IsObject(), an object within an object, call the method for accessing the object element recursively.

When I get it to work, would you like me to share the code with you? I don't know whether it would make emulating CAVO's global treeview for objects in XIDE any easier, but if it does, I'd be glad to help.
User avatar
Chris
Posts: 4854
Joined: Thu Oct 08, 2015 7:48 am
Location: Greece

Visibility of work areas, variables, viewing contents of objects

Post by Chris »

Hi Thomas,

Yes, please send us the code, if it's suitable it can be used in both XIDE and VS!
Chris Pyrgas

XSharp Development Team
chris(at)xsharp.eu
User avatar
robert
Posts: 4478
Joined: Fri Aug 21, 2015 10:57 am
Location: Netherlands

Visibility of work areas, variables, viewing contents of objects

Post by robert »

Thomas,
ThomasWYale wrote:De heer van der Hulst, is de feature toegevoegd? Ik zag dat je de post hebt bekeken.
We are working on this at this moment, as well as an improved "Debugger Expression Evaluator" (both for the VS debugger, the XIDE debugger is another thing).

Robert
XSharp Development Team
The Netherlands
robert@xsharp.eu
User avatar
robert
Posts: 4478
Joined: Fri Aug 21, 2015 10:57 am
Location: Netherlands

Visibility of work areas, variables, viewing contents of objects

Post by robert »

Thomas,
ThomasWYale wrote:Hi, Chris.
I tried it and it works. DbgShowMemvars produces an RTE, but the only other downside is that DbgShowGlobals and I think I may have a solution for that.
I am not sure what you mean with RTE, but if it is an exception, can you please share that with us, so we can take care of it.

Robert
XSharp Development Team
The Netherlands
robert@xsharp.eu
User avatar
Chris
Posts: 4854
Joined: Thu Oct 08, 2015 7:48 am
Location: Greece

Visibility of work areas, variables, viewing contents of objects

Post by Chris »

Robert,

It gives a runtime exception, this is why I did not include this function in my post above

Code: Select all

System.InvalidCastException
Unable to cast object of type 'System.Reflection.RtFieldInfo' to type 'System.Reflection.MethodInfo'.

Callstack : 
XSharp.Debugger.MemVarsWindow.System.Void XSharp.Debugger.MemVarsWindow.LoadValues()() 
XSharp.Debugger.MemVarsWindow.System.Void XSharp.Debugger.MemVarsWindow..ctor()() 
static System.Void XSharp.RT.Debugger.Functions.DbgShowMemvars()() 
Also DbgShowWorkAreas() gives some handled exceptions, looking into it and will open tickets if necessary.
Chris Pyrgas

XSharp Development Team
chris(at)xsharp.eu
User avatar
robert
Posts: 4478
Joined: Fri Aug 21, 2015 10:57 am
Location: Netherlands

Visibility of work areas, variables, viewing contents of objects

Post by robert »

Chris,
I checked in a fix

Robert
XSharp Development Team
The Netherlands
robert@xsharp.eu
User avatar
Chris
Posts: 4854
Joined: Thu Oct 08, 2015 7:48 am
Location: Greece

Visibility of work areas, variables, viewing contents of objects

Post by Chris »

Robert,

Yes, works fine now!
Chris Pyrgas

XSharp Development Team
chris(at)xsharp.eu
ThomasWYale
Posts: 43
Joined: Thu Jun 20, 2019 5:19 pm

Visibility of work areas, variables, viewing contents of objects

Post by ThomasWYale »

Here you are, Chris. I would've finished a lot sooner, but I have a day job, had to contend with walking a few miles to work every day in the wake of a recent snowstorm, among other things.

I tried to emulate as closely as possible to the way VO 2.8 does this. Like the functions in XSharp.RT.Debugger, it has to be either inserted within the code or called from a menu option. As long-time VO 2.8 users know, however, the feature seems to use a kind of hybrid control with the properties of both a listview and treeview. Since I didn't have that, I settled on a treeview instead. And since I can't include the actual form here, you need only construct one yourself with the two PROTECTs as listed in the class declaration for ViewObjects. One or more GLOBALs, or any combination of objects or arrays to be inspected, are included in an array designated as uExtra argument in ViewObjects.

Code: Select all

LOCAL oViewObjects AS ViewObjects
...
oViewObjects:=ViewObjects{,,,{<arrayOrObject1,arrayOrObject2,...}}
oViewObjects:Show(SHOWCENTERED)
Early on I figured that I'd face the problem of a stack overflow error if I constructed a complete tree, particularly if there were circular references, so I put in safeguards prevent that. As in VO, a user double-clicks a node to reveal its content, but the tree adds items only incrementally, revealing only what the user intends to see. That way, the tree indicates the presence of any array elements or object attributes one step in advance, with the [+] designation beside the node as unexpanded.

I tested it as extensively as I could, and hope that you find use with it.

Code: Select all

CLASS ViewObjects INHERIT DATADIALOG 

	PROTECT oDCtv AS TREEVIEW
	PROTECT oCCbtnExit AS PUSHBUTTON

  //{{%UC%}} USER CODE STARTS HERE (do NOT remove this line)

PROTECT aObjectsInTree AS ARRAY
PROTECT nStopAdding AS BYTE

METHOD Init(oWindow,iCtlID,oServer,uExtra) CLASS ViewObjects 
SELF:PreInit(oWindow,iCtlID,oServer,uExtra)
SUPER:Init(oWindow,ResourceID{"ViewObjects",_GetInst()},iCtlID)
oDCtv := TreeView{SELF,ResourceID{VIEWOBJECTS_TV,_GetInst()}}
oDCtv:HyperLabel := HyperLabel{#tv,"View Objects",NULL_STRING,NULL_STRING}
oCCbtnExit := PushButton{SELF,ResourceID{VIEWOBJECTS_BTNEXIT,_GetInst()}}
oCCbtnExit:HyperLabel := HyperLabel{#btnExit,"Exit",NULL_STRING,NULL_STRING}
SELF:Caption := "View Objects"
SELF:HyperLabel := HyperLabel{#ViewObjects,"View Objects",NULL_STRING,NULL_STRING}
IF !IsNil(oServer)
 SELF:Use(oServer)
ENDIF
SELF:PostInit(oWindow,iCtlID,oServer,uExtra)
RETURN SELF

METHOD PostInit(oWindow,iCtlID,oServer,uExtra) CLASS ViewObjects
LOCAL y AS BYTE
SELF:aObjectsInTree:={}
SELF:nStopAdding:=0
FOR y=1 UPTO ALen(uExtra)
 IF IsObject(uExtra[y])
  SELF:doObject(uExtra[y],#ROOT)
 ELSE
  SELF:doArray(uExtra[y],#ROOT)
 ENDIF
NEXT
RETURN NIL

METHOD doArray(aArray,sParentName) CLASS ViewObjects
LOCAL sName AS SYMBOL
LOCAL y AS BYTE
LOCAL oTVI AS TreeViewItem
sName:=String2Symbol(PadL(SELF:oDCtv:ItemCount,3,"0"))
oTVI:=TreeViewItem{sName,"array",aArray}
SELF:oDCtv:AddItem(sParentName,oTVI)
IF SELF:nStopAdding<3
 SELF:nStopAdding:=SELF:nStopAdding+1
 FOR y=1 UPTO ALen(aArray)
  SELF:doAttribute(sName,"["+NTrim(y)+"]",aArray[y],4)
 NEXT
 SELF:nStopAdding:=SELF:nStopAdding-1
ENDIF
RETURN NIL

METHOD doAttribute(sParentName,cName,uValue,dwlInfo)  CLASS ViewObjects
LOCAL sName AS SYMBOL
LOCAL cExp AS STRING
LOCAL y AS BYTE
LOCAL oTVI AS TreeViewItem
sName:=String2Symbol(PadL(SELF:oDCtv:ItemCount,3,"0"))
IF IsNil(uValue)
 IF IsArray(uValue)
  oTVI:=TreeViewItem{sName,cName+" NULL_ARRAY ",uValue}
 ELSEIF IsString(uValue)
  oTVI:=TreeViewItem{sName,cName+" NULL_STRING ",uValue}
 ELSEIF IsObject(uValue)
  oTVI:=TreeViewItem{sName,cName+" NULL_OBJECT ",uValue}
 ELSE
  oTVI:=TreeViewItem{sName,cName+" nil ",uValue}
 ENDIF
 SELF:oDCtv:AddItem(sParentName,oTVI)
ELSEIF IsLogic(uValue)
 oTVI:=TreeViewItem{sName,cName+" logic "+LTOC(uValue),uValue}
 SELF:oDCtv:AddItem(sParentName,oTVI)
ELSEIF IsSymbol(uValue)
 oTVI:=TreeViewItem{sName,cName+" symbol "+Symbol2String(uValue),uValue}
 SELF:oDCtv:AddItem(sParentName,oTVI)
ELSEIF IsFloat(uValue)
 oTVI:=TreeViewItem{sName,cName+" float "+NTrim(uValue),uValue}
 SELF:oDCtv:AddItem(sParentName,oTVI)
ELSEIF IsLong(uValue)
 oTVI:=TreeViewItem{sName,cName+" long "+NTrim(uValue),uValue}
 SELF:oDCtv:AddItem(sParentName,oTVI)
ELSEIF IsNumeric(uValue)
 oTVI:=TreeViewItem{sName,cName+" numeric "+NTrim(uValue),uValue}
 SELF:oDCtv:AddItem(sParentName,oTVI)
ELSEIF IsDate(uValue)
 oTVI:=TreeViewItem{sName,cName+" date "+DToC(uValue),uValue}
 SELF:oDCtv:AddItem(sParentName,oTVI)
ELSEIF IsString(uValue)
 oTVI:=TreeViewItem{sName,cName+" string "+CHR(34)+uValue+CHR(34),uValue}
 SELF:oDCtv:AddItem(sParentName,oTVI)
ELSEIF IsArray(uValue)
 oTVI:=TreeViewItem{sName,cName+" array",uValue}
 SELF:oDCtv:AddItem(sParentName,oTVI)
 IF SELF:nStopAdding<3
  SELF:nStopAdding:=SELF:nStopAdding+1
  FOR y=1 UPTO ALen(uValue)
   SELF:doAttribute(sName,"["+NTrim(y)+"]",uValue[y],4)
  NEXT
  SELF:nStopAdding:=SELF:nStopAdding-1
 ENDIF
ELSEIF IsObject(uValue)
 cExp:=cName+" class "+Symbol2String(ClassName(uValue))
 IF AScan(SELF:aObjectsInTree,uValue)>0
  oTVI:=TreeViewItem{sName,cName+" class "+Symbol2String(ClassName(uValue))+" (circular reference)",uValue}
  SELF:oDCtv:AddItem(sParentName,oTVI)
 ELSE
  SELF:doObject(uValue,sParentName)
 ENDIF
ELSE // IF IsPtr(uValue)
 oTVI:=TreeViewItem{sName,cName+" Other, assuming Pointer",uValue}
 SELF:oDCtv:AddItem(sParentName,oTVI)
ENDIF
RETURN NIL

METHOD doObject(oObject,sParentName) CLASS ViewObjects
LOCAL sName,sAttrib AS SYMBOL
LOCAL y AS BYTE
LOCAL cExp AS STRING
LOCAL oTVI AS TreeViewItem
LOCAL aVarList:=IvarList(oObject)
cExp:="CLASS "+Symbol2String(ClassName(oObject))
sName:=String2Symbol(PadL(SELF:oDCtv:ItemCount,3,"0"))
AAdd(SELF:aObjectsInTree,oObject)
oTVI:=TreeViewItem{sName,cExp,oObject}
SELF:oDCtv:AddItem(sParentName,oTVI)
FOR y=1 UPTO ALen(aVarList)
 sAttrib:=aVarList[y]
 SELF:doAttribute(sName,Symbol2String(sAttrib),IVarGet(oObject,sAttrib),IVarGetInfo(oObject,sAttrib))
NEXT
RETURN NIL

METHOD TreeViewMouseButtonDoubleClick(oTreeViewMouseEvent) CLASS ViewObjects
LOCAL sName,sAttrib AS SYMBOL
LOCAL y AS BYTE
LOCAL uValue AS USUAL
LOCAL oTVI,oTVI2,oTVI3 AS TreeViewItem
LOCAL aVarList AS ARRAY
LOCAL oControl AS Control
oControl:=IF(oTreeViewMouseEvent==NULL_OBJECT,NULL_OBJECT,oTreeViewMouseEvent:Control)
SUPER:TreeViewMouseButtonDoubleClick(oTreeViewMouseEvent)
oTVI:=SELF:oDCtv:GetSelectedItem()
sName:=oTVI:NameSym
uValue:=oTVI:Value
IF(!IsArray(uValue).AND.!IsObject(uValue)).OR.IsObject(uValue).AND.AScan(SELF:aObjectsInTree,uValue)>0
 RETURN NIL
ENDIF
oTVI2:=SELF:oDCtv:GetFirstChildItem(oTVI)
IF!oTVI2==NULL_OBJECT
 WHILE!oTVI2==NULL_OBJECT
  IF IsArray(oTVI2:VALUE).OR.IsObject(oTVI2:VALUE).AND.AScan(SELF:aObjectsInTree,oTVI2:VALUE)>0
   oTVI3:=SELF:oDCtv:GetFirstChildItem(oTVI2)
   WHILE!oTVI3==NULL_OBJECT
    IF IsArray(oTVI3:VALUE).AND.SELF:oDCtv:GetFirstChildItem(oTVI3:NameSym)==NULL_OBJECT
     FOR y=1 UPTO ALen(oTVI3:VALUE) 
      SELF:doAttribute(oTVI3:NameSym,"["+NTrim(y)+"]",oTVI3:VALUE[y],4)
     NEXT
    ELSEIF IsObject(oTVI3:VALUE)
     aVarList:=IvarList(oTVI3:VALUE)
     FOR y=1 UPTO ALen(aVarList)
      sAttrib:=aVarList[y]
      SELF:doAttribute(oTVI3:NameSym,Symbol2String(sAttrib),IVarGet(oTVI3:Value,sAttrib),IVarGetInfo(oTVI3:Value,sAttrib))
     NEXT
    ENDIF
    oTVI3:=SELF:oDCtv:GetNextSiblingItem(oTVI3)
   ENDDO
  ENDIF
  oTVI2:=SELF:oDCtv:GetNextSiblingItem(oTVI2)
 ENDDO
ELSEIF IsArray(uValue).AND.ALen(uValue)>0
 IF SELF:nStopAdding<3
  SELF:nStopAdding:=SELF:nStopAdding+1
  FOR y=1 UPTO ALen(uValue)
   SELF:doAttribute(sName,"["+NTrim(y)+"]",uValue[y],4)
  NEXT
  SELF:nStopAdding:=SELF:nStopAdding-1
 ENDIF
ENDIF
RETURN NIL

METHOD btnExit( ) CLASS ViewObjects 
SELF:EndWindow()
RETURN NIL
ThomasWYale
Posts: 43
Joined: Thu Jun 20, 2019 5:19 pm

Visibility of work areas, variables, viewing contents of objects

Post by ThomasWYale »

Here you are, Chris. I would've finished a lot sooner, but I have a day job, had to contend with walking a few miles to work every day in the wake of a recent snowstorm, among other things.

I tried to emulate as closely as possible to the way VO 2.8 does this. Like the functions in XSharp.RT.Debugger, it has to be either inserted within the code or called from a menu option. As long-time VO 2.8 users know, however, the feature seems to use a kind of hybrid control with the properties of both a listview and treeview. Since I didn't have that, I settled on a treeview instead. And since I can't include the actual form here, you need only construct one yourself with the two PROTECTs as listed in the class declaration for ViewObjects. One or more GLOBALs, or any combination of objects or arrays to be inspected, are included in an array designated as uExtra argument in ViewObjects.

Code: Select all

LOCAL oViewObjects AS ViewObjects
...
oViewObjects:=ViewObjects{,,,{<arrayOrObject1,arrayOrObject2,...}}
oViewObjects:Show(SHOWCENTERED)
Early on I figured that I'd face the problem of a stack overflow error if I constructed a complete tree, particularly if there were circular references, so I put in safeguards prevent that. As in VO, a user double-clicks a node to reveal its content, but the tree adds items only incrementally, revealing only what the user intends to see. That way, the tree indicates the presence of any array elements or object attributes one step in advance, with the [+] designation beside the node as unexpanded.

I tested it as extensively as I could, and hope that you find use with it.

Code: Select all

CLASS ViewObjects INHERIT DATADIALOG 

	PROTECT oDCtv AS TREEVIEW
	PROTECT oCCbtnExit AS PUSHBUTTON

  //{{%UC%}} USER CODE STARTS HERE (do NOT remove this line)

PROTECT aObjectsInTree AS ARRAY
PROTECT nStopAdding AS BYTE

METHOD Init(oWindow,iCtlID,oServer,uExtra) CLASS ViewObjects 

SELF:PreInit(oWindow,iCtlID,oServer,uExtra)

SUPER:Init(oWindow,ResourceID{"ViewObjects",_GetInst()},iCtlID)

oDCtv := TreeView{SELF,ResourceID{VIEWOBJECTS_TV,_GetInst()}}
oDCtv:HyperLabel := HyperLabel{#tv,"View Objects",NULL_STRING,NULL_STRING}

oCCbtnExit := PushButton{SELF,ResourceID{VIEWOBJECTS_BTNEXIT,_GetInst()}}
oCCbtnExit:HyperLabel := HyperLabel{#btnExit,"Exit",NULL_STRING,NULL_STRING}

SELF:Caption := "View Objects"
SELF:HyperLabel := HyperLabel{#ViewObjects,"View Objects",NULL_STRING,NULL_STRING}

IF !IsNil(oServer)
	SELF:Use(oServer)
ENDIF

SELF:PostInit(oWindow,iCtlID,oServer,uExtra)

RETURN SELF

METHOD PostInit(oWindow,iCtlID,oServer,uExtra) CLASS ViewObjects
LOCAL y AS BYTE
SELF:aObjectsInTree:={}
SELF:nStopAdding:=0
FOR y=1 UPTO ALen(uExtra)
 IF IsObject(uExtra[y])
  SELF:doObject(uExtra[y],#ROOT)
 ELSE
  SELF:doArray(uExtra[y],#ROOT)
 ENDIF
NEXT
RETURN NIL

METHOD doArray(aArray,sParentName) CLASS ViewObjects
LOCAL sName AS SYMBOL
LOCAL y AS BYTE
LOCAL oTVI AS TreeViewItem
sName:=String2Symbol(PadL(SELF:oDCtv:ItemCount,6,"0"))
oTVI:=TreeViewItem{sName,"array",aArray}
SELF:oDCtv:AddItem(sParentName,oTVI)
FOR y=1 UPTO ALen(aArray)
 SELF:doAttribute(sName,"["+NTrim(y)+"]",aArray[y],4)
NEXT
RETURN NIL

METHOD doAttribute(sParentName,cName,uValue,dwlInfo)  CLASS ViewObjects
LOCAL sName AS SYMBOL
LOCAL cExp AS STRING
LOCAL y AS BYTE
LOCAL oTVI AS TreeViewItem
sName:=String2Symbol(PadL(SELF:oDCtv:ItemCount,6,"0"))
IF IsNil(uValue)
 IF IsArray(uValue)
  oTVI:=TreeViewItem{sName,cName+" NULL_ARRAY ",uValue}
 ELSEIF IsString(uValue)
  oTVI:=TreeViewItem{sName,cName+" NULL_STRING ",uValue}
 ELSEIF IsObject(uValue)
  oTVI:=TreeViewItem{sName,cName+" NULL_OBJECT ",uValue}
 ELSE
  oTVI:=TreeViewItem{sName,cName+" nil ",uValue}
 ENDIF
 SELF:oDCtv:AddItem(sParentName,oTVI)
ELSEIF IsLogic(uValue)
 oTVI:=TreeViewItem{sName,cName+" logic "+LTOC(uValue),uValue}
 SELF:oDCtv:AddItem(sParentName,oTVI)
ELSEIF IsSymbol(uValue)
 oTVI:=TreeViewItem{sName,cName+" symbol "+Symbol2String(uValue),uValue}
 SELF:oDCtv:AddItem(sParentName,oTVI)
ELSEIF IsFloat(uValue)
 oTVI:=TreeViewItem{sName,cName+" float "+NTrim(uValue),uValue}
 SELF:oDCtv:AddItem(sParentName,oTVI)
ELSEIF IsLong(uValue)
 oTVI:=TreeViewItem{sName,cName+" long "+NTrim(uValue),uValue}
 SELF:oDCtv:AddItem(sParentName,oTVI)
ELSEIF IsNumeric(uValue)
 oTVI:=TreeViewItem{sName,cName+" numeric "+NTrim(uValue),uValue}
 SELF:oDCtv:AddItem(sParentName,oTVI)
ELSEIF IsDate(uValue)
 oTVI:=TreeViewItem{sName,cName+" date "+DToC(uValue),uValue}
 SELF:oDCtv:AddItem(sParentName,oTVI)
ELSEIF IsString(uValue)
 oTVI:=TreeViewItem{sName,cName+" string "+CHR(34)+uValue+CHR(34),uValue}
 SELF:oDCtv:AddItem(sParentName,oTVI)
ELSEIF IsArray(uValue)
 oTVI:=TreeViewItem{sName,cName+" array",uValue}
 SELF:oDCtv:AddItem(sParentName,oTVI)
 IF SELF:nStopAdding<2
  SELF:nStopAdding:=SELF:nStopAdding+1
  FOR y=1 UPTO ALen(uValue)
   SELF:doAttribute(sName,"["+NTrim(y)+"]",uValue[y],4)
  NEXT
  SELF:nStopAdding:=SELF:nStopAdding-1
 ENDIF
ELSEIF IsObject(uValue)
 cExp:=cName+" class "+Symbol2String(ClassName(uValue))
 IF AScan(SELF:aObjectsInTree,uValue)>0
  oTVI:=TreeViewItem{sName,cName+" class "+Symbol2String(ClassName(uValue))+" (circular reference)",uValue}
  SELF:oDCtv:AddItem(sParentName,oTVI)
 ELSEIF SELF:nStopAdding<2
  SELF:nStopAdding:=SELF:nStopAdding+1
  SELF:doObject(uValue,sParentName)
  SELF:nStopAdding:=SELF:nStopAdding-1
 ENDIF
ELSE // IF IsPtr(uValue)
 oTVI:=TreeViewItem{sName,cName+" Other, assuming Pointer",uValue}
 SELF:oDCtv:AddItem(sParentName,oTVI)
ENDIF
RETURN NIL

METHOD doObject(oObject,sParentName) CLASS ViewObjects
LOCAL sName,sAttrib AS SYMBOL
LOCAL y AS BYTE
LOCAL cExp AS STRING
LOCAL oTVI AS TreeViewItem
LOCAL aVarList:=IvarList(oObject)
cExp:="CLASS "+Symbol2String(ClassName(oObject))
sName:=String2Symbol(PadL(SELF:oDCtv:ItemCount,6,"0"))
AAdd(SELF:aObjectsInTree,oObject)
oTVI:=TreeViewItem{sName,cExp,oObject}
SELF:oDCtv:AddItem(sParentName,oTVI)
FOR y=1 UPTO ALen(aVarList)
 sAttrib:=aVarList[y]
 SELF:doAttribute(sName,Symbol2String(sAttrib),IVarGet(oObject,sAttrib),IVarGetInfo(oObject,sAttrib))
NEXT
RETURN NIL

METHOD TreeViewMouseButtonDoubleClick(oTreeViewMouseEvent) CLASS ViewObjects
LOCAL sName,sAttrib AS SYMBOL
LOCAL y AS BYTE
LOCAL uValue AS USUAL
LOCAL oTVI,oTVI2,oTVI3 AS TreeViewItem
LOCAL aVarList AS ARRAY
LOCAL oControl AS Control
oControl:=IF(oTreeViewMouseEvent==NULL_OBJECT,NULL_OBJECT,oTreeViewMouseEvent:Control)
SUPER:TreeViewMouseButtonDoubleClick(oTreeViewMouseEvent)
oTVI:=SELF:oDCtv:GetSelectedItem()
sName:=oTVI:NameSym
uValue:=oTVI:Value
IF(!IsArray(uValue).AND.!IsObject(uValue)).OR.IsObject(uValue).AND.AScan(SELF:aObjectsInTree,uValue)>0
 RETURN NIL
ENDIF
oTVI2:=SELF:oDCtv:GetFirstChildItem(oTVI)
WHILE!oTVI2==NULL_OBJECT
 IF IsArray(oTVI2:Value).OR.IsObject(oTVI2:Value).AND.AScan(SELF:aObjectsInTree,oTVI2:Value)>0
  oTVI3:=SELF:oDCtv:GetFirstChildItem(oTVI2)
  WHILE!oTVI3==NULL_OBJECT
   IF IsArray(oTVI3:VALUE).AND.SELF:oDCtv:GetFirstChildItem(oTVI3:NameSym)==NULL_OBJECT
    FOR y=1 UPTO ALen(oTVI3:VALUE) 
     SELF:doAttribute(oTVI3:NameSym,"["+NTrim(y)+"]",oTVI3:VALUE[y],4)
    NEXT
   ELSEIF IsObject(oTVI3:Value)
    aVarList:=IvarList(oTVI3:VALUE)
    FOR y=1 UPTO ALen(aVarList)
     sAttrib:=aVarList[y]
     SELF:doAttribute(oTVI3:NameSym,Symbol2String(sAttrib),IVarGet(oTVI3:Value,sAttrib),IVarGetInfo(oTVI3:Value,sAttrib))
    NEXT
   ENDIF
   oTVI3:=SELF:oDCtv:GetNextSiblingItem(oTVI3)
  ENDDO
 ENDIF
 oTVI2:=SELF:oDCtv:GetNextSiblingItem(oTVI2)
ENDDO
RETURN NIL

METHOD btnExit( ) CLASS ViewObjects 
SELF:EndWindow()
RETURN NIL
Post Reply