Visio VBA trying to get a list of containers and member shapes - arrays

Background: I have some code that runs through a Visio page and returns all the shapes. Many of these shapes are in containers, so I would like to know what container a shape belong to.
Original approach: I was hoping to retrieve the "parent" container of each shape (I only need one level of container, there are no containers within containers) using the Shape.ContainingShape property but that was only returning '0' for every shape.
If anyone has a solution for how I was originally trying to get the container, that would be the most elegant. But since I can't get that to work, I am trying the following alternative, which is also presenting a roadblock.
Current approach: I was able to get a list of all the containers on the page, and now I would like to pull the member shapes for each container. It's not as clean, but it would allow me to cross-reference the shapes and get the containers they belong to.
Issue: I am getting "Error 13 Type Mismatch" when trying to create an array with column 0 being the container name, and column 1 being the member shapes.
' Create array of containers and member shapes
Dim arr() As Long
Dim vsoMemberShape As Shape
Dim vsoContainerShape As Shape
Dim containerArr() As Long
Dim rows As Integer
Dim i As Integer
For Each ContainerID In vsoPage.GetContainers(visContainerIncludeNested)
Set vsoContainerShape = vsoPage.Shapes.ItemFromID(ContainerID)
arr = vsoContainerShape.ContainerProperties.GetMemberShapes(1)
rows = UBound(arr)
ReDim containerArr(0 To rows, 0 To 1)
For i = 0 To UBound(arr)
Set memberShape = vsoPage.Shapes.ItemFromID(arr(i))
containerArr(i, 0) = vsoContainerShape.NameU
containerArr(i, 1) = vsoMemberShape.NameU
Next
Next
' The following code is in a For loop, not shown
' shapeToName is what I want to compare to the member shapes in the container
' array defined above, and then retrieve the corresponding container
' This is where the error is popping up
shapeToName = CStr(vsoShapeTo.Name)
Dim x As Integer
x = Application.Match(shapeToName, Application.Index(containerArr, 0, 1), 0)
shapeContainer = containerArr(x, 1)

I think what you're looking for is Shape.MemberOfContainers, which returns an array of containers that a shape is a member of.
You can also have a look at this post which covers the same issue:
Visio: How to get the shapes that are contained in one shape?
I'll also throw in a link to a post by David Parker that covers containers in the context of cross-functional flowchart, which makes good use of Containers and Lists:
https://bvisual.net/2009/09/07/visio-2010-containment-and-cross-functional-flowcharts/

Related

Is there a way to make an empty array and growing it as it gets data in vb.net?

