Excel development: How to detect that a Range overlaps another Range? - excel

I am working on a VSTO Excel project and I am having some troubles managing Range objects.
Actually I have case where I need to know if the current selected range overlaps another Range that I stored in list. So basically, I have 2 Range instance and I want to compare their position.
This seemed simple pretty simple to me but with all the Interop dynamic stuff I am bit confused concerning which data I should rely on.

Application.Intersect returns a range that is an intersection of the provided ranges, or null if they don't overlap.

Related

Get Current Region with Office-JS

How do I get the current region surrounding the ActiveCell using the Excel JS API?
In VBA this is
Set rng=ActiveCell.CurrentRegion
The current region property in the JavaScript API has now been implemented. The property is called getSurroundingRegion()
There is no direct equivalent, but we do have a range.getUsedRange() that will take an existing range and give you a smaller range that represents the non-empty portions. Note that this method will throw a not-found error if there is nothing in the entire range (since effectively it's an empty range, which Excel can't express).
If you really need the CurrentRegion scenario (and I'd be curious to learn more), you could first get the used range (to ensure you're not loading too much data), then load the values property, and then do range.getExpandedRange(indexOfLastRow, indexOfLastColumn).
BTW, unlike VBA's usedRange, the JS "getUsedRange()" always creates an accurate snapshot of the current used range (the VBA one could get stale), and we're exposing it not just on the worksheet but also on a given range.
Update
What I mean is that there are a couple of scenario, one simpler, the other harder.
The simpler one: you know roughly what range you need, but you just need to trim it. For example, you know you have a table-like entity in columns A:C, but you don't know the row count. That's where
worksheet.getRange("A:C").getUsedRange()
would get you what you need.
The harder one: you use getUsedRange() to trim down what you can, but you then load range.values and manually do a search for rows and columns where each cell is empty (""). Once you have that (suppose you found that the relative row index you care about is 5, and column index 2), you could do
originalRange.getCell(0, 0).getExpandedRange(rowIndex, columnIndex)
Concrete example for the above: You have data in A2:C7, though the getUsedRange() of the worksheet is much larger (and hence my suggestion could try to trim it down further by doing a range.getUsedRange()). But for this case, let's imagine that getUsedRange on a worksheet returned a range corresponding to A1:Z100. worksheet.getRange(0, 0) would get you the first cell, which you can then expand by 5 rows and 2 columns (which you find through simple albeit tedious array iteration) to get the range you care about. Makes sense?

When should one use ".Value"

I'm a beginner with Visual Basic, and mainly use it to edit MS Excel files.
When copying/pasting (Cell) content or other values, in some cases .value is added.
When should this be added?
When not?
Is it necessary?
Could it harm my code if I use it in places where it isn't needed?
In Excel VBA a Range object is a fairly rich thing which corresponds to either a cell or a range of cells. As such it has all sorts of properties (it is in a given row, accessible via the .Row property, it has interior color, possible borderlines, formulas, etc. -- all accessible via the right properties after the dot.) Value is one of these properties. It refers to the value in the cell -- typically a number or a text. It wouldn't be used when using Copy and Paste since those methods are used on whole Range object -- as can been seen by the fact that they are able to copy formatting and not just raw values.
You can assign the value in one cell to another. Even though this looks like copy/pasting it really is quite different and is in some sense a low-tech solution when all you want to do is transfer the values. It can be done using either e.g. Range("A1").Value = Range("B1").Value or Range("A1") = Range("B1"). The reason the later works is that Value is the default property of a Range object -- hence .Value is implicit in any context in which you aren't treating the Range as an actual object.
Personally, I always explicitly use Value when I want to either read or set the value in a cell (or range of cells) even though I could rely on the fact that Value is the default property. Most Excel VBA code makes heavy use of both Range objects and the values in the Range objects. For reasons of readability it is a good idea for your code to be explicit about when it is using the range vs. when it is using the value. If you follow the excel-vba tag on SO you will see that it is relatively rare for programmers to rely on Range's default property.

Using Named Range as single cell references in formulas that accept arrays

