I have an Excel doc that starts with some fields that come from calculations done on the rows below it. To do the calculations I currently have a module with about 4 functions that loop through rows 20 through N(first blank cell). These functions are called straight from the cells at the top of the sheet. The problem is that the calculations at the top are not updating whenever someone adds/removes data from the rows below. How can this be accomplished?
If your functions are Excel VBA user-defined functions called from worksheet cells, then you will get this not-recalculating behaviour if the UDF refers to cells that are not in the input parameters of the UDF.
If this is the case a good solution would be to define some Dynamic Named Ranges that expand/contract as data is added/deleted and use them as input to the function.
Another solution would be to add Application.Volatile to your UDF, but this has the undesirable side-effect that your UDFs will be recalculated at every calculation which can be painfully slow.
If I understand your question correctly, you can use Worksheet_Change event to accomplish such tasks.
In your sheet module, add a Worksheet_Change event:
Private Sub Worksheet_Change(ByVal rngChanged As Range)
' Call your subs and functions here
MsgBox "You just changed something!"
End Sub
Note that the Worksheet_Change sub must have one and only one argument of type Range. Excel will make it a reference to the range that was changed by the sheet user. If you want to observe its behaviour, try placing this line in the sub:
rngChanged.Interior.ColorIndex = 4
Read more e.g. here.
Related
I am looking for the best way to "link" two worksheets together. I have a main worksheet where information and data is added and then I would like to have two basic worksheets that draws columns from main spreadsheet. Whenever changes are made to the main sheet, they will also occur in the other two sheets. I was thinking of trying to activate a macro which automates hitting ctrl or shift and the two tabs like the below
sheets (Array("Main", "Summary")).select
But this failed to work as well. Its too much information for vlookups or anything like that so if anyone knows a way to have changes that occur in one sheet effect multiple other ones it would be much appreciated.
Thanks!!!
You have the worksheet.Change event for that. Something simple like the following:
Private Sub Worksheet_Change(ByVal Target As Range)
ThisWorkbook.Sheets("sheet2").Cells(Target.Row, Target.Column).Value = Target
End Sub
Will copy the value on every cell change from one sheet to another (the code goes in the sheet's private module).
I use popular custom function which I found on the internet on one of vba blogs. It reads text cell as if it be formula (i.e. "=A2+B2" or "=ABS(A2+B2)", I created them by using CONCATENATE function, in workbook they are without quotation marks). The code goes:
Function Eval(Ref As String)
Application.Volatile
Eval = Evaluate(Ref)
End Function
In my workbook I have multiple sheets which are exact same copies except 2 columns of data, which calculate some descriptive statistics (i.e. sum, average, standard deviation etc.), the formulas are in my source sheet, copied sheet have formula like: Eval(sourceSheet!A1) and A1 contains text of formula like above. The problem is that to apply macro after loading it, I need to pres F9 to refresh (sometimes several times). It makes refreshing ALL my copied worksheets with the data I have in worksheet I am refreshing. So for example: if i refresh in worksheet 3 and sum of data was 5 it changes sum to 5 in all other sheets. I guess somehow my code makes the function applied to entire workbook instead of single worksheets like every other excel function.
So I have 2 questions:
Is there any way to change the code so my custom function will only apply to worksheet I put it in?
Can you post me a macro code for refreshing entire workbook with every single click of left mouse button?
Thank you in advance
Thats a rather buggy UDF: Evaluate always takes unqualified references as referring to the active sheet.
You really need to use Worksheet.Evaluate. Try something like this that assumes that any unqualified references in Ref are to the sheet that Ref is on
Function Eval(Ref As range)
Application.Volatile
Eval = ref.parent.Evaluate(Ref.value)
End Function
Or if you want it to refer to the sheet that the UDF is being called from try this
Function Eval(Ref As variant)
Application.Volatile
Eval = Application.Caller.Parent.Evaluate(Ref)
End Function
There are also a number of strange things/Quirks/bugs with evaluate you should be aware of: see my blog post
https://fastexcel.wordpress.com/2011/11/02/evaluate-functions-and-formulas-fun-how-to-make-excels-evaluate-method-twice-as-fast/
I am writing a fairly lengthy macro in Excel VBA. I want to use named ranges instead of specifying it in the macro. This macro is intended for long-term use. What happens if the range shifts by another user? How can I adjust my code so my named ranges can accommodate for changing positions?
I (personally) hate named ranges. Especially, when you are copying or pasting sheets / ranges from one file to another you always end up with dead-references or copied over named ranges which do not work anymore or got renamed (because they existed already in that file).
My solution to this is one of the following two:
(1) I dedicate a certain part (or even module) in the VBA to declaring my ranges in global variables. This is very similar to the Dim of all variables at the beginning of each sub.
'*********************************************************
'** Declaring all ranges and where to find which data
'*********************************************************
Dim rngNamedRangeName As Range
Sub SetupAllGlobalVariables()
Set rngNamedRangeName = ThisWorkbook.Worksheets(1).Range("A1:C10")
End Sub
'*********************************************************
'** After that all your normal subs follow and whenever
'** necessary you can call the above to get your ranges
'*********************************************************
Sub ExampleCodeToFormatYourRanges()
Call SetupAllGlobalVariables
With rngNamedRangeName
.Interior.ColorIndex = 36
End With
End Sub
(2) Yet, my preferred second solution is to have a separate very hidden sheet where I reference / link all the ranges (which are important to me) again. So, basically, I have in this separate sheet all the "important" data again. This would be your named ranges. But nobody is allowed to touch this sheet (that's why its very hidden). If any of your ranges get shifted or changed then it is easy to re-link the ranges on this hidden sheet with the other sheets again. Yet, on the hidden sheet all data is still in the same spot and allows you to hard-code all ranges in your VBA (taken from the hidden sheet only).
Even non-VBA programmers can normally fix such things with the second method. With the first method you'll probably always need someone with VBA skills to fix it.
Note, the above is not the one and only solution nor might it be the best solution. But I can certainly say that this has proven to be a usable solution even for larger corporations.
I have a worksheet where in the first three columns (A,B,C) I store data. These values are used in a macro.
I would like to know how it is possible to make this macro run automatically after data is pasted onto these columns. I am almost sure that I will use the Worksheet-Change module, but as for the code I am clueless.
Thanks in advance.
A simple implementation:
Private Sub Worksheet_Change(ByVal Target As Range)
If Not Intersect(Target, Range("A1:C" & ThisWorkbook.Worksheets(1).UsedRange.Rows.Count)) Is Nothing Then
'Call your Macro to do stuff
End If
End Sub
Intersect checks if Target is in the range you want to monitor. So if something changes in columns past C, Intersect will return Nothing and your macro won't be called. Just keep in mind that the Worksheet_Change event fires on any change, even double clicking into the cells. If all you are doing in this Worksheet is copy&pasting data and then running your Macro, this should be fine, but if you are manipulating your data further, you might have to look at more sophisticated solutions. Examples include mirroring your Worksheet and comparing it pre/post Worksheet Changed Event. You can read more on this here: Detect whether cell value was actually changed by editing
I've got a code written that categorizes employees along with their qualifications. In order to weed out employees with unwanted qualifications I have applied a filter to each column that titles the category of their qualification.
I've written my VBA code in order that repetitious names and qualifications are made invisible for ease of location. However, I am unable to get the code to run automatically.
Currently the only way I can get the code to run is by setting it to
Private Sub Worksheet_Change(ByVal Target As Range) and then changing the value of an arbitrary cell.
i found what I believe to be the correct solution at:
http://www.ozgrid.com/forum/showthread.php?t=72860
But I cannot make sense of it.
Is there a way to run this code without having to select and deselect a cell after the filter has run?
The key points from my article Trapping a change to a filtered list with VBA
There is more detail and a sample file with the article, the key points are summarised below
A "dummy" WorkSheet is added with a single SUBTOTAL formula in A1 pointing back to the range being filtered on the main sheet.
A Worksheet_Calculate() Event is added to the "dummy" WorkSheet, this Event fires when the SUBTOTAL formula updates when the filter is changed.
The next two setps are needed if it is desired to run the Workbook Calculation as Manual
Add a Workbook_Open Event to set the EnableCalculation property of all sheets other than "Dummy" to False.
Run the Workbook in Calculation mode
The ozgrid code you mentioned tells you that you can put your code in a worksheet_calculate event (in the worksheet module), as long as you have something that will recalculate when you change your autofilter. This something can be a subtotal formula that you can hide in your worksheet, e.g. =subtotal(3,A:A)
Still need to investigate but looks like Chart Calculate event is triggered when Calculation = xlCalculationManual. At least works on my Excel 2007. So the steps are:
create a chart (saying "Chart 1" on Sheet1) which actually uses data from any of your table column
check that it updates its picture when you change the filter
create a new class e.g. clsChartEvents:
Public WithEvents Chart As Chart
Private Sub Chart_Calculate()
Stop
End sub
add this code to some module or class:
Private chartEvents as new ChartEvents 'create a module-scope variable
sub SubscribeToChartEvents
set chartEvents.Chart = Sheet1.ChartObjects("Chart 1").Chart
end sub
execute SubscribeToChartEvents
change a filter and you should appear in Sub Chart_Calculate()