I have been working on a project, and am attempting to make a new array for data. I have tried making an empty array with Dim Name() As String = {}. I am using a ListView, and the way I have done it there are blank spots where I have gotten rid of data. This is my current code:
Sub English(ByVal Country() As String, ByVal Language() As String)
rbDisplayallData.Checked = False
lstResults.Visible = True
lstResults.Items.Clear()
lstResults.Columns.Clear()
With lstResults
.View = View.Details
.Columns.Add("English Speaking Countries", 200, HorizontalAlignment.Left)
End With
For i = 0 To 181
Dim EnglishSpeakingCountries(i) As String
If Language(i) = "English" Then
EnglishSpeakingCountries(i) = Country(i)
End If
lstResults.Items.Add(New ListViewItem({EnglishSpeakingCountries(i)}))
Next
End Sub
I am trying to get rid of these spaces.
I Was thinking if I were to compact the array or make a new one with the same data going into a new array it would fix the issue.
If you have a solution please let me know.
There are two things that could be considered an empty array
An array with no elements, i.e. a Length of zero.
An array where every element is Nothing.
All arrays are fixed-length. Once you create an array with a particular number of elements, it always has that number of elements. You can use ReDim Preserve or Array.Resize but, in both those cases, what actually happens is that a new array is created and the elements copied from the old array. The new array is assigned to the same variable but anywhere the old array is referenced, it will still have that same number of elements. Try running this code to see that in action:
Dim a1 As String() = {}
Dim a2 As String() = {"First", "Second", "Third"}
Dim b1 = a1
Dim b2 = a2
Console.WriteLine(a1.Length)
Console.WriteLine(a2.Length)
Console.WriteLine(b1.Length)
Console.WriteLine(b2.Length)
Console.WriteLine()
ReDim Preserve a1(2)
Array.Resize(a2, 6)
Console.WriteLine(a1.Length)
Console.WriteLine(a2.Length)
Console.WriteLine(b1.Length)
Console.WriteLine(b2.Length)
Console.ReadLine()
Output:
0
3
0
3
3
6
0
3
As you'll be able to see, a1 and a2 end up referring to new arrays with the specified lengths but the original arrays with the original lengths still exist and are still accessible via b1 and b2.
If you start with an array with no elements then you can use ReDim Preserve or Array.Resize to give the appearance of resizing the array but that's not really what's happening and that should generally be avoided. If you know how many elements you'll end up with then you could create an array of that size and then set each element in turn. You'd need to keep track of the next element index though, so that's still a bit tedious.
Generally speaking, if you want an array-like data structure but you want it to be able to grow and shrink as required, you should use a collection. The most common collection is the List(Of T), where T is any type you care to specify in your code. If you want to store String objects then use a List(Of String). You can call Add to append a new item to the end of the list, as well as Insert, Remove and RemoveAt methods. You can also get or set an item by index, just as you can do for array elements.
Note that a List(Of T) actually uses an array internally and uses the aforementioned method of "resizing" that array. It optimises the process somewhat though, which makes the code easier for you to write and large collections more efficient to use.
It's worth noting that, in your own code, the Columns and Items properties of your ListView are both collections, although they are slightly different to the List(Of T) class.
Looking at your original code, this:
For i = 0 To 181
Dim EnglishSpeakingCountries(i) As String
If Language(i) = "English" Then
EnglishSpeakingCountries(i) = Country(i)
End If
lstResults.Items.Add(New ListViewItem({EnglishSpeakingCountries(i)}))
Next
could be changed to this:
Dim englishSpeakingCountries As New List(Of String)
For i = 0 To 181
If Language(i) = "English" Then
englishSpeakingCountries.Add(Country(i))
lstResults.Items.Add(Countries(i))
End If
Next
Note that you're just adding items to two collections. I guess the question is whether you actually need this extra collection at all. If you do want to use it later then you need to assign it to a member variable rather than a local variable. If you don't need it later then don't create it at all. As I said, you're already adding items to a collection in the ListView. Maybe that's all you need, but you haven't provided enough info for us to know.

I want to create a 2D array of points that links to my 2D array of components

I am making a circuit diagram drawer for a school project and have a 2D array that stores whether 2 components are connected or not. I want to find a way to have an array that then stores the co-ordinates of these points so that if they are deleted, it can find the points and get rid of the connection
ElseIf P(1).X = Nothing And P(1).Y = Nothing Then
P(1).X = pointx
P(1).Y = pointy
Form1.Drawlines() 'run the DrawLines sub in Form1.vb
NoOfNodes = NoOfNodes + 1 'increment the NoOfNodes Variable by 1
Node2 = COMPONENTID
Connections(Node1, Node2) = true
This is the code that stores the points being drawn, at the end the value of Connections is set to true to show that there is a connection between the components.
I need a way to have another array that can say something along the lines of:
Connections(Node1,Node2) = (P(1),P(0))
any contribution is much appreciated,
Charlie
Sounds like each element in your array should point to an instance of a class. On the class you can pack as many attributes and as much information about the connection as you like.

How to pass the value of picturebox image to another picturebox using array vb.net

I'm beginner at vb.net please help me
How can i pass my picturebox1.image to picturebox2.image and the value of picturebox2.image to picturebox3.image. My point is, Everytime the new image arrive it's like the images moving forward.the incoming image will go to picturebox1. And the old value of picturebox1 will move to picturebox2 and the value of picturebox2 will go to picturebox3. I don't know how to construct the logic in coding. Help me please
Ok, there are a number of ways the accomplish this. I use my database to store images all the time, so I'll show you a method for displaying the images in the order they are retrieved from the database. You are correct in that if you assign pic2.image to pic1.image and so on, the picture boxes will all have the same image because that's the value of pic1, reversing the order does the same thing. You need to assign the image you want in each picture box to that picture box.
First I create a list of images, they're all jpg's but it shouldn't matter:
Imports System.Drawing ' <---- I assume you have this at the top of the code behind
Dim MyImages as List(of Image)
I retrieve the images from the database in a datatable in order of the primary key ascending, key is 1 to n and fill the list:
Dim x as Integer = 1
pictureboxname = "pic" & x
For Each MyDataRow as DataRow in MyTable.Rows
' Load the list, the image is in column zero
MyImages.Add(MyDataRow(0))
' because this is the initial list, you can also populate the pictureboxes too
' you need to use the picturebox name in an expression to populate the pictureboxes in a loop.
' Something like this .... this is untested
(pictureboxname).Image = MyImages(MyImages.Count -1)
' and change picturebox name to the next picture box
x += 1
' I assume picturebox name is pic1, pic2, etc ....
pictureboxname = "pic" + x
' you'll have to limit this loop to the number of pictureboxes you have if they are less than the number of images, but you get the idea
Next
When a new images is added, you can then add it to the list and database at the same time and then repopulate the picture boxes.
StoreImage(NewImage)
MyImages.Add(NewImage)
Dim x as integer = 1
pictureboxname = "pic" & x
For each MyImage as Image in MyImages
' assign each image in turn to the picture boxes ... again, untested
(pictureboxname).Image = MyImage
x += 1
pictureboxname = "pic" & x
' again, you'll have to limit this loop to the number of pictureboxes you have if it is less than the number of images ....
Next
This is not be exactly what you're looking for, but should get you started.

