Run Solver NOT on active sheet (VBA) - vba

I have a workbook that I am using to solve a system of non-linear equations, and then display the solution on a chart. The chart has a large amount of content (basically the result of solving the system of equations for a few standard cases), which references a table of about 1200 cells. That content is static, and so for aesthetics I keep the calculations table on sheet1 and graph on sheet2.
Then on the graph sheet I allow user input to two cells which are referenced in the calculations on sheet1 to build three dynamic instances of the system of equations. Pretty lightweight for solver to handle one at a time.
I use a Worksheet_Change event on sheet2 to watch for the user to modify those two cells and then it calls a sub that runs the solver.
The event code:
Private Sub Worksheet_Change(ByVal Target As Excel.Range)
If (Target.Address = "$C$3") Or (Target.Address = "$D$3") Then
Application.ScreenUpdating = False
Application.EnableEvents = False
SolverEQN 178, 180
Application.ScreenUpdating = True
Application.EnableEvents = True
End If
End Sub
The Solver code:
Sub SolverEQN(rw, rwend As Integer)
Dim iter As Integer
For iter = rw To rwend
If Sheets(1).Cells(iter, 14) <> 0 Then
SolverReset
SolverOptions AssumeNonNeg:=False
SolverOK SetCell:="'Sheet1'!$N$" & iter, MaxMinVal:="3", ValueOf:="0", ByChange:="'Sheet1'!$H$" & iter
SolverSolve True
End If
Next iter
End Sub
Here is where the problem is encountered. Solver runs on sheet2, despite my assigning it to run on sheet1, meaning the system of equations don't exist where they should and solver quickly optimizes to 0 = 0 in the middle of my active sheet.
There are two workarounds that I've found:
1 - Set my activesheet to be sheet1 before running solver and then set activesheet back to sheet2 after solver runs. This causes a short pause and screen flicker, so the experience would not be 'seamless' for a user.
2 - Solve the dynamic system of equations on the graph sheet and hide them in a crude way (white font, stuff them behind my graph, YUCK!).
I will handle this with option 2 if I have to, but do you creative folks have any ideas on how to keep the calculations on my calculations sheet?
Input much appreciated.
Cheers.

Related

Mirroring Sheet1 to Sheet2 for Interior Color only, through VBA

