How to reference "active/selected" chart data point in Excel using VBA - excel

I am looking for a way to reference active/selected chart data point in Excel using VBA.
Imagine I have a line chart, which I would like to add an error bar to. But, I do not want to add error bars to the whole series, but only to ONE, selected point. See the screen below:
What I want to do is to add a vertical error bar that would point down to the X-axis, something like this:
I KNOW how to do it in Excel, there are multiple ways, for example, adding a new one-point series and then adding an error bar. There are other ways. The issue I have is HOW to reference active/selected data point.
If I would choose to create a new, one point series, I need to know the point number to do that. I know (I used it) that you can reference points with Points object/method. Sadly, I do not know how to extract the selected point number, coordinates, whatever, so I can work with it later on in my project.
I cannot add any code, as everything I have done is formatting and playing with error bars, as well as iterating through existing, ALL data points (the code would have no use in this case). What I am looking for is THE selected point information, so I could refer to it as .Point(x) with x being my previously extracted point number, without being forced to reference point number right away like .Point(8) (I do not know the specific number, as I just clicked on it).
I have seen the way to extract it using chart events, but this is a an overkill for what I want to do in my little simple project (especially how to reference the "extracted" point in other macros, outside the class module).
Any ideas? All help is greatly appreciated, as I am lost after 3 days of trying to find the way to reference it.

To get the index, you can parse the point's Name, which is in the format:
S<series number>P<point number>
Sub Test()
If TypeOf Selection Is Point Then
Dim p as Point
Set p = Selection
Debug.Print CLng(Split(p.Name, "P")(1)) ' this is p's index
End If
End Sub
To get more information about that point, such as the x and y values, perhaps you could do the following:
Sub Test()
If TypeOf Selection Is Point Then
Dim p As Point
Set p = Selection
Dim i As Long
i = CLng(Split(p.Name, "P")(1))
Dim s As Series
Set s = p.Parent
Debug.Print s.Values(i) ' Values is a one-based array so you can use the point index to get its corresponding value
Debug.Print s.XValues(i)
End If
End Sub

Related

Table extending instead of creating

I have a bug in my excel sheet that appears on other computers I can't reproduce on my machine.
I made a sub that takes data from a database and create tables in Excel. In this case it's mining projects.
It picks the first cell, finds the first empty cell when going to the right, writes the header, makes a table with it then fill it with another sub.
When a coworker tries to use it, it extends the previous table then crashes while trying to make the new one, as two tables can't overlap.
Initial Layout:
What's supposed to happen:
What my coworkers get:
Public Sub creerTable(Nom As Variant)
Dim cellule As Range
' se placer
Set cellule = ActiveWorkbook.Worksheets("DATA").Range("A1").End(xlToRight).Offset(0, 1)
Do While cellule.Value <> "" Or Not IsEmpty(cellule)
Set cellule = cellule.Offset(0, 1)
Loop
'cr?er le header
cellule.Interior.Color = vbBlue
cellule.Value = Nom
'cr?er la table
ActiveSheet.ListObjects.Add(xlSrcRange, cellule, , xlYes).Name = Nom
End Sub
EDIT: After more testing on my coworkers' computers, the problem is likely linked to an Excel setting. When something is written to the right of a table, the table extends to cover it. Googling revealed this to be an auto-correct behavior. I guess I'll have to deactivate that option of auto-correct.
The answer was really dumb. The problem is an autocorrect setting in excel. All I had to do was add this line:
Application.autocorrect.AutoExpandListRange = False
It's not clear how this code could possibly work on your machine, but not your co-workers. I suspect that the inputs must somehow be different, but that is for you (having access to the inputs, which we do not) to work out on your own. Generally: inconsistencies cannot exist, so when one appears to exist, examine your inputs and you'll usually find there is a discrepancy in the input data which causes the divergent output.
I'll note that The End method of range objects may not be reliable, in particular I observed things like Range("A1").End(xlToRight).Address yields "B1" in the following scenario where column A, B and C are each separate ListObject tables:
And ALSO in this scenario:
It seems like you try to find the next empty cell/column (which may be between two or more non-empty cell/column). It should be trivial to do this, but we need to understand that next is relative to what existing thing on your worksheet?
Are you're trying to find the next empty cell after a ListObject named "Tâches" (or cell value = "Tâches")?

How to list all excel current regions in one worksheet