So background first, question second.
I recently discovered an interesting property of named ranges that I'm experimenting with and not finding much help. The property is this: If I name a range (a column in this example), I can use the named range as a reference in formulas and it will usually resolve as though it were a reference to the same relative position as the current cell within the named range. So if I call A:A "Alphabet" and it contains letters a through z, each in their own row, I can simply type =Alphabet in cell b26 and it will evaluate to "z" (i.e. A26 instead of a:A). Seems simple, but it is shaping up to be quite powerful, because there is essentially an index function built-in. Very useful for making tidy formulas.
Onto the issue: when I use this same technique with a range that accepts an array argument (i.e. MAX, EOMONTH, etc.), the reference is resolving in the standard way (Max(A:A)). If I wrap the reference in VALUE(), then it resolves to just the single reference within the larger range (a26). The question is simply can anyone think of any way to avoid needing to do this, or at least to make the wrapper as unobstrusive as possible?
Real world example: I have a list of employees and I want to determine which date from three named ranges is larger. So I have something like
='DateSameRow1' > Max('DateSameRow2','DateSameRow3','DateSameColumn', and it is resolving as =a10 > Max(b:b,c:c,2:2). Note the issue: The named range outside of MAX resolved one way, and inside MAX resolved another. Sure, I could just write = a10 > Max(b10,c10,d2) or whatever, but this is so much more readable in what will end up being a huge formula. Right now I'm having to write MAX(VALUE('DateSameRow2')) or whatever to get the result I want and it is defeating the purpose.
Thanks
You can use --NAMED_RANGE in front of the "offending" named range and it will force a negative VALUE and then undo the negative.
Per my comment, I would recommend you not build a spreadsheet on this functionality. The -- is even further removed from VALUE in indicating that a named range is being used in this way.

Modifying a Dynamic Name with VBA

I have a spreadsheet that links to several other spreadsheets to pull data that compiled into one sheet; I am tasked with modifying the sheet to graph this data. There is a row of 12 slots for each month in a person's report; if a person was working in a month then the month will be displayed in the appropriate slot. For the graph I have figured out to use a dynamic name range so that the number of months in the graph is equal the number of months present in the line; the only problem is that the start pointing changes based on what month a person started.
I am able to make a rather large nested if statement but that doesn't seem like a very good way to approach resolving my problem. Is there a way I can have a VBA script change the value of the dynamic name range or just modify the chart's value directly? If this isn't a viable option is there any other way to approach this problem other than a dynamic named range?
I can't change how this report sheet behaves as this is a spreadsheet related to work and that's how they want it to be for reporting reasons; I'm simply looking to add the graphing functionality.
EXAMPLES:
https://www.dropbox.com/s/zn6yt4l6kjvwq33/Example1.xlsx
https://www.dropbox.com/s/j88tgoik68s4lhx/Example2.xlsx
In the first example you see an example of the problem when you select "Second" from the dropbox next to Agent Name. It's including pointless data in the graph.
In the 2nd example I have resolved the problem by adding an if statement to the dynamic named ranges used in the chart; but as you can see to include 2 months it's now massive and to include all 12 it would be pretty much impossible. If I were to add a 3rd person who started in March they would also not work in the 2nd example.
Is there a way I can make it so the values in the Dynamic name range are changeable through VBA? Can I modify the chart's values directly using VBA? Is there a way to bypass this problem without even using VBA?
Thanks.
(I'm not 100% certain I've understood the problem, but this might at least help to get closer...)
Try defining three names, something like this:
start_month
data
chart_data
Where start_month contains the number of the column that you want to be charted, data is all twelve columns of data, something like =OFFSET($C$1,0,0,COUNT($A:$A),12) and chart_data selects the column you want from the data, e.g. =INDEX(data, , start_month)
Now set your chart's series to WorkbookName!chart_data, using whatever your workbook's called. As the start_month value changes, so should the values displayed in the chart.
You can add or modify a Name in VBA like:
Dim nm As Name
Dim rng As Range
'Here, you could use a more complicated expression'
' to derive the proper address dynamically.'
Set rng = Sheets("Sheet1").Range("A2:A25")
'Now, add a Name to the workbook:'
Set nm = ActiveWorkbook.Names.Add("My_Name", rng)
With some string functions/variables, you could derive/caclulate the range address in VBA.

Increase Range dimensions

i have a named range that refers to range D3:I23 and this range is well-defined for some automation purposes.
Recently i had an update that required me to redefine this range as F3:I23 and exclude, initially, columns D & E. But further in the various logic coding, i need to include E for evaluation (turning dynamic data to static data).
Was thinking of using Resize but didnt seem right. Also thought Offset but that moves the whole range forward or backwards. I basically need to resize the range back 1 column while retaining the original defined range
In essence i need the named range to be defined as F3:I23 but during this one code segment i need the range to be evaluated to E3:I23.
Any thoughts, or combination of Range properties to use in VBA? At the point that i am passing the reference, it is being stored in a Range object, so any chained set of properties is fair game.
Please try to apply the KISS policy when answering. Doesnt need to be an overly complex formula, as i am not guaranteed to be the one supporting the end result.
As Rachel indicated... this should do the trick assumes your named range is defined as namedRange:
Set neededRange = namedRange.Resize(namedRange.Rows.Count, _
namedRange.Columns.Count + 1).Offset(0, -1)
Resize to increase the columns included by 1, then offset the entire range by -1 column to get your neededRange.

Resources