Using VBA commands to work with Solver Dialogue box - vba

Currently I'm creating a program in excel that runs solver. I have set a maximum time limit on the solver command. If the program exceeds the time limit, a solver dialogue box comes up that asks whether to continue or stop. I was wondering if there was a way to code into VBA to automatically select stop rather than having to have a user click the option.
Thanks in advance!

Yes, you can. You need to set some options on the Solver object. You can read more about it In the MSDN documentation
SolverSolve(UserFinish, ShowRef)
UserFinish Optional Variant. True to return the results without displaying the Solver Results dialog box. False or omitted to return the results and display the Solver Results dialog box.
ShowRef Optional Variant. You can pass the name of a macro (as a string) as the ShowRef argument. This macro is then called, in lieu of displaying the Show Trial Solution dialog box, whenever Solver pauses for any of the reasons listed
You need to define a macro that runs, which must take an integer argument. Here is a code example, straight from the linked page (posted here in case the link is broken in the future)
You call SolverSolve, passing it the arguments above:
SolverSolve UserFinish:=True, ShowRef:= "ShowTrial"
You then need to define the ShowTrail macro that runs, and it must have the correct signature:
Function ShowTrial(Reason As Integer)
'Msgbox Reason <= commented out, as you just want it to end, with no user input
ShowTrial = 0 'See comment below on the return value.
End Function
The return value is important. Return 0 if you want solver to just carry on regardless, or 1 if you want it to stop.
You could then get it to have different behavior on different reasons it finishes. Eg, if it reaches maximum time limit (your case), then stop. Otherwise, carry on:
Function ShowTrial(Reason As Integer)
Select Case Reason
Case 1 '//Show iterations option set
ShowTrial = 0 '//Carry on
Exit Function
Case 2 '//Max time limit reached
ShowTrial = 1 '//Stop
Exit Function
Case 3 '//Max Iterations limit reached
ShowTrial = 0 '//Keep going
Exit Function
Case 4 '//Max subproblems limit reached
ShowTrial = 0 '//Keep Going
Exit Function
Case 5 '//Max feasible solutions limit reached
ShowTrial = 0 '//Keep going
Exit Function
End Select
End Function

Related

VBA - Leave Do Until when specific row is reached

In my VBA Code i go from the last row in my table to the top of my table. But i want that the Do Until Loop ends when it reached Row 10.
Right now I am using this Do Until Loop:
Do
' Do Something
Loop Until ActiveCell.Address = "$N$10:$BI$10"
How do i have to change my code that it will stop when it reached row 10?
Assuming rest of code is correct use:
Loop Until ActiveCell.Row = 10
If that fails you problem is logic in other parts of your code ie. ActiveCell never reaches row 10.
To answer your question in more theoretical fashion. There are multiple ways of exiting Do loops in VBA.
The proper way
1.1. While or Until at the begining
This checks the condition first, if the condition is met, it enters the loop and repeat with the condition being met at the start of every loop.
Do While i <= 5
'#code here
Loop
These two are equivalent.
Do Until i > 5
'#code here
Loop
1.2. While or until at the end
This is almost the same as what is described above. The only difference being, with sole Do statement at the beginning, your code-block always gets executed at least once! This can be particularly useful, when you want to execute something at least once, but don't want it to repeat unless a condition is met.
Do
'#code here
Loop While i <= 5
or
Do
'#code here
Loop Until i > 5
The enforced way
You can exit out of any loop, including Do with the so called Exit statement. This escapes the currently ongoing Do loop upon reaching the statement no questions asked. While you usually should try to avoid using the Exit statement, as in majority of cases it is possible to avoid using it with a proper condition at the While or Until portion of your code, it can come in handy in some cases.
Additionally, keep in mind, inside nested Do loops, Exit always exits only the innermost loop. This means, this would exit only the loop inside and let the others run, acting as a weird form of Continue
Do While (handler = True)
Do
'# execute me
If weird_condition = True Then
Exit Do
' i return to the "handler" loop
End If
field = field + 1
Loop Until field = field_amount
Loop
The not so nice enforced way
Alternatively, you can stop the entire exution, with the Stop statement. I would strongly advise against doing this, but technically it is a possibility so I'm listing it here. Similarly like End it ends the execution, but unlike End (eg. End Sub), it does not close any files or clear any variables - so technically this means you could use it to exit a loop. I would however recommend simply using the Exit statement instead. Can't really think of a case when I would ever use this.