In order to navigate through complex spreadsheets that I'm asked to analyse I need a list of all current regions in a worksheet. Excel help does not give me many clues. My solution so far is to loop over areas using the special cells function, but it is rather slow.
Function list_all_current_regions(work_sheet)
Dim current_region_dic As New Dictionary
Set r = work_sheet.Cells(1, 1)
For Each x In Array(xlCellTypeConstants, xlCellTypeFormulas)
Set c = work_sheet.Cells(1, 1).SpecialCells(x, 23)
For Each a In c.Areas
If Not current_region_dic.Exists(a.CurrentRegion.Address) Then
current_region_dic.Add a.CurrentRegion.Address, ""
End If
Next
Next
Set list_all_current_regions = current_region_dic
End Function
Is there a smarter way to list all the current regions in a worksheet?
Over the years I've avoided having multiple ranges on Worksheets for the very reason you are asking about, and I move disconnected content onto its own Worksheet when a client gives me a model designed as a single "ubersheet".
The only way around this I've found is to use Named Ranges. They are accessed using the Names collection attached to the Workbook and Worksheet objects.
One other tip I'll give is to just used the Named Range on the top left cell only then you can use the CurrentRegion property to grab the entire range. This helps when you have expanding regions that you don't want to go in to set and reset the entire range.
https://msdn.microsoft.com/en-us/library/office/ff196678.aspx?f=255&MSPPError=-2147217396

Calling a custom built VBA function when an Excel cell is changed

I'd like to preface this question by saying that I am an undergrad in college who knows C++ and has a very rudimentary understanding of VBA.
Now then, as stated in the title I need some help configuring some VBA code for an Excel worksheet so that whenever a cell in a column (specifically the D column) is modified it will automatically update other cells within the same row.
Essentially I want this to work such that when user Bob modifies cell D26 (for example) it will call a custom function I built and insert that code into cell B26 and then repeat with a different function for cell C26.
However, this function needs to be such that if cell D27 is modified it will only modify other cells in row 27, leaving row 26 and prior or subsequent rows alone until such a time as this function is called in D28 and so on.
I'm not entirely sure if this is even possible but I'd be gracious if anybody could help me configure this.
The code I built/scavenged from the internet for my custom function is this:
http://pastebin.com/RE0V2nrT
The second function I want to call for this project is the =TODAY() function built into Excel.
The code I have scraped together so far for checking if the cell has changed is this:
http://pastebin.com/S5E8cmty
If anybody could help me understand how to write what I'm looking for it would be much appreciated. If you have a different approach to solving the issue I would also love to hear it... as long as you could help me then enact your solution, haha!
Anyways, thanks to anybody who replies.
Have a look at the worksheet events available within the Excel namespace.
For this, you would use the Change event
If you double click on the worksheet you want to monitor, you can insert a Worksheet_Change sub. Then you can use the intersect function to check if the changed cell was within your range you want to monitor (e.g. D:D).
You can specify which cells you want to change. Here I just gave an example based on what you asked. This will put the output of your function into cell B[R] and put the current date into cell C[R]. Note that I'm using the Now() function since there is no Today() function in VBA. Since this returns both date and time, I'm using the Format function to get just the date.
Just for fun, let's go a little further into the object model and first get the Worksheet object to which the target range belongs. This is not 100% necessary - you could just rely on ActiveSheet. Now, you probably don't need to do this, and it's mostly just for fun, but it's also worth noting that if you were programmatically making changes to this sheet, but had not activated this sheet first (so another sheet was active) and you had not turned off EnableEvents you would get some strange results :)
Private Sub Worksheet_Change(ByVal Target As Range)
Dim TargetSheet As Worksheet
Set TargetSheet = Target.Parent
With TargetSheet
If Not Application.Intersect(Target, .Range("D:D")) Is Nothing Then
.Cells(Target.Row, 2) = ExtractWindowsUser()
.Cells(Target.Row, 4) = Format(Now(), "YYYY-MM-DD")
End If
End With
End Sub
Explanation
Worksheet change sub is declared like this. The Worksheet objects have pre-defined method stubs for events. Kind of like an interface, though not listed as an interface in the documentation. If you think of it in that concept, this is your event handshake. See the link I posted above for a list of the worksheet events available.
Private Sub Worksheet_Change(ByVal Target As Range)
In the next lines we are getting the worksheet object to which the object named Target belongs. You can see in the sub declaration that Target is declared as an object of the type Range. If you check out the Worksheet object (linked above) or the Range object documentation you'll see that the range object is a member of the worksheet object, and the documentation kind of sucks here, but FYI the worksheet object is contained within the Parent property. Now, originally I had my code using the ActiveSheet member of the Application object - but I've edited it for the reasons given in my answer above.
Dim TargetSheet As Worksheet
Set TargetSheet = Target.Parent
I use With Blocks to save typing the same Worksheet reference in multiple places. A With block just lets me access the members of the namespace specified (in this case members of the object TargetSheet) by typing .SomeMember. The compiler understands that every reference like this refers to whatever is specified in the opening With .... statement. I personally like this for readability, but I also recommend it for maintenance (change reference one place vs many). Also having a single reference gives a tiny, insignificant, probably not worth mentioning performance boost over multiple references as well.
With TargetSheet
Next we check whether or not Target is within the range of cells we want to watch. The If....Then should look familiar enough. For our condition we use the boolean operator Not to check if the result of the intersect function (linked above) Is Nothing. The reason we do this is to check if the return is allocated. If an object is allocated the Not SomeObject Is Nothing condition will evaluate to False. If the object is not allocated (i.e. our Intersect function failed to return anything) then the statement evaluates to True. So, from the Intersect function documentation we know that if our return is allocated, the ranges intersect and the intersecting range object was returned. Thus if we want to know if they intersect, we can just check for the opposite of a failure.
If Not Application.Intersect(Target, .Range("D:D")) Is Nothing Then
The next lines then just execute some code on cells within the same row as Target. We use the Cells member of the worksheet object to specify what cells to modify. Per the documentation, the default property for Cells is Item which lets us access a range object through a row and column index like this: .Cells[Row,Column]. So, I simply use the row of our Target object and the column you wanted (column "A" =1, "B"=2, etc. You can see this by changing excel properties to R1C1 reference style if you are interested).
.Cells(Target.Row, 2) = ExtractWindowsUser()
And I think the Format() and Now() functions are pretty well explained in the documentation.

