Leveraging a .NET Numerical Library with Microsoft
Transcription
Leveraging a .NET Numerical Library with Microsoft
Leveraging a .NET Numerical Library with Microsoft Excel Visual Studio Office Development and Other Tools A White Paper by Visual Numerics, Inc. June, 2010 Visual Numerics, Inc. 2500 Wilcrest Drive, Suite 200 Houston, TX 77042 USA www.vni.com Leveraging a .NET Numerical Library with Microsoft Excel Visual Studio Office Development and Other Tools By Visual Numerics, Inc. © 2010 Visual Numerics, Inc. All rights reserved Printed in the United States of America Publishing History: May 2007, June 2009, June 2010 Trademark Information The Rogue Wave Software name and logo, the Visual Numerics name and logo, SourcePro, Stingray, HostAccess, IMSL and PV-WAVE are registered trademarks of Rogue Wave Software, Inc. or its subsidiaries in the US and other countries. JMSL, JWAVE, TS-WAVE, PyIMSL and Knowledge in Motion are trademarks of Rogue Wave Software, Inc. or its subsidiaries. All other company, product or brand names are the property of their respective owners. IMPORTANT NOTICE: The information contained in this document is subject to change without notice. Rogue Wave Software, Inc. and Visual Numerics, Inc. make no warranty of any kind with regard to this material, including, but not limited to, the implied warranties of merchantability and fitness for a particular purpose. Rogue Wave Software, Inc. and Visual Numerics, Inc, shall not be liable for errors contained herein or for incidental, consequential, or other indirect damages in connection with the furnishing, performance, or use of this material. TABLE OF CONTENTS ABSTRACT ......................................................................................................... 4 INTRODUCTION .............................................................................................. 4 Configuration of the Tools ....................................................................................... 5 Add-Ins and Workbook Projects .............................................................................. 5 WORKBOOK EXAMPLES ............................................................................... 5 Creating a Project .................................................................................................... 6 Adding a Reference ................................................................................................. 8 Simple Example ..................................................................................................... 11 Scalar Input and Output ........................................................................................ 13 Array Input ............................................................................................................ 15 Array Input and Output ......................................................................................... 17 Charting Visualization ............................................................................................ 19 ADD-IN EXAMPLES ....................................................................................... 21 Custom Task Pane ................................................................................................. 23 Custom Ribbon Design .......................................................................................... 30 Add-In Summary ................................................................................................... 35 VISUAL STUDIO OFFICE PROJECT DEPLOYMENT .............................. 35 THIRD PARTY OPTIONS ............................................................................. 40 XLL......................................................................................................................... 40 ExcelDna................................................................................................................ 41 BlueReference ....................................................................................................... 44 Excel4Net .............................................................................................................. 44 CONCLUSION .................................................................................................. 45 Microsoft Excel is a very popular tool for anyone who analyzes data. The familiar interface and ability to easily view and edit data are key reasons for its success. However, some users need to integrate additional functionality into Excel for their numerical analysis needs. A popular library for this extension is the IMSL C# Numerical Library for .NET Applications, which adds a significant number of mathematical and statistical algorithms to the built-in Excel routines. There are many tools and options available for developers looking to extend Excel. The primary focus of this paper is on the Microsoft Office developer tools in Visual Studio that allow a developer the convenience of the well-known Visual Studio interface and use of their preferred .NET language to customize individual Excel workbooks or to create application-level add-ins. We also cover several open source and commercial tools that customize Excel, including tools that allow the use of XLL (Excel DLL) integration method along with .NET Framework languages. This paper is intended for the developer interested in extending the functionality found in Microsoft Excel. Specialized mathematical and statistical libraries can extend analysis techniques well beyond any spreadsheet tool. However, spreadsheets are very popular because of their ease of use in organizing data and obtaining results quickly. The combination of an advanced numerical library and a spreadsheet with extensibility features lets a developer use a common interface for powerful numerical analysis. Microsoft first released the Visual Studio Tools for Office (VSTO) for the Office 2003 product family and the .NET Framework version 1.1. The concept allows .NET developers to create applications that combined the features of Microsoft Office and .NET using the Office Suite as a development platform. With Visual Studio 2008, Microsoft released the .NET Framework version 3.5 and a variety of updated tools. An updated version of VSTO, version 3.0, was designed to overcome many of the challenges users faced when using earlier versions. This document focuses on the next iteration of releases: the combination of Visual Studio 2010 and Excel 2010. The VSTO name is being phased out in favor of “Office Development in Visual Studio”. The general concepts are similar with the latest versions of these products covered in this document, but for those interested in the combinations of Visual Studio 2005 and Office 2003, or Visual Studio 2008 and Office 2007, earlier versions of this document are available from Visual Numerics upon request. There is extensive documentation available on MSDN1 with regard to Office Development in Visual Studio. Supplementing the Microsoft documentation, this document provides detailed examples using a .NET numerical library as part of an Excel customization project. 1 http://msdn.microsoft.com/en-us/library/d2tx7z6d.aspx |4 The Visual Studio integration with Microsoft Office is powerful and flexible, but might not be the best answer to every challenge. A number of other tools exist commercially or as open source projects that can also be used to integrate third party .NET assemblies like the IMSL C# Numerical Library into the Microsoft Excel environment. Some of these options are discussed in the “Third Party Options” section of this document. Configuration of the Tools The core component for developing Excel applications in conjunction with the .NET Framework is Visual Studio 2010. When installing Visual Studio 2010, be sure to either do a complete install or check the box for Office Development components. On the Excel side, this example works with Excel 2010, a component of Office 2010. Earlier versions of Excel can also be used in a similar manner; however, earlier versions of Visual Studio are not substituted easily. Visual Studio also provides the option to install support for several different languages. The examples in this paper utilize the IMSL C# Numerical Library, which is a .NET assembly with language-independent metadata equally usable from Visual Basic, Managed C++ or F#. In earlier versions of VSTO, Visual Basic was easier to work with when writing Office applications because it simplifies the calling of the Office object model. However, with the later releases the C# language is a feasible option. Add-Ins and Workbook Projects There are three kinds of Office projects for Excel: Add-Ins, Templates and Workbooks. The Template model is very similar to a Workbook, but works for blank templates instead of specific workbooks. The conceptual difference is the add-in model is referred to as an “application-level project” while a workbook model is referred to as a “document-level project”. At the application level, custom Excel functions would be available to all workbooks, however at the document level, all customization is unique to one or more sheets contained in a single workbook. The end results may contain the same kinds of functionality discussed in this document, but the interface and development are variations on the theme. Further, the two models can be combined with unique task panes and ribbons for a specific workbook. For more information on document and application level projects, please refer to the comprehensive online documentation2. Examples in this document are presented in order of increasing complexity, beginning with the workbook model. The programming language used is C#, but the procedure would be similar with Visual Basic or other .NET languages as discussed previously. A single project is used throughout, adding additional components and code behind methods as needed, but they are independent and the later examples do not rely on code from earlier examples. 2 http://msdn.microsoft.com/en-us/library/bb386107.aspx |5 Creating a Project To create a new project the first step is to open the New Project dialog in Visual Studio 2010. For this example, we want to create a new Visual C# project for an Excel 2010 Workbook. There are several ways to do this that include choosing Create Project… from the opening summary page or selecting the New Project… option from the File menu. On the system used in this example, the user is configured as a C# developer, so the default project selections are for Visual C#. The Office project templates are under the Office subsection where the choice of 2010 and 2007 versions are offered. We want to allow users within an Excel Workbook context to use IMSL functionality, which is accomplished by creating an Excel Workbook project, so select the Excel 2010 Workbook template as shown in the following screenshot: Name the new project ExcelWorkbookIMSL and click OK to proceed to the next step in the new project wizard being sure to keep the .NET Framework version set to 4. For future projects, you may already have an existing workbook created; however, here we shall start with a brand new workbook in the Excel 2010 .xlsx format, as shown in the next image: |6 If this is the first time using the Office projects, you may be prompted with the security warning shown below. While access is disabled to prevent macro viruses from spreading, it must be enabled to allow you to program the Microsoft Office System. Click OK to continue. |7 At this stage, you should see an empty Excel workbook, and it usually contains three blank worksheets. In the Solution Explorer, you will find “code behind” C# (.cs) files for each sheet and for the workbook as a whole. All these empty source files created from the templates now exist in the solution, but are blank, except for empty Startup and Shutdown methods and Designer-generated code. Here are images of the blank workbook and source code files: Adding a Reference Once the new project is created, we need to add a reference to the numerical library assembly. In this case, we are using the IMSL C# Numerical Library. Like most Visual Studio projects, the Solution Explorer contains a References heading that can be right-clicked to add a reference to other projects or assemblies. Alternatively, you can select Add Reference… under the Project menu item. Using either method, the Add Reference dialog appears as shown below. |8 Unless the IMSL C# Library assembly appears under the Recent list, use the Browse tab to locate the imslcs.dll assembly; the default installation directory is C:\Program Files\VNI\imsl\imslcs650\bin. The standard project template is configured to use the .NET Framework 4 Client Profile platform. However, the IMSL C# Library requires the full version of the Framework. To set this, select Project -> ExcelWorkbookIMSL Properties from the Visual Studio toolbar. In the Application tab, locate the Target Framework dropdown and change it to “.NET Framework 4” instead of the Client Profile version. A warning dialog box will appear that allows you to confirm the change by clicking Yes. The project will be reloaded, and you will need to re-open the Sheet1.cs source file. The screenshot below shows the options for frameworks to target. |9 Once the reference is added to the project and the Target Framework is properly configured, we can confirm the library is available by opening one of the source files (for example, rightclick on Sheet1.cs in the Solution Explorer and select View Code) and adding a using line to the source. Code completion, as shown in the next image, should be active if the IMSL Library has been successfully added as a reference. |10 This confirmation with using is valuable but is not used in the examples. All examples use the full class names including the namespace to create inclusive code snippets. As an external .NET reference, a copy of the imslcs.dll assembly should be found in the bin\Debug or bin\Release directory with the spreadsheet object. If imslcs.dll has been added to the Global Assembly Cache, a local copy may not exist in the project but a copy is required when deploying the application to other computers. For more information on deployment, see the Deployment section of this document. With the newly created blank project and the IMSL C# Numerical Library available, we are ready to start building examples. Simple Example One of the simplest things we could do with the IMSL C# Numerical Library is to minimize the interaction with the spreadsheet objects and hard code variables. To begin, we need to provide a source for the user to initiate the action. There is a variety of ways to allow a user to interact with the worksheet, but basic button controls are used in these examples to avoid additional complexity. To view the spreadsheet and open the Visual Studio Toolbox, select the ExcelWorkbookIMSL.xls tab. If the Toolbox is not visible, drag it from the left edge (usually) or press Ctrl-W then X to open the window. Select the Button control and drag in the spreadsheet to create it. Upon creation, it is called “button1” and has that name as its text property. To be more descriptive, with the button selected, scroll down in its Properties view on the lower right and change the name via the Text property field to “Erf(0.2)”. This name will make more sense as we proceed. See the following image for the current status in Visual Studio 2010: |11 Next, Visual Studio can generate some code to allow us to fill in the blanks and take some action when the user clicks the button. Double-click the button in this design view and the view changes to the code behind Sheet1.cs with a blank button1_Click method. This is the method executed when a user clicks the button at runtime. For this example, we return the value of the error function evaluated at 0.2. Fill in this method so that it reads as follows: private void button1_Click(object sender, EventArgs e) { double result = Imsl.Math.Sfun.Erf(0.2); this.Application.ActiveCell.Value2 = result; } In detail, this code creates a double named result and then calls the static method Erf in the Sfun class of the Imsl.Math namespace. The output is then stored in the active cell. To test this code, the Start Without Debugging command automatically builds the solution and starts Excel. You can do this by pressing Ctrl+F5 or by selecting the Start Without Debugging option under the Debug item on the menu bar. You should then see Excel with your new button. Click the button to see the result filled in the active cell. Screenshots follow: |12 This example shows one straightforward way to embed IMSL C# Library functionality in an Excel worksheet. The code behind the button interaction could be significantly more complex, referencing almost any options available in the .NET Framework using the base class libraries or external assemblies like the IMSL C# Numerical Library. Interaction with the spreadsheet object was limited to writing a scalar output value to the active cell, but the following examples expand on that interaction and the types of data being passed back and forth. Scalar Input and Output This exercise takes the previous example to the next level. The input argument for the Erf method is retrieved from the spreadsheet and the output is returned to a specific cell. Interaction with specific cells in the worksheet is accomplished using Named Ranges3. The advantage of a Named Range is that the cells appear to the user as any other cell, but are also accessible by name in the code behind for the developer. Under the Formulas tab in the Excel Ribbon, the Defined Names group houses all the tools necessary to manage the ranges defined in a workbook. Individual items can be edited using the Name Manager. Building off the same project we created, select cell A4 and right-click. Select the Define Name… option to bring up a dialog. Provide the information as shown below: 3 http://msdn.microsoft.com/en-us/library/microsoft.office.tools.excel.namedrange.aspx |13 The current range is listed in the “Refers to” field, while the “Name” field requires your typing a name; adding a comment is optional but informative. Repeat the process for cell B4 and enter the name ErfOutput. These cells are shaded with colors in the following images to distinguish them from the others in the worksheet; of course, coloring is optional but important cells will often be highlighted or labeled in some manner in larger workbook applications. To allow user interaction, another button control is added to the worksheet and named “Compute Erf()”. This button is created and edited as before. You may need to make the button larger to display the full text. Again, double-clicking the button brings up the empty callback method for this button’s action. Fill in this method with the following code: private void button2_Click(object sender, EventArgs e) { double input = (double)ErfInput.Value2; ErfOutput.Value2 = Imsl.Math.Sfun.Erf(input); } The input value is retrieved from the named range ErfInput using the Value2 property. For other data types such as Date/Time values or currencies, the Value property may be a better choice, but for simple numbers Value2 is more straightforward. As you enter the above code, code complete reveals a large number of properties and methods available for this object because it implements the Range interface. The Value2 returns an Object which has to be explicitly cast to a double. Returning an Object is a general implementation because a cell may contain any content from text to formulas. You should include error checking in a deployed application, since an exception will be thrown if we try to cast text into a double type, as shown below where the input cell contains the letter “a”: |14 While the exception is descriptive enough, a cleaner approach would be to catch this conversion error in the code and then report the problem appropriately in a MessageBox, for example. Moving on, assuming the input variable is now a valid number, the Erf method is called and the result returned by setting the Value2 property for the ErfOutput named range. A successful run is shown below: With this example, we allow the user to enter a value in cell A4 and evaluate the value of the error function, returning the result in cell B4 with a click of a button. A single value is used as input to a static method for this example. The next example expands to accepting an array of values as input into an IMSL C# Library class. Array Input This example increases with complexity by accepting an array as input. Further, the array may be any length as the size is retrieved in a Named Range while the values are located below that cell. The IMSL Library function used is the SignTest class. The previous example used a static method in the Sfun class, but SignTest requires a constructor and a follow-up method call. |15 The code behind structure supports this more complex object case and is not limited to static method calls, allowing use of multiple classes or reference libraries in combination. Continuing to work with the ExcelWorkbookIMSL.xls spreadsheet, follow the same instructions above for creating a named range to name cell A8 “SignTestCount” and cell B8 “SignTestOutput”. The cells in column A below A8 are used as input. For flexibility, enter the formula “=COUNT(A9:A1000)” into cell A8 to allow use of nearly one thousand values. In this example, we are only using a handful of values, but a general formula could be used. The cells A9 through A27 are filled in with values from the SignTest example in the IMSL C# Numerical Library documentation, as shown in the screenshot. Another button control is added, named “Sign Test”, and its callback method contains the following code: private void button3_Click(object sender, EventArgs e) { int count = (int)(double)SignTestCount.Value2; Excel.Range dataRange = this.Range[ this.Cells[SignTestCount.Row + 1, SignTestCount.Column], this.Cells[SignTestCount.Row + count, SignTestCount.Column]]; Object[,] array = (Object[,])dataRange.Value2; double[] data = new double[count]; for (int i = 0; i < data.Length; i++) { data[i] = (double)array[1 + i, 1]; } Imsl.Stat.SignTest st = new Imsl.Stat.SignTest(data); double result = st.Compute(); SignTestOutput.Value2 = result; } This is the most complex code shown so far. The basic parts are very similar to the previous examples with additional code that retrieves an array from a range of cells. First, the number of cells, count, is collected from the named range SignTestCount.Value2, and this variable is used to declare the size of the data array. Note that we first cast the Object to a double then to an int to avoid an issue casting an Object directly to an integer. We then define a new Excel.Range object and use the Cells property to define its endpoints relative to the SignTestCount cell, expecting the input values to be listed directly below this cell. To retrieve the contents of this range properly, the array variable is declared as a two-dimensional Object array and its Value2 property is queried. With this call, we are able to parse the array of Objects as double values and fill the data array (note that the array variable is a 1based array rather than the traditional 0-based form most C# developers are used to). The array of Objects cannot be passed directly to the SignTest object since the constructor expects a one-dimensional array of double values. Once we loop through the array to fill data, the SignTest object is instantiated, and its Compute method is called. The result is returned to the SignTestOutput named range. The result of running this example is shown below (for formatting reasons, the columns are split in half): |16 Array Input and Output For this example, we demonstrate matrix multiplication, which is not typically performed in a spreadsheet, yet is used regularly in many areas of numerical analysis. From the IMSL C# Library, the overloaded Matrix.Multiply static method accepts one-dimensional and twodimensional arrays of double values and returns an appropriately sized double array containing the product of the matrix-matrix, vector-matrix or matrix-vector combination. Named ranges are used again to define the input variables, but their sizes are queried at runtime instead of requiring a counting cell as in the SignTest example previously. Defining a range of cells as a named range is done the same as single cells: highlight the area and rightclick, and then select Define Name… from the list. Since the output range depends on the sizes of the input matrices, we use another single cell Named Range for the top left corner of the output matrix. Finally, for clarity, the entire output matrix is shaded. The code in the button’s callback method is as follows: private void button4_Click(object sender, EventArgs e) { Object[,] oMatrixA = (Object[,])MatrixA.Value2; Object[,] oMatrixB = (Object[,])MatrixB.Value2; int rowA = oMatrixA.GetLength(0); int colA = oMatrixA.GetLength(1); int rowB = oMatrixB.GetLength(0); int colB = oMatrixB.GetLength(1); double[,] dMatrixA = new double[rowA, colA]; double[,] dMatrixB = new double[rowB, colB]; for (int i = 0; i < rowA; i++) { for (int j = 0; j < colA; j++) { |17 dMatrixA[i, j] = (double)oMatrixA[i + 1, j + 1]; } } for (int i = 0; i < rowB; i++) { for (int j = 0; j < colB; j++) { dMatrixB[i, j] = (double)oMatrixB[i + 1, j + 1]; } } double[,] result = Imsl.Math.Matrix.Multiply(dMatrixA, dMatrixB); Excel.Range resultRange = this.Range[MatrixOutput.Cells[1, 1], MatrixOutput.Cells[1 + rowA - 1, 1 + colB - 1]]; resultRange.Value2 = result; resultRange.Interior.Color = MatrixOutput.Interior.Color; } There are many differences in this code segment compared to the previous examples revealing the myriad of ways one can interact with the cells on an Excel spreadsheet at this level. To begin, the values of the MatrixA and MatrixB named ranges are retrieved with calls to their Value2 property and stored in Object[,] arrays. The dMatrixA and dMatrixB arrays are declared as two dimensional double arrays with sizes determined from their counterparts. Note again, the offset of +1 that arises to account for the differences in handling array indices. Once the loops fill the double arrays, the Matrix.Multiply method is called, storing the answer in result. We define a new named range, resultRange, with a size defined by the number of rows of A by the number of columns of B (per matrix multiplication rules). Output to the MatrixOutput Named Range cell in the sheet is accomplished by setting its Value2 property to the result. The MatrixOutput cell was shaded to start with, and the entire output matrix is shaded to match. Screenshots of an example are shown below. The input range MatrixA is shaded orange; MatrixB, yellow. Before clicking the button, the output cell I2 is shaded blue; after the calculation the full output matrix is shaded blue. |18 This example works for input matrices of any size. Further, the input or output of a twodimensional data set has many applications beyond just matrix operations, and there are numerous functions in the IMSL Library that rely on such data. Charting Visualization While Microsoft Excel offers a variety of charting options along with an easy-to-use interface for creating charts, the end results are sometimes poorly applied for quantitative applications. In these cases, a straightforward graph created programmatically may provide a better solution. The IMSL C# Numerical Library contains charting components with an object-oriented programming interface, and these charts can be added to a worksheet using the same programming model as the previous examples. One adds .NET user interface components like the button controls to an Excel worksheet by dragging it from the toolbox window and dropping it onto the sheet. To work with the IMSL C# Library charting components in this manner, a component must first be added to the Visual Studio Toolbox. Follow these steps to add the PanelChart component to the Toolbox: in a Design view, right-click somewhere in the Toolbox and select Choose Items… and under the .NET Framework Components tab click Browse… and locate the ImslCS.dll assembly. (Alternatively, you can drag the DLL and drop it onto the Toolbox.) Make sure the PanelChart class from the Imsl.Chart2D namespace is on the list and its box is checked, then click OK to close the dialog box. With the project in Design View (showing the Excel spreadsheet), locate the PanelChart object on the Toolbox (it is probably at the bottom of the All Windows Forms section) and select it by single-clicking. Move the cursor over to the worksheet area and drag to define the boundaries of the chart. It will appear as a rectangular region with a standard default image showing a simple chart as shown below. |19 Programmatically, the properties of the created PanelChart object are accessed in the code behind the Excel spreadsheet in exactly the same way as in a traditional .NET Windows Forms desktop application. For this C# example, consider plotting a simple one-dimensional array like the one we used for the SignTest example. The following code segment plots the array data from the SignTest example as a colored line; this snippet can be added at the end of the button3_Click method shown above: // code snippet to work with a PanelChart object Imsl.Chart2D.Chart chart = imsl_Chart2D_PanelChart1.Chart; Imsl.Chart2D.AxisXY axis = new Imsl.Chart2D.AxisXY(chart); Imsl.Chart2D.Data line = new Imsl.Chart2D.Data(axis, data); line.LineColor = System.Drawing.Color.BlueViolet; imsl_Chart2D_PanelChart1.Refresh(); The first few lines of the code define the objects in the chart hierarchy: a Chart holds Axis objects, which hold Data objects. The chart reference is obtained from the embedded PanelChart object’s Chart property. The axis and data objects are then defined and the data variable from the SignTest example appears here. The LineColor property is set, and |20 then the Refresh method is called to redraw the PanelChart in the spreadsheet. A screenshot of the end result is shown below: This is a simple example, but any of the charting features of the IMSL C# Numerical Libraries would be similarly accessible. These features include bar, pie, high-low-close, contour, heatmap, dendrogram, statistical process control charts, and others. This section covers creating an Excel Add-In that references the IMSL C# Numerical Library. Creating the project and adding a reference to the IMSL C# Library assembly follows the same steps as the Workbook Examples above. For completeness, below are screenshots of the project creation dialog and an empty Add-In project: |21 Note that since Add-Ins are application-wide customizations, a specific Excel workbook instance is not required as part of the project. As before, the template is largely blank except for some code generated by Visual Studio. Do not forget to change the target framework to .NET Framework 4 instead of the Client Profile. |22 As an application-wide customization, your Add-In is not accessed from elements added to a workbook, but instead through the Excel application interfaces itself. There are two common methods: creating a custom task pane4 or creating a new ribbon5 element. Custom Task Pane With custom task panes, developers can add in their own features using a view familiar to the typical Excel user. Since the custom panes are built using Windows Forms controls, their construction is familiar to most .NET developers as well. You can consider a custom task pane as a miniature Windows Forms application hosted within Excel. All of the Controls and Events operate as one would expect in a traditional desktop application. A Task Pane is a panel usually docked on the side of the default view in Excel, but it can be a floating window within the spreadsheet as well. This example shows how to use the custom task pane concept to build a solver for polynomial equations. Begin with a blank Add-In project and add a reference to the IMSL C# Library as described above (right-click on References in the Solution Explorer, select Add Reference…, and browse to the ImslCS.dll assembly). The first step is to add a new User Control to the project. From the Project item on the menu bar, select Add New Item… and choose “User Control” from the next dialog as shown below (it can be found in the Windows Forms category): 4 5 http://msdn.microsoft.com/en-us/office/aa942864.aspx http://msdn.microsoft.com/en-us/office/bb386089.aspx |23 We will use the default “UserControl1” name for the class. This action adds another item to the Solution Explorer (the C# source code file for the new control) and switches the view in Visual Studio to a design view showing the blank control, as seen below: In a few steps we will fill in this empty space with some Windows Forms controls, but first switch back to the source code view of ThisAddIn.cs. The next step is to add some code that will enable your new task pane when Excel is started. Add the following five lines of source code to instantiate the UserControl1 object and load it into the list of custom task panes: namespace ExcelAddIn1 |24 { public partial class ThisAddIn { private UserControl1 userControl1; private Microsoft.Office.Tools.CustomTaskPane imslCustomTaskPane; private void ThisAddIn_Startup(object sender, System.EventArgs e) { userControl1 = new UserControl1(); imslCustomTaskPane = this.CustomTaskPanes.Add (userControl1, "IMSL Task Pane"); imslCustomTaskPane.Visible = true; } This is the entire source code required to create a blank task pane that does nothing. As a test, save and build the project and then press Ctrl+F5 to start without debugging. You should see Excel 2010 start with a new task pane visible and empty, except for the title: This pane can hold a variety of Windows Forms controls and can be treated as a miniature .NET Windows Forms application that is hosted within Excel. The next steps are to switch to design view for the UserControl1 object and drag items from the Toolbox in Visual Studio to build an interface. Instead of tediously explaining each step, a screenshot is shown below. A GroupBox is used to hold all the elements for this example, which allows you to add additional tools in the same area. A few labels are used for description; text boxes and a NumericUpDown are used for input; and a RichTextBox is used for output with a button to run the solver. No code has been written yet, although the user interface code is being generated by Visual Studio. The following images show the design view (left) and runtime view in Excel (right): |25 There is quite a bit of customization that can be done. For example, you probably want to fill in default values of “0” for the coefficients. And with a little more code, it is easy to disable the text boxes for coefficients that are not required given a smaller order for the polynomial. But the key part of missing functionality so far is solving for the roots and printing the results. For that step, double-click the Solve button in the designer and Visual Studio creates an empty button1_Click method. In this method, gather the user input from the text boxes, pass them to the ZeroPolynomial class in the IMSL C# Library, and then nicely format the output of the solution. This can be written using many different styles and the following is an example showing some customization and output formatting style: public partial class UserControl1 : UserControl { private TextBox[] textBoxArray; private const int maxOrder = 6; private const int minOrder = 2; public UserControl1() { InitializeComponent(); textBoxArray = new TextBox[maxOrder]; textBoxArray[0] = textBox1; textBoxArray[1] = textBox2; textBoxArray[2] = textBox3; textBoxArray[3] = textBox4; textBoxArray[4] = textBox5; |26 textBoxArray[5] = textBox6; } private void numericUpDown1_ValueChanged(object sender, EventArgs e) { int num = (int)numericUpDown1.Value; for (int i = minOrder; i < maxOrder; i++) { if (i < num) { textBoxArray[i].Enabled = true; } else { textBoxArray[i].Enabled = false; } } } private void button1_Click(object sender, EventArgs e) { int num = (int)numericUpDown1.Value; double[] coef = new double[num]; for (int i = 0; i < num; i++) { coef[i] = Double.Parse(textBoxArray[i].Text); } Imsl.Math.ZeroPolynomial zp = new Imsl.Math.ZeroPolynomial(); Imsl.Math.Complex[] root = zp.ComputeRoots(coef); richTextBox1.Text = ""; for (int i = 0; i < root.Length; i++) { richTextBox1.AppendText(root[i].Real().ToString("0.0000") + " " + root[i].Imag().ToString("0.0000") + "i\n"); } } Running this example and solving the equation for x is shown below. (Note in this image the pane has been undocked and is floating in an Excel spreadsheet.) The output values are generally complex numbers, though the second root for this problem is real. |27 Designing custom task panes using Visual Studio Office Tools and Excel 2010 is similar to creating any .NET desktop application using Windows Forms. The flexibility allows a wide variety of applications, and integrating a .NET library such as the IMSL C# Numerical Library is as easy as adding a reference to the assembly. This basic example does not interact with the current Excel spreadsheet instance (for example, to retrieve coefficient values from a series of cells). Such interaction is possible with a combination of the custom task pane UserControl and the Workbook model. Specifically, UserControl objects can be created without necessarily showing them every time Excel opens. In a specific Workbook project, an existing UserControl can be referenced and displayed for a specific sheet with code along the lines of the following: public partial class Sheet1 { UserControl1 uc = new UserControl1(); private void Sheet1_Startup(object sender, System.EventArgs e) { Globals.ThisWorkbook.ActionsPane.Controls.Add(uc); } } Combining custom task panes with workbook-level customizations can yield a very powerful solution using both Excel data and objects and .NET Framework tools. |28 Note that once you create this Add-In, each time Excel starts it will be loaded. To manage the Add-Ins you create, click on the Office Button on the top left corner of the Excel application and choose Excel Options at the bottom. Next, click on Add-Ins on the left side and notice the “ExcelAddIn1” item listed under the Active Application Add-Ins list (see below). Notice its type is listed as a “COM Add-in” providing some insight into how the integration with .NET works behind the scenes. To disable this Add-In, simply select “COM Add-Ins” in the Manage dropdown at the bottom of the dialog and click “Go…” which shows the itemized registered COM Add-Ins: |29 Simply uncheck the box for your custom add-ins that you do not want to load each time Excel starts. Click OK and restart Excel to confirm that your custom task pane is no longer being loaded. Custom Ribbon Design Ribbons were introduced with the Office 2007 release. The concept is that similar individual tasks are formatted in groups which are then collected together on separate ribbons. For example, in Excel 2010 to center the contents of a cell, you click the “Center” button located in the Alignment group on the Home ribbon. Adding a custom Ribbon object is another way to enhance the functionality of Excel, and the model is quite similar to the custom task pane. As above, you start with a new Excel 2010 Add-In project in Visual Studio 2010. Then select Add New Item… from the Project item on the menu bar. Here, select the “Ribbon (Visual Designer)” template. It is possible to design a ribbon using XML as well, but the designer should feel more familiar to developers accustomed to Windows Forms projects. Here are screenshots of selecting the template along with the resulting design view of a blank ribbon: |30 Using the Visual Studio Toolbox, you can now simply drag items into the empty group. Note that individual components cannot be added to the ribbon, but must be grouped together in containers. This is not a significant limitation, however, as it offers the designer the option of grouping similar tasks. For this example, we will just add a single button that displays a Windows Forms application to evaluate some special functions that Microsoft Excel does not include. Note that the Ribbon, Tab, and Group can all be renamed as desired. The Tab name is the one above the ribbon on the menu bar. The screenshot below shows the new button and the tab’s new name of “IMSL”: |31 To make the button perform an interesting task, double-click it in the design view. This switches automatically to the code view of Ribbon1.cs and inserts a button1_Click method. Adding components to the Ribbon and the general abilities are very similar to the Custom Task Pane previously discussed, so for completeness we will simply provide the source for a working example. The Click method for the button on the ribbon creates a new EvalForm object that lets a user select a special function using a ComboBox and set parameters using TextBox objects. The result is computed and displayed in another TextBox. In design view and at runtime, the EvalForm looks very similar: |32 However, at runtime Excel now looks slightly different with an IMSL tab added at the top of the application. When this tab is selected, you will see the new group and the button: Clicking on the Special Functions button shows the EvalForm window by adding a single line to its callback method: private void button1_Click(object sender, RibbonControlEventArgs e) { new EvalForm().Visible = true; } The source code for the EvalForm.cs class is as follows: public partial class EvalForm : Form { |33 private Hashtable sfuns; public EvalForm() { InitializeComponent(); sfuns = new Hashtable(); sfuns.Add("Beta", 2); sfuns.Add("BetaIncomplete", 3); sfuns.Add("ErfcInverse", 1); sfuns.Add("ErfInverse", 1); sfuns.Add("Gamma", 1); sfuns.Add("LogBeta", 2); sfuns.Add("Poch", 2); IDictionaryEnumerator en = sfuns.GetEnumerator(); while (en.MoveNext()) { comboBox1.Items.Add(en.Key); } comboBox1.Sorted = true; comboBox1.SelectedIndex = 0; } private void comboBox1_SelectedIndexChanged(object sender, EventArgs e) { if (comboBox1 == null) return; string selKey = comboBox1.SelectedItem.ToString(); int n_params = (int)sfuns[selKey]; switch (n_params) { case 1: textBox2.Enabled = false; textBox3.Enabled = false; break; case 2: textBox2.Enabled = true; textBox3.Enabled = false; break; case 3: textBox2.Enabled = true; textBox3.Enabled = true; break; } } private void button1_Click(object sender, EventArgs e) { string selKey = comboBox1.SelectedItem.ToString(); int n_params = (int)sfuns[selKey]; Imsl.Math.PrintMatrix pm = new Imsl.Math.PrintMatrix(); Assembly imslcs = Assembly.GetAssembly(pm.GetType()); MethodInfo mi = imslcs.GetType("Imsl.Math.Sfun").GetMethod(selKey); object[] parms = new object[n_params]; parms[0] = Double.Parse(textBox1.Text); if (n_params > 1) { |34 parms[1] = Double.Parse(textBox2.Text); { if (n_params > 2) { parms[2] = Double.Parse(textBox3.Text); } } } double result = (double)mi.Invoke(null, parms); textBox4.Text = result.ToString(); } } The functionality of the EvalForm class is somewhat straightforward. In short, the list of available functions with the number of required parameters is added to a Hashtable, which is used to populate the ComboBox. Depending on how many parameters are accepted, the ComboBox callback enables or disables the input TextBox items. In the callback for the “Calculate” button, the string value of the selected item is used as the actual method name to call in the Imsl.Math.Sfun class, so reflection is used. Not shown are using statements adding the System.Collections namespace (for the Hashtable) and the System.Reflection namespace (for the reflection classes). The result is then displayed in textBox4. Add-In Summary Both the Custom Task Pane and Custom Ribbon models are excellent models that can be used to add functionality to Excel. With the custom add-ins enabled, each time Excel is started the tool becomes available to the user in any workbook opened. In the examples presented here, the interface is used to display standard Windows Forms applications, although there is virtually no limit to the amount of customization available. By combining the Workbook model with a custom Add-In, it is possible for the task pane or ribbon to communicate with cells in the active workbook directly adding significantly more power and flexibility to the solution. Combining task panes and ribbons in a Workbook project is as easy as selecting Add New Item… from the Project menu. This allows you to create a new group on the Add-Ins Ribbon or a new Task Pane that is available only to this specific workbook. Deployment of Office Solutions was a challenge for many developers in the .NET Framework version 1.1, but has been progressively simplified with later updates. There is detailed documentation available online6 including the list of prerequisites, but we can summarize what is required to deploy the project created in this paper to a separate computer from the development machine. With Visual Studio 2008 and Excel 2007, deployment became significantly easier than for previous releases of VSTO. With the current platform, simply select Build from the Visual Studio 6 http://msdn.microsoft.com/en-us/library/bb386179.aspx |35 2010 menu bar, and then select Publish <project>. The specific project name is shown on the list. The following screenshots guide you the default selections. First, you need to select a location to publish the application. This can be nearly any legal path including shared network resources and web sites. The default is simply “publish\” which creates a subdirectory under the working Visual Studio solution that contains the distributable files: Next, the default installation path is required. For simplicity, select “CD-ROM or DVD-ROM”, but this can also be a web URL or network path. |36 Finally, the wizard is ready to build the files. The full path to their location is displayed: |37 After a few seconds, you should see “Publish succeeded” in the status area at the bottom of the Visual Studio window. If you browse to the newly created directory, you should see files with names like setup.exe, ExcelWorkbookIMSL.vsto and ExcelWorkbookIMSL.xlsx along with a subdirectory that contains the application files. At this point, do not open the Excel workbook (.xlsx) file; instead, this entire directory hierarchy should be packaged for distribution on an end user’s computer. When you publish an Add-In project, there is not an xlsx workbook available for distribution since this type of project applies to the Excel application as a whole instead of a specific workbook. An example of the files in the target folder is shown below. |38 To install all the components, run setup.exe. For test purposes, we copied the “publish” directory and its contents to the root directory. If some components are missing (such as .NET 4.0 or the Office Runtime components) the user will be presented with license agreements to accept. Once accepted, the components are downloaded and installed automatically. Once the prerequisites are configured, the installation continues. Since the customization has not been officially registered, a warning will most likely appear that states the publisher cannot be verified: You must click Install to continue. The files are then copied and you should see a dialog that notifies you that the customization was successfully installed: |39 For a workbook project, the primary Excel workbook (ExcelWorkbookIMSL.xlsx here) is now fully functional. For add-in projects, each time Excel is started your new custom pane or ribbon will be available. To remove the installation, use the Add/Remove Applications feature of Microsoft Windows. The customized workbook or add-in will appear on the list: While the combination of Visual Studio Office Development solutions of Add-Ins and Workbook projects meets many user requirements for integrating the IMSL C# Library with Microsoft Excel, there are other options one may wish to consider as well. Some of these require significantly less coding expertise while others are focused on solving different kinds of business problems. This section covers tools outside of the Microsoft Office Tools stack. Some of the following tools are open source and some are commercial, however any interested user should investigate each to its own merits. Visual Numerics makes no claims for validity or support for any of these third party tools. XLL An XLL is basically a DLL for Excel. Developing XLL files for Excel has traditionally been well documented7 (and here8) by Microsoft and has expanded to utilize a separate Excel 2010 XLL 7 http://msdn.microsoft.com/en-us/library/aa730920.aspx |40 SDK9. However, development can be quite complex and requires knowledge of the Excel API, the component object model (COM) as well as a solid C/C++ programming background. An MSDN page on the C API in Excel 201010 goes into further details on various aspects of this topic. Building an XLL does not require a .NET library, and nearly any native C/C++ mathematical library could be utilized in this manner. A commercially licensed toolkit called XLL Plus11 is available to aid developers building Excel add-ins using C or C++. Users interested in integrating IMSL Library functions into a native XLL should consider the IMSL C Numerical Library12, which is easily called from C or C++ applications. For users interested in High Performance Computing (HPC) with Excel, especially the Parallel Excel features tied into Excel 2010 and Windows HPC Server 2008 R2, the XLL solution will be best performing and most flexible option. There have been discussions on how to create an XLL using managed .NET code, specifically the C# language. This is not a currently supported feature of the Visual Studio Office Development tool suite, but other options are discussed in the following sections. ExcelDna The ExcelDna project13 is a freely available open source project for creating XLLs using managed code. There are many examples and documentation available through its Google Group14. The general concept is that C# source code is integrated into an XML file with the “.dna” extension. At runtime, the ExcelDna.xll (prebuilt) parses the script and exposes appropriate functions through Excel’s “Insert Function” interface. The project requires .NET 2.0 and limits you to static function calls. This limitation may require some significant development to map complex functionality to a single function call. Optional arguments are supported, which adds some flexibility to the API you expose. Especially note that because of the .NET 2.0 requirement, you will need the proper version of the IMSL C# Library installed, which is a different assembly than the version supporting .NET 4.0 used in the above section. ExcelDna has the ability to parse a .NET assembly and expose all of the public static methods. The IMSL C# Library contains a large number of static methods, so this can be a useful example. For this case, make a copy of the file ExcelDna.xll and rename it to IMSLCS.xll. Next, create a plain text file called IMSLCS.dna (the filename prefixes must match) that contains the following lines: <DnaLibrary> <ExternalLibrary Path="ImslCS.dll" /> </DnaLibrary> 8 http://msdn.microsoft.com/en-us/library/bb687861.aspx http://www.microsoft.com/downloads/details.aspx?FamilyId= bc60e752-9a1d-47a7-912f-eac18fd95695&displaylang=en 10 http://msdn.microsoft.com/en-us/library/bb687829.aspx 11 http://www.planatechsolutions.com/xllplus/ 12 http://www.vni.com/products/imsl/c/imslc.php 13 http://exceldna.typepad.com/ 14 http://groups.google.com/group/exceldna 9 |41 To ease deployment, copy these two files to the IMSL C# Library installation directory (typically C:\Program Files\VNI\imsl\imslcs650\bin). To enable this extension inside Excel, you can simply double-click the IMSLCS.xll file. You will be greeted by a security notice: Alternatively, open the Excel Options window, select Manage Excel Add-Ins and browse to the XLL file. It then appears in the list of available Excel Add-Ins to manage: It may not be immediately obvious, but after enabling the add-in, all of the static methods contained in the IMSL C# Library are available for use in any cell function. They will appear in the list of available functions as you type into an Excel cell; the IMSL Library functions will be distinguishable because they use their mixed-case standard names unlike the built-in Excel functions that appear in all capital letters. For example, in Cell A1, if you start to type “=ga” you should see the following: |42 The “Gamma” and “GammaProb” functions are static methods in the Imsl.Math.Sfun class that have been loaded by the ExcelDna XLL. One caveat to this automatic loading of the full library is that method names that match the Excel built-in functions will not be available. So the Excel YIELD function trumps the Imsl.Finance.Bond.Yield static method; most of the methods in the Imsl.Finance namespace are therefore unavailable. To access these methods, they will need to be wrapped using different names. Many more interesting tasks can be accomplished using non-static classes in the IMSL C# Library. However, you must wrap the function call in a static method. As an example, consider the SignTest class in the Imsl.Stat namespace. Computing the probability requires an instance of the SignTest class and a double[] array in the constructor. Using ExcelDna, wrapping this class into a static method call could look like the following: <DnaLibrary Language="C#"> <Reference AssemblyPath= "C:\Program Files\VNI\imsl\imslcs650\bin\ImslCS.dll"/> <![CDATA[ using Imsl.Stat; public class ImslWrapper { public static double ImslSignTest(double[] val) { SignTest st = new SignTest(val); return st.Compute(); } } ]]> </DnaLibrary> You will notice standard C# code wrapped inside the XML tags. The default language is Visual Basic, so you need to specify the language as C# for this example. Create a new copy of the ExcelDna.xll file and name it SignTest.xll. Then create a file called SignTest.dna with the code shown above. To load this add-in, double-click on the SignTest.xll file and create a new blank workbook in Excel. For this example, we need some data to pass into the function, so use the |43 same values presented previously in the Workbook example. The following series of screenshots show the function name appearing on the list of available routines, referencing the range of input data, and the result: The ExcelDna project allows you to use any .NET language (C#, Visual Basic and F# examples are available) to write the code behind an XLL plug-in. The XLL interface is easy to configure in Excel and well known to many users and this is a convenient way to incorporate additional functionality into Excel at the spreadsheet cell level. BlueReference On the commercial side, the company blue reference15 offers a product called inference for .NET16 that provides a platform to create dynamic Excel and Word documents with scripts written in various languages including IronPython, which can easily call .NET numerical libraries. The solution resides in the ground between simple VBA macros and a full Visual Studio Office project as it allows the use of a scripting language, making it more accessible to nondevelopers. There is a blog post17 available that describes the use of inference for .NET with the IMSL C# Library describing the development steps to create a dynamic document that leverages the mathematical and charting features of the library. Excel4Net The relatively new Excel4Net18 project is an application development tool for combining Excel and .NET. This GUI-driven framework allows you to integrate custom functions into Excel. A 15 http://www.bluereference.com/ http://inferencefordotnet.com/ 17 http://inferencefordotnet.com/blog/Lists/Posts/Post.aspx?ID=10 18 http://www.excel4net.com/ 16 |44 custom Insert Function menu allows the user to map Excel table values to input arguments of public functions exposed in the .NET assembly. This is a free development kit, but may not yet support Excel 2010. This document described and showed examples of how to develop and deploy an Excel-based application that calls the IMSL C# Numerical Library through the Office Development with Visual Studio interface. Interactions are as easy as adding code behind the spreadsheet using any .NET language and calling the IMSL C# Library assembly like any other assembly reference. In a workbook project, converting spreadsheet cell areas to arrays and copying the arrays back to cell areas is accomplished using Named Ranges. Charting components can be added as if they were any other Windows Forms component. Excel Add-Ins such as custom task panes and ribbons are another powerful integration option and can host Windows Forms applications inside the Excel environment. Finally, deployment is easily achieved using the publishing wizard to package your customized solutions for distribution to end users. For additional information, refer to the various hyperlinked sources in this document, many of which lead to the MSDN web pages. If the Visual Studio interfaces do not meet your needs, a growing set of third party tools provide other options for integration with Microsoft Excel. Additional Expert Consulting Services19 are also available for your IMSL-based applications from Visual Numerics, A Rogue Wave Software Company. 19 http://www.vni.com/products/consulting/index.php |45