Functions give error only in macro-called dialog

I am writing and re-writing Excel VBA code to make some engineering analysis easier for myself and the others at my company. So the goal is to make it easy for others, not to be integrated into other code or purposes outside our own Excel work.
The problem: a few of the macros will add a custom function to a worksheet cell, bring up the function dialog for inputs, but then when completed, come up with a Value error, stating A value used in the formula is of the wrong data type. Based on this, I believe the issue arises when my function uses a Variant or Range type variable. Some of these functions only give this error via certain input types.
HOWEVER, in every single case, using the function dialog Not in the macro works fine, as does typing the formula in manually. Additionally, all you have to do is double-click on the error cell to edit it, change nothing, press enter, and it's fine!
My macros themselves are as simple as:
Sub NewSTC_dialog()
ActiveCell.FormulaR1C1 = "=sheetname.xlsm!NewSTC()"
Application.Dialogs(xlDialogFunctionWizard).Show
End Sub
My functions have more to them, but the situation is different for different functions, and the functions themselves work fine, so it is preferable to have a solution that applies to the macros rather than within the functions. If I must, I can go through each situation individually or come up with a simplified function that has the same issue to give as an example.
My best workaround so far (I cannot re-find the website were I found this particular solution):
Sub Recalculate()
ActiveCell.Replace What:="=", Replacement:="="
End Sub
Set to a convenient shortcut, and it works great. However, as mentioned above, the goal is to make this easy for everyone else, which would mean not requiring anything additional to have the formula work. Unfortunately, calling this from within the initial macro will not work. In fact, nothing located after the dialog call will run. I tried using On Error Resume Next and it still won't run anything after the dialog call.
Some options searched and tried:
How to refresh cells in Excel 2010 using VBA? and links within that - as mentioned, nothing located after the dialog call will run, thus won't reach these adjustments. Putting before the dialog call also doesn't help. Putting the dialog call in between the false and true statements makes the false apply, then never runs the true statement.
http://www.excelforum.com/excel-general/471632-formula-do-not-work-until-edited.html - Excel is not set to manual
http://blog.contextures.com/archives/2012/02/16/excel-formulas-not-calculating/ - SUMIF is not involved
This is all on Excel 2010, Windows 7 Professional.
EDIT:
Here is an example that creates this behavior. This is similar to the behavior of two of my functions, but not to all of them, so again, I would much prefer the solution be in the macro, not the function. This is just for anybody else's testing purposes.
Function LetNum(input1 As Variant, input2 As Variant) As Integer
If input1 = "A" Or input1 = "a" Then
input1 = 100
ElseIf input1 = 10 Then
input1 = -10
End If
If input2 = "B" Or input2 = "b" Then
input2 = 200
ElseIf input2 = 10 Then
input2 = -1000
End If
LetNum = input1 + input2
End Function
Sub LetNum_dialog()
ActiveCell.FormulaR1C1 = "=LetNum()"
Application.Dialogs(xlDialogFunctionWizard).Show
End Sub
In this situation, typing A and 2 into the dialog box in that order works, but 2 into the second then A into the first doesn't. Additionally, typing B in the second box never works. In all situations, clicking to edit, changing nothing, and hitting enter, fixes it. Additionally, any situation in the dialog box if you bring it up without the macro works fine.
Also, I'm aware this case works better if you input it with quotes, but excel automatically changes it correctly, and not all my functions are string issues.
EDIT 2:
Tim's answer is a great step in the right direction! However, if anybody can come up with a solution that doesn't involve ignoring all errors, that would be more ideal due to some desired error-throwing being ignored. I will look into handling errors differently in the meantime.
The issue seems to be that any error triggered in your UDF effectively "disables" the Function Wizard: note that in cases where you get #VALUE! error, the Show() method never returns any value, and any lines following the call to Show() are not executed.
The only thing which resolved this in my testing was to add a line to the function which skips any errors when the Function Wizard is open:
Dim bSettingUp As Boolean
Function LetNum(input1 As Variant, input2 As Variant) As Integer
If bSettingUp Then On Error Resume Next
If input1 = "A" Or input1 = "a" Then
input1 = 100
ElseIf input1 = 10 Then
input1 = -10
End If
If input2 = "B" Or input2 = "b" Then
input2 = 200
ElseIf input2 = 10 Then
input2 = -1000
End If
LetNum = input1 + input2
End Function
Sub LetNum_dialog()
ActiveCell.Formula = "=LetNum()"
bSettingUp = True
Debug.Print Application.Dialogs(xlDialogFunctionWizard).Show()
bSettingUp = False
End Sub
I'm aware that you'd rather not alter your functions ,but this was the only work-around I could find.