Excel VBA "AxisBetweenCategories" property missing in ChartSpace

I vainly try to set the AxisBetweenCategories property to False in a Userform ChartSpace. It is a clusteredBarChart. I did the same manually in a normal chart and it worked. Recording a Marco generated the code with the AxisBetweenCategories property. Why can't I use it in the Userform ChartSpace.
me.ChartSpace1.Charts(0).Axes(0).AxisBetweenCategories = False 'doesn't work
What do I miss?
Thanks
The "Chart" inside a ChartSpace object is of type ChChart and the same properties and methods you use on a Chart (worksheet, or chartsheet) do not translate directly so unfortunately the macro recorder won't be of much help, you will have to turn to good old fashioned debugging, trial & error.
I use the Locals window to examine the objects for a hint at the object model (that's how I observe the type is ChChart, etc.)
You can then look at the available properties and Google usually can point you in the right direction, like this example. Exploring the intellisense, I see that there are really limited options that are not the same as the Chart objects on a worksheet.
After all of that, this is probably not the answer you want to hear, but it can't be done the way you want it to be done.
This seems to verify that observation and suggests that while some things are the same, others can simply not be rendered the same way.
[Chartspace Charts do] not have the refined axis crossing as Excel
does. Features between the two will be similar in certain area and
not in others
And a similar example from Microsoft:
This example sets the category axis to cross the value axis at value zero (0) in the chart workspace.
Sub SetCrossingValue()
Dim chConstants
Dim axValueAxis
Dim axCategoryAxis
Set chtContants = ChartSpace1.Constants
Set axValueAxis = ChartSpace1.Charts(0).Axes(chConstants.chAxisPositionValue)
Set axCategoryAxis = ChartSpace1.Charts(0).Axes(chConstants.chAxisPositionCategory)
axValueAxis.CrossingAxis = axCategoryAxis
axCategoryAxis.CrossesAtValue = 0
End Sub

Link ActiveX to Excel

Hope somebody can help!
I am programming in VB6 and am trying to write an activeX control for an indicator. The indicator should change color relative to an excel open workbook cell being true or false. The indicator should be auto updating i.e. the indicator needs to link live to the excel cell.
I can then place several of the indicators c/w links to different cells on a userform. The workbook is opened and tested in the userform and object references set up ok.
I can't figure out how to link the indicator to the excel cell.
This is part of a larger project I am trying. Other control such as bargraphs, Switches etc. to be added if I can get the first one working.
Thanks in advance
You need to first add a reference to the Microsoft Excel x.x library in your control's references.
Next, you should add a private module level variable of type Excel.Worksheet, and declare it WithEvents, e.g.
Private WithEvents m_oWorksheet As Excel.Worksheet
You should also create a Property Set procedure called Worksheet which sets this variable.
Then you should add code for its Change event, e.g.
Private Sub m_oWorksheet_Change(ByVal Target As Range)
If Target.Column = 1 And Target.Row = 2 Then ' m_oWorksheet.Range("A2")
'Do some coding here
End If
End Sub
Obviously, .Column = 1 and .Row = 2 would be replaced by the cell coordinates you are interested in. I originally used coordinates like "A2", but found that the objects returned from m_oWorksheet.Range("A2") cannot be directly compared with the Target object, e.g.
If Target Is m_oWorksheet.Range("A2") Then
I tried to extract the cell reference "A2" from Target, but I can't seem to find a way to do it, unless you write a function to do the conversion of Column/Row to a string reference.
Note the previous answer I provided was very wrong, since I was testing
If Target = m_oWorksheet.Range("A2) Then
... which only worked because the default value properties were identical. This would fall over if any changed cell had the same value as a "watched" cell.

Resources