Charting A Mathematical Equation Using Excel and Defined Names
Charting A Mathematical Equation Using Excel and Defined Names
Introduction
When doing mathematics, wouldn't it be nice if we could type an equation into a cell in Excel and immediately see the resulting graph? Suppose we have an equation like: y=x2-2.x+6 Normally, we would create a column with x values and in the adjacent column "translate" this formula into a cell formula: =A2^2-2*A2+6 and copy this formula down to match the number of x values in column A:
Enter the (any!) formula into a single cell Enter the lower and upper limit of the x-range into two other cells Enter the number of values to use for the chart in yet another cell:
Yet another constraint we're going to place on this task: Apart from the cells shown in the figure above, no cells are to be used, just defined names with formulas.
and click the chart wizard button. Make sure to select the "XY (scatter)" chart type and hit Finish. Now define two names local to the worksheet the chart is on: Name Refers To
Sheet1 $A$12:$A$ !x 15 Sheet1 $B$12:$B$ !y 15 Now we need to use these two defined names in the chart's SERIES formula (click the line in the chart and then click the formula bar), currently looking like this:
(Note this shows the SERIES formula as I see it, with the semicolon as list separator.) Edit this formula so it looks like this: =SERIES(Sheet1!$B$8,Sheet1!x,Sheet1!y,1) Cell B8 will contain the chart's title, so we'll put this little formula in B8: ="Charting: " & Sheet1!$B$1
Getting x values
The next task is to get a list if x values we can use for the x-axes of the chart. A little known fact, is that when a defined name is used to name a formula this formula will be an array formula by default. We're going to put that to use.
First we need a set of incrementing numbers. We'll use the ROW worksheet function for that. When we define this name local to worksheet Sheet1: Name: Sheet1!x RefersTo: =ROW(1:20) and we enter an array formula into cells A1:A20 (select A1:A20 in Sheet1 and type =x, then hit control-shift-enter) we get this result:
But we want the number of x-values to be dependent on a number entered into a cell. We'll use the OFFSET worksheet function for this purpose: =ROW(OFFSET(Sheet1!$A$1,0,0,20,1)) If we replace the previous formula in "Sheet1!x" with the one above, the result will remain the same as shown above. All it takes now is replace the fixed 20 with a cell reference: =ROW(OFFSET(Sheet1!$A$1,0,0,Sheet1!$B$5,1))
Now we have the numbers 1 to 20 (or up to whatever number we enter into cell B5 on sheet1). But we need more flexibility, we want to be able to set a minimum and a maximum value for x and use the 1-20 range to space out these limits. Let's define these named ranges: Name xStart Refers To =Sheet1! $B$3 =Sheet1! $B$4 =Sheet1! $B$5 =xEndxStart
To get a series of "xNumberOfPoints" from xStart to xEnd, the following formula applies: xPoint=xStart+xRange/(xNumberOfPoints-1)*Counter(1 to xNumberOfPoints-1) So applying the approach depicted above: =xStart+xRange/(xNumberOfPoints-1)*(ROW(OFFSET(Sheet1! $A$1,0,0,xNumberOfPoints,1))-1) We'll name this new formula (surprisingly): Sheet1!x
Getting y values
Now that we have gotten a dynamic set of x values it is time to derive results. Again we'll define a named range:
Name
Refers To
Formul =Sheet1! a $B$1 What we need is a mechanism to evaluate the formula in cell B1 using the x values we have available. For this we'll use the fact that Excel accepts ancient xl4 macro functions inside defined names. The function needed is the EVALUATE function, which we use in the name y, local to worksheet Sheet1: Name Refers To
Sheet1 =EVALUATE(Formu !y la) Strange enough, this version of y only seems to work for functions that do not use built-in Excel functions like SIN or COS, using those in the function will cause Excel to compute all constant values, regardless of the x's! Stephen Bullen has created an almost identical version of this workbook a long time ago, using Excel 5. Look for a download calledChtFrmla.zip. He used a trick to make these functions work by adding "0*x" to the set of y values: Name Refers To
Sheet1 =EVALUATE(Formula&"+0 !y *x") Suddenly, including Excel functions has become possible!
Click the Office button and select "Excel options..." (for Excel 2010 and up: Click File, Options and then locate this option on the "Customize ribbon" tab). Click the "Popular" tab and check the box next to "Show Developer tab in the ribbon":
Fig 1: Showing the Developer tab on the ribbon (Excel 2010 and up).
After you've checked the box and clicked OK, you will find a new tab called "Developer" on your ribbon. This tab houses a group called "Controls", which in turn contains a button "Insert". The dropdown list shows all available controls:
Excel 97-2003
In older Excel versions the controls are housed on two toolbars; the "Forms" toolbar and the "Control toolbox" toolbar. You can show both toolbars using the menu "View", "Toolbars":
By now it is apparent that there are two distinct series of controls: Those from the forms group and those from the Control toolbox (named ActiveX controls in Excel 2007 and up).
ActiveX controls
Advantages
Simple to use Can be used on chart sheets Assigning control to a macro is simple Little known problems
Lots of optio Lots of even Lots of form Lists return than the ind
Disadvantag es
Lists return the index number rather than the selected value
Inserting controls
Inserting a control on your sheet is very simple: Just click the control you need (See fig. 2 and 4 on the previous page) and drag a rectangle on the sheet at the position where you want the control to appear. You can also just click on the sheet and have Excel decide what dimensions to use for the control. If you hold down the alt key when you click on the worksheet, then the control will be aligned to the cell grid. You can also hold the alt key when you are dragging the control or resizing the
control to have it snap to the grid. This is a quick way to ensure your controls are nicely aligned and of equal size. Double click a control on the toolbar or on the Insert controls dropdown if you want to draw multiple copies of that control. Click that control again (or any other control) to get out of that mode.
Button(CommandButt Start a macro on) CheckBox OptionButton ListBox ComboBox ScrollBar Spinner
Set an option, Select multiple options from a list o Select one option from a (short) list.
Select an option from a list, only the selected optio Quickly change numeric values. Change values step-by-step easily.
TextBox ToggleButton
Enter a text. Toggle status. This control is not recommended, I checkbox or a set of two OptionButtons.
Frame
You can use a frame to visually group controls with a shared purpose. Apart from that, the frame control has a specific function for option button controls (see the appropriate section about them). You must start with drawing the frame control before adding the controls you want placed "inside" the frame. To make this a painless process, start out by drawing a relatively large frame (you can make the frame smaller later on). After that, draw the controls inside the frame:
Button (CommandButton)
Buttons or CommandButtons are used to start VBA code (macro's). If you draw a Button from the Forms toolbar on a sheet, Excel will prompt you for a macro to run when the button is clicked (fig 7). If you have not written a macro yet, then you can type the macro's name and click the "New" button to have the (empty) subroutine created for you:
Fig 7: Excel asks you what macro to run when the button is clicked.
If you used the CommandButton from the Control toolbox, you need to double-click the button (in design mode) to access it's
VBA click event. Code for control toolbox (ActiveX) controls is typically written in the code module behind the sheet they are placed on. TIP: If you want to change the properties of a control from the control toolbox (an ActiveX control), then you must put your sheet into "Design mode". In Excel 2003 you can click the first button on the control toolbox toolbar. In Excel 2007 and up you can find this button on the "Developer" tab, within the "Controls" group. When you want to start using the controls, click the same button to get out of design mode.
OptionButton
The option button is very similar to the check box, but only allows mutually exclusive choices to be made; in a set of option buttons, only one option can be "checked". If you take no specific action then all option buttons on one sheet will be treated as one single group. It is possible to have multiple groups on a sheet. The method to achieve this differs between the two sets of option buttons. The method to tie an option button to a cell differs between the two types too. Forms option button To group option buttons from the forms toolbar, first draw frames on your sheet. Then draw the option buttons INSIDE the frame:
Option buttons from the forms toolbar share their linked cell. The value returned to the cell is the index number of the selected option. Note that the index will match the order in which you created the option buttons. To select a control from the forms toolbar (for example to be able to move the control), either right-click the control or control+click it. To select multiple controls to change their properties in one go, hold control while clicking them. Group controls you want to keep together, for example those within a frame, by control+clicking them in turn and then rightclicking on one of their edges and selecting "Grouping", "Group". Control toolbox option button (ActiveX) The option button from the Control toolbox toolbar (ActiveX) has a special property to set up which work together, called the Groupname property:
If you do not change this property, all option buttons on a sheet work together as a single group. ActiveX option buttons all have their own linked cell, which will receive the checked state of their parents as a True/False value. TIP: You can get at the properties window of the controls by right-clicking a control and selecting "Properties". You can also click the appropriate button on the Control Toolbox toolbar. In Excel 2007 and up, you'll find that button on the Developer tab, within the Controls group.
You can either have the control pick up the list from a range of cells, or add the choices to the list box control using VBA. This is done by entering the corresponding information into the ListFillRange or Input range property. If your list of choices resides on a different worksheet from the one your list box is placed on, you must define a range name for the list. Do so by selecting the list and hitting control+F3. In Excel 2007 and up you then need to click the "Add" button. Enter a name for the list and click OK until you're back in Excel. After that, you can enter this new range name in the appropriate property of the control. The second most important property is the LinkedCell (cell link), this cell will receive the result of selecting an item in the list box:
Fig 10, Two important propeties of the ActiveX list box control.
Note that the list box from the forms toolbar will show the index of the chosen item in the cell, whereas the control from the control toolbox returns the actual value to the cell. For the list box from the forms toolbar, use a formula like this one to get the actual value:
=INDEX(ListForComboAndList,C1)
You can set the list box to "Multi" or "Extended" to enable multiple selections. In that case, the linked cell will show either a zero for the list box from the forms toolbar or #N/A for the control toolbox list box control. You will have to use VBA to read what items have been selected and act accordingly.
ComboBox
A combobox is very useful when there are many values to choose from and when you only want to show the chosen item. With the combobox from the Control toolbox you can dynamically add items to the list -using VBA- when the user types a new item in the box instead of selecting an existing item. The combo box form
the forms toolbar does not have this possibility: the user is limited to the choices available in the list. The two important properties LinkedCell and ListFillRange operate in the exact same way as for the list box control.
A vertically placed scroll bar works opposite compared to a spinner control. Clicking the up arrow on a spinner increases the value; clicking the up arrow on a scroll bar decreases the value. In my opinion, the former is more intuitive. If I need this vertical setup, I prefer to use a spinner over a scroll bar. A scroll bar enables you to change the value in two ways. The "Incremental change" is performed by clicking the arrow buttons, the "Page change" is performed by clicking next to the scroll button. See fig. 13:
These properties have a different name for the scroll bar of the Control Toolbox toolbar (ActiveX) "SmallChange" and "LargeChange" respectively, see figure 14:
Scroll bars can only work with integers. The range you can use differs between the two families. The forms control ranges from 0 to 30,000. The ActiveX control can go as high as 666,666. If you need steps less than 1, then use a calculation. For example if you need a step size of 0.5, divide the linked cell's value by 2.
Spinner
If you want to be able to quickly change a cell value stepwise, the spinner is the place to be.
Setting up spinner controls works identical to the scroll bar control, using the same properties. Of course the spinner does not have a Page change (LargeChange) property.
TextBox
There is only one text box control, member of the ActiveX family of controls, on the Control Toolbox toolbar in Excel 2003 and before. I find this control to have little use, because you can simply use a cell and enter text into the cell directly.
ToggleButton
The last control I discuss here is the toggle button. This is another example of a control that is only available through the ActiveX family of controls. In my opinion, this control is ambiguous. It could be used to either indicate an action, or a state. You could use a control like this to change the page setup of a sheet from portrait to landscape and vice versa. Disadvantage of this control is that the state and action paradigm are conflicting, especially if the two states have a different name (like in the example). Suppose you want to enable toggling between portrait en landscape. You might be tempted to use a toggle button, which has some VBA attached to it to set the option and which updates the caption of the control. What does the caption indicate, the current status, or the status AFTER clicking the toggle button?
Fig 16: Ambiguity when using a toggle button: which one indicates we're in Portrait mode?
Due to this ambiguity a toggle button is only useful to indicate an on or off state for a property which has the same name in both states. For this goal, a checkbox is to be preferred. If there are two mutually exclusive choices, consider using two option buttons.
Conclusion
Excel is a very flexible instrument to perform analyses and what-if scenarios. You use formulas in cells with one or more input cells to calculate the various situations. To ease working with different values and/or choices, you can put the controls from either the Control toolbox or the Forms toolbar to good use. Proper use of these controls make your models easier to use. The controls also enable you to ease data entry and at the same time improve data quality by minimizing the risk of wrong entries. For "day-to-day" use, I recommend the Forms controls. If there are specific options you need which are not offered by the form controls then you can also implement the Control toolbox (ActiveX) controls.
Introduction
In Working with Tables in Excel 2013, 2010 and 2007 I promised to add a page about working with those tables in VBA too. Well, here you go.
It's a ListObject!
On the VBA side there seems to be nothing new about Tables. They are addressed as ListObjects, a collection that was introduced with Excel 2003. But there are significant changes to this part of the object model and I am only going to touch on the basic parts here.
Creating a table
Converting a range to a table starts with the same code as in Excel 2003:
Sub CreateTable() ActiveSheet.ListObjects.Add(xlSrcRange, Range("$B$1:$D$16"), , xlYes).Name = _ "Table1" 'No go in 2003 ActiveSheet.ListObjects("Table1").TableStyle = "TableStyleLight2" End Sub
But the new stuff is right there already: TableStyles. A collection of objects which are a member of the Workbook object. This gives rise to some oddities. You can change the formatting of a tableStyle, e.g. like this:
Sub ChangeTableStyles() 'No Go in Excel 2003 ActiveWorkbook.TableStyles(2).TableStyleElements(xlWholeTable) _ .Borders(xlEdgeBottom).LineStyle = xlDash End Sub
This changes the linestyle of the bottom of your table. But hold your horses! If you have any other workbook open, all tables with the same tablestyle appear in your changed style! But if you save your file, close Excel and open Excel again with the file, the changes are gone. This is because you've just changed a built-in tablestyle. If you ask me, I find it strange that the Workbook is a tablestyles' parent, whereas built-in table styles behave as if being bound to the Application object. If you want full control over your table style, you'd better duplicate a built-in style and modify and apply that style to your table.
This snippet of code works exactly the same in Excel 2003, so nothing new there (well, that is, in 2003 those tables ARE called Lists).
As you may have spotted, Excel 2013, 2010 and 2007 handle tables like they are range names. Well, that is exactly what is going on. After inserting a table, a range name is defined automatically. These range names are special though. Excel controls them entirely. You cannot delete them and they get renamed automatically when you change a table's name. Remove a table (convert back to range) and the defined name is removed as well.
If you need to do something with a newly inserted row, you can set an object variable to the new row:
Dim oNewRow As ListRow Set oNewRow = Selection.ListObject.ListRows.Add(AlwaysInsert:=True)
If you then want to write something in the first cell of the new row you can use:
oNewRow .Range.Cells(1,1).Value="Value For New cell"
Set oSh = ActiveSheet 'remove table or list style oSh.ListObjects("Table1").Unlist End Sub
Find out where in your table the cell is located (on header row, on first column, in the bulk of the table Determine the table settings: does it have row striping turned on, does it have a specially formatted first column, ...
Based on these pieces of information, one can extract the appropriate TableStyleElement from the table style and read its properties.
The function shown here returns the TableStyleElement belonging to a cell oCell inside a table object called oLo:
Function GetStyleElementFromTableCell(oCell As Range, oLo As ListObject) As TableStyleElement '------------------------------------------------------------------------' Procedure : GetStyleElementFromTableCell ' Company : JKP Application Development Services (c) ' Author : Jan Karel Pieterse ' Created : 2-6-2009 ' Purpose : Function to return the proper style element from a cell inside a table '------------------------------------------------------------------------Dim lRow As Long Dim lCol As Long 'Determine on what row we are inside the table lRow = oCell.Row - oLo.DataBodyRange.Cells(1, 1).Row lCol = oCell.Column - oLo.DataBodyRange.Cells(1, 1).Column With oLo If lRow < 0 And .ShowHeaders Then 'on first row and has header Set GetStyleElementFromTableCell = oLo.TableStyle.TableStyleElements(xlHeaderRow) ElseIf .ShowTableStyleFirstColumn And lCol = 0 Then 'On first column and has first column style Set GetStyleElementFromTableCell = oLo.TableStyle.TableStyleElements(xlFirstColumn) ElseIf .ShowTableStyleLastColumn And lCol = oLo.Range.Columns.Count 1 Then 'On last column and has last col style Set GetStyleElementFromTableCell = oLo.TableStyle.TableStyleElements(xlLastColumn) ElseIf lRow = .DataBodyRange.Rows.Count And .ShowTotals Then 'On last row and has total row Set GetStyleElementFromTableCell = oLo.TableStyle.TableStyleElements(xlTotalRow) Else If .ShowTableStyleColumnStripes And Not .ShowTableStyleRowStripes Then 'in table, has column stripes If lCol Mod 2 = 0 Then Set GetStyleElementFromTableCell = oLo.TableStyle.TableStyleElements(xlColumnStripe1) Else Set GetStyleElementFromTableCell = oLo.TableStyle.TableStyleElements(xlWholeTable) End If ElseIf .ShowTableStyleRowStripes And Not .ShowTableStyleColumnStripes Then 'in table, has column stripes
If lRow Mod 2 = 0 Then Set GetStyleElementFromTableCell = oLo.TableStyle.TableStyleElements(xlRowStripe1) Else Set GetStyleElementFromTableCell = oLo.TableStyle.TableStyleElements(xlWholeTable) End If ElseIf .ShowTableStyleColumnStripes And .ShowTableStyleRowStripes Then If lRow Mod 2 = 0 And lCol Mod 2 = 0 Then Set GetStyleElementFromTableCell = oLo.TableStyle.TableStyleElements(xlRowStripe1) ElseIf lRow Mod 2 <> 0 And lCol Mod 2 = 0 Then Set GetStyleElementFromTableCell = oLo.TableStyle.TableStyleElements(xlColumnStripe1) ElseIf lRow Mod 2 = 0 And lCol Mod 2 <> 0 Then Set GetStyleElementFromTableCell = oLo.TableStyle.TableStyleElements(xlRowStripe1) Else Set GetStyleElementFromTableCell = oLo.TableStyle.TableStyleElements(xlWholeTable) End If End If End If End With End Function
Note that the function shown above does not take into account that you can set the width of the stripes, both vertically and horizontally.
Wrap Up
Of course there is more to learn and know about tables and lists. A good way to come acquainted with the VBA behind them is by recording macro's while fooling around with them. Luckily Microsoft did include the table object if it comes to recording your actions, unlike the omission on the charting side...
CallByName
I decided it was time to explore the CallByName function, introduced with Office 2000. According to Excel XP VBA Help: CallByName Function Executes a method of an object, or sets or returns a property of an object. Syntax CallByName(object, procname, calltype,[args()]) The CallByName function syntax has these named arguments:
Part object Description Required; Variant (Object). The name of the object on which the function will be executed. Required; Variant (String). A string expression containing the name of a property or method of the object.
procna me
calltype Required; Constant. A constant of type vbCallType representing the type of procedure being called. Can be vbGet (to return a property), vbLet (to change a property), vbMethod (to execute a method) or vbSet (to set an Object)
args()
Since I would like to make this method a bit more general, I would like to just pass the cell object and the entire "procname" in a string:
Sub test() MsgBox CallByName(ActiveCell, "Interior.Colorindex", VbGet) End Sub
Unfortunately, this does not work, "procname" only accepts a single entity (Property, Method or Object). So it is necessary to split up the "procname" string into it's individual elements. Something like this (note: Excel 97 doesn't have the Split function, nor the CallByName function):
Dim lProps As Long Dim vProps As Variant vProps = Split(sProperties, ".") lProps = UBound(vProps)
So to get at the colorindex property of the Interior object of the Cell object, we need to loop through the variant vProps:
For lCount = 0 To lProps - 1 Set oTemp = CallByName(oTemp, vProps(lCount), VbGet) Next
We stop the loop at the one-but-last element of vProps, because all of the elements except the last one will be objects and the last one will be the property we're interested in. Then we get the property of the last object the loop has given us: CallByName(oTemp, vProps(lProps), VbGet) The complete function is shown below:
Function FindCells(ByRef oRange As Range, ByVal sProperties As Strin g, _
ByVal vValue As Variant) As Range Dim oResultRange As Range Dim oArea As Range Dim oCell As Range Dim bDoneOne As Boolean Dim oTemp As Object Dim lCount As Long Dim lProps As Long Dim vProps As Variant vProps = Split(sProperties, ".") lProps = UBound(vProps) For Each oArea In oRange.Areas For Each oCell In oArea.Cells Set oTemp = oCell For lCount = 0 To lProps - 1 Set oTemp = CallByName(oTemp, vProps(lCount), VbGet) Next If CallByName(oTemp, vProps(lProps), VbGet) = vValue Then If bDoneOne Then Set oResultRange = Union(oResultRange, oCell) Else Set oResultRange = oCell bDoneOne = True End If End If Next Next If Not oResultRange Is Nothing Then Set FindCells = oResultRange End If End Function
A small example of its use, selecting all cells with a white fill:
Sub UseFindCellsExample() FindCells(ActiveSheet.UsedRange, "Interior.ColorIndex", 0).Select End Sub