Code Execution Sequence

I am a PLC programmer who is currently using a variant of VB to control a motor.
I want to call a function that will execute moves and not return to the main code until the move has been completed. Currently here is what I have:
Program 'Main Program
While 1
If move_req = 1
Function MoveMotor
End If
Wend
End Program
Function MoveMotor
MoveABS 10 ' Move to encoder position 10mm
move_complete = 1
While move_req = 1
'Do Nothing
Wend
End Function
For some reason this code isn't working and the move command is being sent over and over again. Could this be because the main program continues to run when the function is running? Is that how VB works? I am used to thinking of code sequence in terms of PLC's where they scan through everything repeatedly at a certain frequency.
Whenever the move is complete, there must be a way for it to be detected by the program. It looks like you want move_req to be set to zero when this happens, but I cannot see what would cause that. How does the machine signal the program that it's finished moving?
A second point is that when you have a loop that waits while it checks for variable change, it can cause a CPU spike. You can put a pause in the loop with some thing like System.Threading.Thread.Sleep(100) where 100 is milliseconds to pause.

VBA Compile error

I have very little experience with VBA and the last time I used it was years ago.
Heres my VBA:
Function MacIDGen(total, current)
If total - current = 0 Then
current -1
Else
current 1
End If
Macro1 (current)
End Function
Sub Macro1(cur)
Sheets("Donations").Cells(I2).Value = cur & "Test"
End Sub
It's its own module, no other code in with it at all.
So I have a cell which is calling MacIDGen(). It's passing MacID two other cell values. The error comes whenever it's executed, "Compile Error: Expected Sub, Function, or Property" and highlights the functions method signature.
I have no idea why its kicking this up here, I'm guessing its because I've either missed a major step or something about how you can't have a function in that context or some other such issue.
EDIT
As a little extra info, I'm creating these spreadsheets in Excel to serve as random data generation sheets to be imported into a drupal site. I've encountered a problem that cannot be resolved without the use of loops (I also can't hard code the number of iterations either), which formulas don't do.
Regarding the Compile Error, you are getting that because you forgot to use the = sign in current -1 and current 1
Like I mentioned, there are many errors but the main error is that you cannot use a UDF to write to another cell. It really doesn't matter if you break that function into functions or subs. You simply can't write to any other cell. You will get a #Value in the cell where you are using that function.
A Function is used when you want to return something. Now since you are using this as a UDF (user defined function), it should return something and to that particular cell.
If you are writing to the same cell where the function is called from then change your code to
Function MacIDGen(total, current)
If total - current = 0 Then
current = -1
Else
current = 1
End If
MacIDGen = current & "Test"
End Function
If you want to change a cell you can use a sub procedure instead of a UDF, in that case you will need a way to execute it from your spreadsheet such as a commandButton, a ribbon button or a key combination shortcut.
If so, your first line should be:
Sub MacIDGen(total, current)

VBA: Jump to the beginning of a for loop

I have a little problem with jumping back to the start of a for loop.
Part of the code:
For M = 1 To System
Openhours = Numberofhours(M, 1)
If Openhours = 0 Then
M = M + 1
Exit For
End If
I can have 1-6 "Systems", more could be added. "Openhours" says how many hours is dedicated to a particular system, but the problem is that if I happen to have a system (say system number 3) where there are no hours dedicated to at the moment, then the system crashes as, for example "Openhours" is zero with quite a few other arrays.
So I'd need to tell the code that immediately if it notices that Openhours is zero that it goes back to the beginning of the loop and tests the next system.
"Exit For" did of course not work as it skipped all the other systems after it found the first system with zero hours.
You could either check for Openhours being NOT equal to 0 and only execute your code in those cases, thus eliminating the need to jump to the next iteration or you could use the NEXT-keyword to jump to the next iteration of your loop.