ArcObjects - enumerating feature classes and datasets within a geodatabase - vba

I'm trying to enumerate the contents (feature classes and feature datasets, not interested in tables, etc) of a file geodatabase using vba/arcobjects.
I have the file GDB set as an IGxDatabase object, but can't find a way of getting further in. I've had a look at the geodatabase object model and tried using IFeatureClass and IFeatureDataset but neither seem to return useful results.
Thanks in advance for any assistance

It is much faster to enumerate the names contained in a geodatabase instead of the things that the names can open. The tricky part is looping through names in a featuredataset. While IFeatureWorkspace.Open can be used to open a featureclass without first opening the featuredataset that contains it, getting at featureclassnames within a featuredataset requires opening the featuredataset.
If you know for sure you'll need to open each featureclass, then I suppose it wouldn't hurt to use IWorkspace.Datasets, IEnumDataset, and IDataset instead of IWorkspaceDatasetNames, IEnumDatasetname and IDatasetname.
Option Explicit
Sub TestGetContents()
Dim pGxApp As IGxApplication
Set pGxApp = Application
If Not TypeOf pGxApp.SelectedObject Is IGxDatabase Then
Debug.Print "select a geodb first"
Exit Sub
End If
Dim c As Collection
Set c = GetContents(pGxApp.SelectedObject)
Dim l As Long
For l = 1 To c.Count
Dim pName As IName
Set pName = c.Item(l)
If TypeOf pName Is IFeatureClassName Then
Dim pFC As IFeatureClass
Set pFC = pName.Open
Debug.Print pFC.AliasName, pFC.FeatureCount(Nothing)
ElseIf TypeOf pName Is IFeatureDatasetName Then
Dim pDSName As IDatasetName
Set pDSName = pName
Debug.Print pDSName.name, "(featuredataset)"
End If
Next l
End Sub
Function GetContents(pGxDB As IGxDatabase) As Collection
Dim c As New Collection
Dim pEnumDSName As IEnumDatasetName
Set pEnumDSName = pGxDB.Workspace.DatasetNames(esriDTAny)
Dim pDSName As IDatasetName
Set pDSName = pEnumDSName.Next
Do Until pDSName Is Nothing
If TypeOf pDSName Is IFeatureClassName Then
c.Add pDSName
ElseIf TypeOf pDSName Is IFeatureDatasetName Then
c.Add pDSName
AddSubNames pDSName, c
End If
Set pDSName = pEnumDSName.Next
Loop
Set GetContents = c
End Function
Sub AddSubNames(pDSName1 As IDatasetName, c As Collection)
Dim pEnumDSName As IEnumDatasetName
Set pEnumDSName = pDSName1.SubsetNames
pEnumDSName.Reset
Dim pDSName2 As IDatasetName
Set pDSName2 = pEnumDSName.Next
Do Until pDSName2 Is Nothing
If TypeOf pDSName2 Is IFeatureClassName Then
c.Add pDSName2
End If
Set pDSName2 = pEnumDSName.Next
Loop
End Sub