Visio VBA - How can I distribute shapes with a known, fixed distance

I'd like to place all currently selected shapes into an array. I'd then like to sort that array so I can find either the top most or left most shape in the array. I'd then like to use that shape as my starting point, and then from there align the other shapes a fixed, known distance apart. I've tried to place the shapes into an array like so:
Dim numShapes As Integer, i As Integer
Dim arrShapes As Visio.Selection
numShapes = Visio.ActiveWindow.Selection.Count
For i = 1 To numShapes
arrShapes(i) = Visio.ActiveWindow.Selection(i)
Next i
I have tried to create the array with no type specification, specifying as variant, and as in this example as selection. I don't know if I can put them into a list of some kind either? Obviously I can't get to the point of sorting the array and then distributing my shapes until I can get the array to populate. I'm placing a break point in the code and I have the "Locals" window open and I can see that the array is not being populated.
Update:
Why does this work,
Dim Sel As Visio.Selection
Dim Shp As Visio.Shape
Set Sel = Visio.ActiveWindow.Selection
For Each Shp in Sel
Debug.Print Shp.Name
Next
And this does not?
Dim i As Integer
Dim Shp As Visio.Shape
For i = 1 To Visio.ActiveWindow.Selection.Count
Set Shp = Visio.ActiveWindow.Selection(i)
Debug.Print Shp.Name
Next i
Regards,
Scott
There was a couple of problems in your code - fixing only one would not have got you any further in understanding if you had actually fixed anything.
Your arrShapes is declared as a general object - the Selection
Object is one of those objects that is the Jack of all trades, and
master of none.
You didn't "Set" when assigning to the array.
I don't have Visio on this machine, so cannot directly test the code below. I am also assuming that all items selected are shapes (usually a safe assumption in Visio).
Dim numShapes As Integer, i As Integer
Dim arrShapes() As Shape ' Set this up as an array of shape
If Visio.ActiveWindow.Selection.Count > 0 then ' don't want to cause a problem by setting the array to 0!
ReDim arrShapes(Visio.ActiveWindow.Selection.Count)
numShapes = Visio.ActiveWindow.Selection.Count ' while not really necessary it does help explain the code.
For i = 1 To numShapes
' must Set as we want the reference to the shape, not the default value of the shape.
Set arrShapes(i) = Visio.ActiveWindow.Selection(i)
Next i
Else
MsgBox "No shapes selected. Nothing done." ' soft fail
End If

Creating pie chart with array

I am trying to create a Pie chart based on an array (rather than a range). The array is [11,10,1] (I have other code that populates the array).
Dim type_chart As Chart
Dim type_array(2) As Integer
Set type_chart = Charts.Add
type_chart.ChartType = xlPie
type_chart.SeriesCollection(1).Values = type_array
On the last line of the code above, I receive an 'Invalid Parameter' error.
Also, it doesn't have to use an array, but it cannot use a Range.
Your chart needs to work from a range. Find a blank area you can use. Try something like the following:
my_temp_range = "A10:C10"
ActiveSheet.Range(my_temp_range) = type_array
type_chart.SeriesCollection(1).Values = ActiveSheet.Range(my_temp_range)
Once you're working with a temporary range you may not even need the last line (so long as the size of the array doesn't change). You could just set up the chart in advance instead.

Resources