I have a schedule showing a lot of information. I would like to condense this onto a second sheet that displays the fill color only and none of the values.
I want that any fill color changes are automatically copied from sheet1 to sheet2.
I want the code to work with a specific cell range as they differ from both sheets, (Sheet1 is "D8:QP27) & (Sheet2 is B3:QN22) and to get it to mirror at all.
Sheet1 showing all information
Sheet2 showing fill (Interior.Color)
It looks as if you also want to copy the borders (eg the diagonal border of column I), so I would suggest you use PasteSpecial with the option xlPasteFormats. See https://learn.microsoft.com/en-us/office/vba/api/excel.range.pastespecial
With ThisWorkbook
.Worksheets("Sheet1").Range("D8:QP27").Copy
.Worksheets("Sheet2").Range("B3:QN22").PasteSpecial xlPasteFormats
End With
Update: As you are looking for a trigger to copy the format automatically. First step is to create a subroutine:
Sub copyFormat()
Application.ScreenUpdating = False
With ThisWorkbook
.Worksheets("Sheet1").Range("D8:QP27").Copy
.Worksheets("Sheet2").Range("B3:QN22").PasteSpecial xlPasteFormats
Application.CutCopyMode = False
End With
Application.ScreenUpdating = True
End Sub
Now you need to find a way to call the code, eg
o call the routine from the Worksheet_SelectionChange-event (drawback: as this is rather slow, it could annoy the user)
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
copyFormat
End Sub
o Place a button on the sheet to call the routine.
o Use a Timer to call the routine every n seconds (see https://learn.microsoft.com/en-us/office/vba/api/excel.application.ontime). Plenty of examples on SO and elsewhere

VBA - copy specific rows from one sheet into another sheet when user closes file

I'm new to VBA and I'm struggling a lot with a file I want to build.
I have a main Sheet that in a simple way looks like this (starting at column B - A is an empty column):
main
This is a simplified version just for the example. The first table of the sheet varies from B13 to O92, the second varies from B104 to O114 but some of those rows might be empty.
My goal is to join rows with content from the first area with rows with content from the second area in a different sheet (Sheet1), add to the left a column with 1s and "Cell 0" (content of cell B1). Using the example, the result would be something like this:
Sheet1
Sheet1 will stay hidden as I'm using it as a source of information to a different file. In fact, I may not need the 1s column if I find a way to copy information in a different way - I'm doing it like this (wsSource is Sheet1):
lRow = wsSource.Columns("A").Find(1, SearchDirection:=xlPrevious, LookIn:=xlValues, LookAt:=xlWhole).Row
wsSource.Range("B1:N" & lRow).Copy
I was trying to do it so Sheet1 is "emptied" when the file is opened and edited when file is closed - so that if new rows are added or information updated, it gets into Sheet1 every time.
I've tried several stuff I found online but couldn't make it to work. My main problem is adding the specified rows one after the others but I'm also struggling to reset Sheet1 every time the file is opened and automatically running the macro when file is closed.
Any help would be really appreciated.
Hopefully this will get you started. Both subs need to be pasted in VBE under ThisWorkBook rather module or sheet(n).
The first sub will execute when the workbook is open.
Are you sure you want to clear your sheet under these circumstances?
You will never have access to your table (without workarounds) since it will clear when opening every time.
If this is what you need, see the below method to clear a dynamic range (Col A - D down to last cell used in Col A) on Sheet1 every time the workbook that houses this code is opened.
Private Sub Workbook_Open()
Dim ws As Worksheet: Set ws = ThisWorkbook.Sheets("Sheet1")
Dim ClearRange As Range
Application.ScreenUpdating = False
With ws
.Visible = xlSheetVisible
.Range("A1:D" & .Range("A" & .Rows.Count).End(xlUp).Row).ClearContents
.Visible = xlSheetHidden
End With
Application.ScreenUpdating = True
End Sub
Next, the below sub will only execute before the book is closing. This is where the bulk of your code will go to build your table.
Private Sub Workbook_BeforeClose(Cancel As Boolean)
Application.ScreenUpdating = False
ThisWorkbook.Sheets(1).Visible = xlSheetVisible
'Code goes here
ThisWorkbook.Sheets(1).Visible = xlSheetHidden
Application.ScreenUpdating = True
End Sub
You will need to qualify your objects (Ranges, Cells, etc.) directly (which sheet) since the code will not be housed in a sheet. The first sub uses a With block to qualify ranges and the second sub directly qualifies all objects with ThisWorkbook.Sheets(1).
Lastly, I recommend writing your code to build your table inside a module (qualify ranges!) so you can test and debug without needing to close/open your book continuously. Once you have the code working in a module, you can copy and paste the code directly into the 2nd sub here and you are good to go!

Removing all $ from formulas in Excel

This is a hack-y Excel question, certainly not standard procedure. Apologies in advance.
Inside a workbook, I have a financial model. It spans three sheets: Revenue and Expense, which both read information from Inputs. Together, the sheets form a complete model.
I want to expand my workbook by creating a duplicate model alongside my first one. The idea is to have side-by-side scenarios that I can compare. 2 sets of inputs, 2 sets of expense calcs, and 2 sets of revenue calcs, each set side-by-side on its respective tab.
Normally, I would just copy the formulas over and 'bam' I've got a duplicate model. Unfortunately, I can't do this because I used a ton of $ characters, locking my cell references. Copying the formulas in Revenue off to the right wouldn't change which cells the formulas reference on Inputs tab. The model is large enough that it would take hours to remove the cell reference locks from each formula manually.
My current plan is to use VBA to remove all of the $ characters from formulas and then go ahead with the copy pasting method.
Will this work?
How can I remove a specific character from formulas using VBA?
You can press ctrl+' this will change all the cells from showing their value to showing their formula. In this view you can press ctrl+H to replace all $ with nothing and click "Replace All".
So no need to code it all in vba, which would have been possible too, but probably a bit more complicated.
This will remove all the dollar signs you wanted to remove.
Pay attention though if you make any edits extrapolating formulas in the new dollar-sign-less sheet, that it will probably be incorrect for it will also extrapolate the set references which should contain a $.
For removing the $ in external link paths in Excel -
Sub ExtLinks_RelativePaths()
This macro converts external links in selected cells to relative links by removing the $ from the cell reference
' Source: todd.kluge#merrillcorp.com
Dim myCells As Range, curCell As Range
Dim myVal As Boolean
Application.DisplayAlerts = False
Application.EnableEvents = False
Application.ScreenUpdating = False
Set myCells = ActiveSheet.UsedRange
On Error Resume Next
For Each curCell In myCells
curCell.Select
myVal = IsFormula(curCell)
If myVal = True Then
With Selection
.replace What:="$", Replacement:="", LookAt:=xlPart, SearchOrder:= _
xlByColumns, MatchCase:=False, SearchFormat:=False, ReplaceFormat:=False
End With
End If
Next curCell
Application.DisplayAlerts = True
Application.EnableEvents = True
Application.ScreenUpdating = True
MsgBox ("Formulas on the active sheet now have relative references.")
End Sub
Function IsFormula(cell_ref As Range)
IsFormula = cell_ref.HasFormula
End Function

Excel still recalculates when hiding a row despite CalculationManual

Excuse me if duplicating, but couldn't find anything in regards to my problem on the internet.
So I have:
Private Sub Worksheet_Change(ByVal Target As Range)
Application.ScreenUpdating = False
Application.Calculation = xlCalculationManual
Then somewhere down the script there is
For Each sheet In sheets
If sheet.Cells(i, ColBrand).Rows.Hidden Then
sheet.Cells(i, ColBrand).EntireRow.Hidden = False
End If
Next
, where ColBrand is just a column number defined as a constant integer in the beginning.
And so it goes through an array of worksheets hiding rows where necessarity doing its job just fine.
Problem is, despite CalculationManual, "calculating: xx%" still pops up in a status bar occasionally. Which, alongside with the speed of execution, leads me to thinking Excel recalculates the entire Worksheet (possibly Workbook) after hiding each row. Which is sad.
Any ideas?
Thank you!
Now I'm thinking, could it be because I have a couple of VBA written Application.Volatile functions on one of the Worksheets?
I do have this function, which gets called under a condition, which triggers WorkSheet_change event. If this is of any help
Function get_rand(floor As Long, ceiling As Long, exceptions() As Long)
Dim rand As Long, _
position As Variant
If UBound(exceptions) - LBound(exceptions) + 1 >= 999999 Then
get_rand = "error"
Else
Do Until IsError(position)
rand = CLng(((ceiling - floor + 1) * Rnd() + floor))
position = Application.Match(rand, exceptions, False)
get_rand = rand
Loop
End If
End Function
Oh btw the code above interacts with sheets which have WorkSheet_Change routines themselves. Target.Adress 'es of these do partly intersect but get neither changed nor updated by the code above. Could it be the cause?
This MS Document discusses this problem.
It states "The user can trigger recalculation in Microsoft Excel in several ways, for example:" ... "Deleting or inserting a row or column."
I think you've stumbled upon a documented "feature".

Searching a range of columns defined by variables in VBA

I'm trying to search a range of columns on a different worksheet, where the range is defined by two separate variables. I have successfully been able to use the same code to search a range of columns that I manually inputted, but using variables result in an error:
Run-time error '1004':
Application-defined or object-defined error
I am using the code to search a separate worksheet for the column number of the first instance of the month and then search a range beginning with that column number for the specific day.
An example of the worksheet I'm searching through:
http://i.imgur.com/ljmmGGi.png
Below is the code. Specifically, the MonthFind function has worked perfectly, but the subsequent DayFind function, which uses output from MonthFind is acting up.
Private Sub ComboBox21_Change()
Dim i As String
Dim j As String
i = "February"
j = 9
Dim MonthFind As Variant
With Sheets("Project Schedule").Range("A1:ZZ1")
Set MonthFind = .Find(i, LookAt:=xlWhole, MatchCase:=False)
End With
Dim Month1 As Integer
Dim Month2 As Integer
Month1 = MonthFind.Column
Month2 = MonthFind.Column + 12
Dim DayFind As Variant
With Sheets("Project Schedule").Range(Columns(Month1), Columns(Month2))
Set DayFind = .Find(j, LookAt:=xlWhole, MatchCase:=False)
End With
End Sub
Any help would be much appreciated, I've been trying so many different variations of this code to no avail!
Edit - Link to Excel file: https://www.dropbox.com/s/275fo0uucfeum3y/Project%20Scheduling%20SO.xlsm?dl=0
I almost gave up on this, but I found out what the problem was.
Your ComboBox_21 object has an input range (which will fill in the combobox with the selectable values) on the Inputs sheet that uses a bunch of formulas that reference the Project Schedule sheet. Whenever you do all those copy/paste functions against the range in Project Schedule that the combobox relies on, you are effectively changing the data for the dropdown box, and in turn, causing the _Change() event to fire off with every paste that effects that area.
This isn't always a problem (though in my opinion, it's causing a lot of unnecessary code execution), but there's this bit of code that is causing an issue in your AddJob1_Click() event:
Range(Sheet1.Cells(erow, 5), Sheet1.Cells(erow + 3, 10000)).ClearContents
Apparently you're not allowed to perform the Range.Find() method when the contents of the cells that the combobox rely on are being changed.
Here's some info about that, though it's not terribly helpful:
https://msdn.microsoft.com/en-us/library/office/aa221581(v=office.11).aspx
So that's the why, now how to fix it:
Defer automatic calculation until your UserForm code is finished doing whatever it needs to do.
This will ensure that you are allowed to do the Find's and referencing you need to do.
Private Sub AddJob1_Click()
' turn off automatic calculation
Application.Calculation = xlCalculationManual
erow = Sheet1.Cells(Rows.Count, 1).End(xlUp).Offset(1, 0).Row
' ... other stuff
' turn calculation back on and perform a calculate, which will fire off the ComboBox21_Change() event
Application.Calculation = xlCalculationAutomatic
Application.Calculate
Unload Me
End Sub