you can use the ListFeatureClasses Method on the Geoprocessor
(the following shows how this can be done in C#)
IGeoProcessor gp = new GeoProcessorClass();
gp.SetEnvironmentValue("workspace", #"C:\temp");
IGpEnumList gpEnumList = gp.ListFeatureClasses("*", "Polygon", "");
string fc = gpEnumList.Next();
while (fc != "")
{
//Do whatever
}

Related

CATVBA - CATIA recursive loop throught Product Tree

Sorry but I'm a total newbie in CATScript.
But I'm looking for a solution that will provide me to check every node in my Product structure recursively.
I try to adopt the Fibonacci procedure:
Function Fib(n As Long) As Long
Dim first As Long
Dim second As Long
Dim sum As Long
Dim i As Long
first = 0
second = 1
sum = 0
If n = 0 Then
Fib = first
ElseIf n = 1 Then
Fib = second
Else
For i = 2 To n
sum = first + second
first = second
second = sum
Next i
Fib = sum
End If
End Function
with this:
Private Sub TestMain
Dim searchName As String
searchName = "SearchName"
' Start with the selected object
Dim doc As Document
Set doc = CATIA.ActiveDocument
Dim prod As Product
Set prod = doc.Product
Dim foundItem As Object
foundItem = TestMainChildren(doc.Selection.Item(1).Value, searchName)
MsgBox "Found: " & foundItem.Name
End Sub
Private Function TestMainChildren(ByRef catiaObject As Object, ByVal searchName As String) As Object
Dim item As Object
For Each item In catiaObject.Items
If item.Name = "SearchName" then
Set TestMainChildren = item
Exit For
End if
Dim catiaType As String
catiaType = TypeName(item)
If catiaType = "Product" Then
TestMainChildren item, searchName
End If
Next
End Sub
but I have no idea how to do this. Can anybody help here?
It depends on what you want, but it is often very useless to check all the instances whith a recursive loop.
what is your end goal?
i suggest you to check every instance opened :
Sub main()
Dim d As Document
For Each d In CATIA.Documents
Dim p As Product
Set p = d.Product
MsgBox (p.Name)
Next
End Sub
If you insist and really want a recursive loop :
Sub main()
Dim d As Document
Set d = CATIA.ActiveDocument
Dim p As Product
Set p = d.Product
Call RecursiveAllProducts(p) 'here your recursive starts
End Sub
Sub RecursiveAllProducts(p As Product) 'your recursive
MsgBox (p.PartNumber)
If p.Products.Count > 0 Then
For i = 1 To p.Products.Count
Dim p_ As Product
Set p_ = p.Products.Item(i)
Call RecursiveAllProducts(p_) 'you call your recursive again
Next i
End If
End Sub

Programatically sort pages in a Visio Document using VBA

Does anyone know a method to sort Visio pages alphabetically using VBA?
I looked to see if a method such as vzdVisioDocument.Pages.Sort exists, but found nothing in documentation or through internet searches.
Do I need to write my own sorting function using the Application.ActiveDocument.Pages.ItemU("Page Name").Index property? That seems to be the method suggested by recording a macro of the action.
So that wasn't as painful as expected. With vzdVisioDocument as an already defined Visio.Document:
' Make a collection of titles to iterate through
Dim colPageTitles As Collection
Set colPageTitles = New Collection
Dim intPageCounter As Integer
For intPageCounter = 1 To vzdVisioDocument.Pages.Count
colPageTitles.Add vzdVisioDocument.Pages.Item(intPageCounter).Name
Next intPageCounter
' For each title in the collection, iterate through pages and find the appropriate new index
Dim intPageIndex As Integer
Dim varPageTitle As Variant
For Each varPageTitle In colPageTitles
For intPageIndex = 1 To vzdVisioDocument.Pages.Count
' Check to see if the title comes before the index's current page title
If StrComp(varPageTitle, vzdVisioDocument.Pages.Item(intPageIndex).Name) < 0 Then
' If so, set the new page index
vzdVisioDocument.Pages.ItemU(varPageTitle).Index = intPageIndex
Exit For
End If
Next intPageIndex
Next varPageTitle
' Clean up
Set colPageTitles = Nothing
I mentioned this in another comment, but when I made some test pages, it was always shuffling the pages around when I ran it because I the way that this is implemented, I don't believe that Exit For should be in there.
I also swapped the comparison to StrCompare due to personal preference along with the order of the for loops.
Sub PageSort()
Dim titlesColl As Collection
Set titlesColl = New Collection
Dim i As Long
For i = 1 To ActiveDocument.Pages.Count
titlesColl.Add ActiveDocument.Pages.Item(i).Name
Next i
Dim title As Variant
For i = 1 To ActiveDocument.Pages.Count
For Each title In titlesColl
If StrComp(ActiveDocument.Pages.Item(i).Name, title, vbTextCompare) < 0 Then
ActiveDocument.Pages.Item(title).index = i
End If
Next title
Next i
Set titlesColl = Nothing
End Sub
Private Sub reorderPages()
Dim PageNameU() As String
Dim isBackgroundPage As Boolean
Dim vsoPage As Visio.Page
Dim vsoCellObj As Visio.Cell
'// Get All Pages
Dim i As Integer
For Each vsoPage In ActiveDocument.Pages
i = i + 1
ReDim Preserve PageNameU(i)
PageNameU(i) = vsoPage.NameU
Next vsoPage
For i = 1 To UBound(PageNameU)
Set vsoPage = vsoPages.ItemU(PageNameU(i))
Set vsoCellObj = vsoPage.PageSheet.Cells("UIVisibility")
isBackgroundPage = vsoPage.Background
'// Make foreground page to set page index
If isBackgroundPage = True Then
vsoCellObj.FormulaU = visUIVNormal
vsoPage.Background = False
End If
vsoPage.Index = NumNonAppSysPages + i
'// Set to background page
If isBackgroundPage = True Then
vsoCellObj.FormulaU = visUIVHidden
vsoPage.Background = True
End If
Next i
End Sub

vba cast variant listbox / Objects

I got an issue with "casting" variants to defined objects.
At runtime my variant variable is of type "Variant/Object/Listbox", which i then want to set to a ListBox variable to route it as a parameter to another function (GetSelected) that requires a Listbox object.
But I get the error 13: types incompatible on command "Set lst = v".
Any ideas how to get it working?
Code:
Function GetEditableControlsValues(EditableControls As Collection) As Collection
'Gibt die Werte der editierbaren Felder zurück.
Dim v As Variant
Dim coll As New Collection
Dim lst As ListBox
For Each v In EditableControls
If TypeName(v) = "ListBox" Then
Set lst = v 'Fehler 13: Typen unverträglich. v zur Laufzeit: Variant/Object/Listbox.
coll.Add GetCollectionString(GetSelected(lst))
Else
coll.Add v.Value
End If
Next
End Function
This is what I have so far:
Imagine that you have a module with the following code in it:
Option Explicit
Public Sub TestMe()
Dim colInput As New Collection
Dim colResult As Collection
Dim lngCount As Long
Dim ufMyUf As UserForm
Set ufMyUf = UserForm1
Set colInput = GetListBoxObjects(ufMyUf)
For lngCount = 1 To colInput.Count
Debug.Print colInput(lngCount).Name
Next lngCount
End Sub
Function GetListBoxObjects(uf As UserForm) As Collection
Dim colResult As New Collection
Dim objObj As Object
Dim ctrCont As Control
For Each ctrCont In uf.Controls
If LCase(Left(ctrCont.Name, 7)) = "listbox" Then
Set objObj = ctrCont
colResult.Add objObj
End If
Next ctrCont
Set GetListBoxObjects = colResult
End Function
If you run TestMe, you would get a collection of the ListBox objects. Anyhow, I am not sure how do you pass them to the collection function, thus I have decided to iterate over the UserForm and thus to check all of the objects on it.
Cheers!
I had problems with casting controls myself and didn't find a general solution that I could use easy.
Eventually I found the way to do it: store as "Object" makes it easy to convert to whatever type the control actually is.
I tested (and use) it
The sub below shows that it works (here : 1 TextBox; 1 ListBox; 1 ComboBox; 1 CommandButton on a worksheet)
Sub Test_Casting()
Dim lis As MSForms.ListBox
Dim txt As MSForms.TextBox
Dim btn As MSForms.CommandButton
Dim com As MSForms.ComboBox
Dim numObjects As Integer: numObjects = Me.OLEObjects.Count
Dim obj() As Object
ReDim obj(1 To numObjects) As Object
Dim i As Integer: i = 0
Dim cttl As OLEObject
For Each ctrl In Me.OLEObjects
i = i + 1
Set obj(i) = ctrl.Object
Next ctrl
Dim result As String
For i = 1 To numObjects
If TypeOf obj(i) Is MSForms.ListBox Then
Set lis = obj(i): result = lis.Name
ElseIf TypeOf obj(i) Is MSForms.TextBox Then
Set txt = obj(i): result = txt.Name
ElseIf TypeOf obj(i) Is MSForms.CommandButton Then
Set btn = obj(i): result = btn.Name
ElseIf TypeOf obj(i) Is MSForms.ComboBox Then
Set ComboBox = obj(i): result = com.Name
Else
result = ""
End If
If (Not (result = "")) Then Debug.Print TypeName(obj(i)) & " name= " & result
Next i
For i = 1 To numObjects
Set lis = IsListBox(obj(i))
Set txt = IsTextBox(obj(i))
Set btn = IsCommandButton(obj(i))
Set com = IsComboBox(obj(i))
result = ""
If (Not (lis Is Nothing)) Then
result = "ListBox " & lis.Name
ElseIf (Not (txt Is Nothing)) Then
result = "TexttBox " & txt.Name
ElseIf (Not (btn Is Nothing)) Then
result = "CommandButton " & btn.Name
ElseIf (Not (com Is Nothing)) Then
result = "ComboBox " & com.Name
End If
Debug.Print result
Next i
End Sub
Function IsListBox(obj As Object) As MSForms.ListBox
Set IsListBox = IIf(TypeOf obj Is MSForms.ListBox, obj, Nothing)
End Function
Function IsTextBox(obj As Object) As MSForms.TextBox
Set IsTextBox = IIf(TypeOf obj Is MSForms.TextBox, obj, Nothing)
End Function
Function IsComboBox(obj As Object) As MSForms.ComboBox
Set IsComboBox = IIf(TypeOf obj Is MSForms.ComboBox, obj, Nothing)
End Function
Function IsCommandButton(obj As Object) As MSForms.CommandButton
Set IsCommandButton = IIf(TypeOf obj Is MSForms.CommandButton, obj, Nothing)
End Function
One use for it is a class for handling events in one class.
Private WithEvents intEvents As IntBoxEvents
Private WithEvents decEvents As DecBoxEvents
Private genEvents As Object
Private genControl as OLEobject
Public sub Delegate(ctrl As OLEObject)
set genControl = ctrl
' Code for creating intEvents or decEvents
if .... create intevents.... then set genEvents = new IntEvents ' pseudo code
if .... create decevents.... then set genEvents = new DecEvents ' pseudo code
end sub
I hope this helps others that struggle with casting controls

Is there a Way to test if a Chart in Excel has its series stacked or not

To my understanding Chars have the ChartType as XlChartType property that but that is a long list of Enumerated Values. Is there a way to test if chart is using stacked Series without listing them all?
I am trying to avoid following scenario:
Select ActiveChart.ChartType
Case xlAreaStacked
....
Case xlBarStacked
....
Case xlColumnStacked
....
... 1000 more Cases ....
End Select
Some sample code below to produce a dictionary object with the members of the requested Enum.
Code adapted from dlmille's answer here: http://www.experts-exchange.com/Software/Office_Productivity/Office_Suites/MS_Office/Excel/Q_27613392.html
Sub tester()
Dim dict
Set dict = GetEnumLookup("Excel", "XlChartType")
If Not dict Is Nothing Then
'get string from numeric value and see if it contains "stacked"
Debug.Print UCase( dict(XlChartType.xl3DAreaStacked) ) Like "*STACKED*"
Debug.Print UCase( dict(XlChartType.xl3DArea) ) Like "*STACKED*"
Else
MsgBox "Enum not recognised!"
End If
End Sub
'VB Project References required:
' Microsoft Visual Basic for Applications Extensibility
' TypeLib Information
Function GetEnumLookup(LibName As String, sEnumName As String) As Object
Dim rv As Object
Dim tl As TLI.TypeLibInfo
Dim mi As TLI.MemberInfo
Dim tiEnum As TLI.TypeInfo
Dim vbProj As VBProject, oVBProjRef As Reference
Set vbProj = ThisWorkbook.VBProject
For Each oVBProjRef In vbProj.References
'Debug.Print oVBProjRef.Name, oVBProjRef.FullPath
If oVBProjRef.Name = LibName Then
Set tl = New TypeLibInfo
tl.ContainingFile = oVBProjRef.FullPath
On Error Resume Next
Set tiEnum = tl.GetTypeInfo(sEnumName)
On Error GoTo 0
If Not tiEnum Is Nothing Then
Set rv = CreateObject("scripting.dictionary")
For Each mi In tiEnum.Members
rv.Add mi.Value, mi.Name
'or if you want to map the other direction...
'rv.Add mi.Name, mi.Value
Next mi
End If
Exit For
End If
Next oVBProjRef
Set GetEnumLookup = rv
End Function

String values left out of PropertyInfo.GetValue

I am not very well-versed in Reflection, but I have been working on this bit of code for a few days trying to obtain the values of class properties. I am using an API to find the values inside of cron jobs managed by the program VisualCron.
I'll explain the structure a bit. Each cron job has several tasks inside of it which have their own settings. The settings are stored in properties inside the TaskClass class that are declared like so:
Public Property <propertyname> As <classname>
Each property is tied to its own class, so for instance there is an Execute property inside TaskClass which is declared like this:
Public Property Execute As TaskExecuteClass
Inside TaskExecuteClass are the properties that hold the values I need. With the below block of code I have been able to retrieve the property values of all types EXCEPT strings. Coincidentally, the string values are the only values I need to get.
I know there must be something wrong with what I've written causing this because I can't find anyone with a similar issue after lots and lots of searching. Can anyone help me please?
Dim strAdd As String = ""
For Each t As VisualCronAPI.Server In vcClient.Servers.GetAll()
For Each f As VisualCron.JobClass In t.Jobs.GetAll
For Each s As VisualCron.TaskClass In f.Tasks
Dim propVal As Object
Dim propInfo As PropertyInfo() = s.GetType().GetProperties()
For i As Integer = 0 To propInfo.Length - 1
With propInfo(i)
If s.TaskType.ToString = propInfo(i).Name.ToString Then
Dim asm As Assembly = Assembly.Load("VisualCron")
Dim typeName As String = String.Format("VisualCron.{0}", propInfo(i).PropertyType.Name)
Dim tp As Type = asm.GetType(typeName)
Dim construct As ConstructorInfo = tp.GetConstructor(Type.EmptyTypes)
Dim classInst As Object = construct.Invoke(Nothing)
Dim classProps As PropertyInfo() = classInst.GetType().GetProperties()
For h As Integer = 0 To classProps.Length - 1
With classProps(h)
If .GetIndexParameters().Length = 0 Then
propVal = .GetValue(classInst, Nothing)
If Not propVal Is Nothing Then
strAdd = f.Name & " - " & s.Name & " - " & .Name & " - " & propVal.ToString
End If
End If
If strAdd <> "" Then
ListBox1.Items.Add(strAdd)
End If
End With
Next
End If
End With
Next
Next s
Next f
Next t
I solved my own problem, albeit in a crappy way. This is probably incredibly inefficient, but speed isn't a necessity in my particular case. Here is the code that works:
Dim strAdd As String = ""
For Each t As VisualCronAPI.Server In vcClient.Servers.GetAll()
For Each f As VisualCron.JobClass In t.Jobs.GetAll
For Each s As VisualCron.TaskClass In f.Tasks
Dim propVal As Object
Dim propInfo As PropertyInfo() = s.GetType().GetProperties()
For i As Integer = 0 To propInfo.Length - 1
With propInfo(i)
If s.TaskType.ToString = propInfo(i).Name.ToString Then
Dim asm As Assembly = Assembly.Load("VisualCron")
Dim typeName As String = String.Format("VisualCron.{0}", propInfo(i).PropertyType.Name)
Dim tp As Type = asm.GetType(typeName)
Dim construct As ConstructorInfo = tp.GetConstructor(Type.EmptyTypes)
Dim classInst As Object = construct.Invoke(Nothing)
Dim classProps As PropertyInfo() = classInst.GetType().GetProperties()
For h As Integer = 0 To classProps.Length - 1
With classProps(h)
If .GetIndexParameters().Length = 0 Then
propVal = .GetValue(CallByName(s, propInfo(i).Name.ToString, [Get]), Nothing)
If Not propVal Is Nothing Then
If propVal.ToString.Contains("\\server\") Or propVal.ToString.Contains("\\SERVER\") Then
strAdd = f.Name & " - " & s.Name & " - " & .Name & " - " & propVal.ToString
ListBox1.Items.Add(strAdd)
End If
End If
End If
End With
Next
End If
End With
Next
Next s
Next f
Next t
The piece of code that made the difference was the
classProps(h).GetValue(CallByName(s, propInfo(i).Name.ToString, [Get]), Nothing)
line.
If there are any suggestions for improving this code - I'm assuming that I've still got a lot of mistakes in here - then please comment for the future viewers of this answer and so I can adjust my code and learn more about how all of this works.