Wednesday, June 13, 2012

Excel User Defined Functions with C# and ExcelDna

Adding user defined functions to Excel can be laborious, either you use VBA or COM. Why can't Microsoft make it easier to use Visual Studio and .NET within Excel?

Enter ExcelDna.



This example creates a static function in C# using Visual Studio. There is documentation that already describes this, what I am adding is:

  • Use Visual Studio to create project
  • Post Build Events to help create the final XLL file.


NOTE: I hate Post Build Events. The ONLY exception is in this case where I am modifying the output of the final build. Post Build Events are Evil especially when used in a large development team. A better solutions is to create actual Build Scripts that do what I am about to do. You have been warned.

First, create a new Class Library project. Use NuGet to add a reference to the Excel-DNA package. NuGet will also add ExcelDna.dna and ExcelDna.xll to your project. Rename them both to the name that you want your final output xll to be. In my case I renamed them to Scratch-ExcelDna.dna and Scratch-ExcelDna.xll. Also for both files change to properties for Build Action to "Content" and Copy to Output Directory to "Copy Always".

Within the packages folder created by NuGet is the ExcelDnaPack utility, in the tools folder. This will package your project into one xll file. For it to work you need to update the .dna file:

<DnaLibrary RuntimeVersion="v4.0">
     <ExternalLibrary Path="DC.Scratch.ExcelDna.dll" Pack="true" />
</DnaLibrary>


Note that I am using .Net 4.

To get the pack utility to work, add a Pre-build event to delete the existing xll:

del "$(TargetDir)Scratch-ExcelDna-packed.xll"

Then add a Post-build event to recreate it:

"$(SolutionDir)packages\Excel-DNA.0.29\tools\ExcelDnaPack.exe" "$(TargetDir)Scratch-ExcelDna.dna"

While you are in the Project Properties messing with the evil build events set the Debug options so you can test your code. Set the External Program to you MS Excel and add a Command Line argument with a path to your final xll file.

Now, add a class TestFunctions:
[csharp]
using ExcelDna.Integration;

namespace DC.Scratch.ExcelDna
{
public class TestFunctions
{
[ExcelFunction(Description = "Product of two numbers", Category = "DNA Test")]
public static double TheProductOf(double x, double y)
{
return x*y;
}
}
}
[/csharp]

Hit F5 and see if Excel Starts. If it does, add some numbers to the Excel spreadsheet and see if the TheProduct() function works.

Download sample project here: DC.Scratch.ExcelDna

Microsoft should really look at buying Excel-DNA and incorporating it into Visual Studio. You Guys Listening!?!

Tuesday, May 22, 2012

Using Sencha Architect to Create Grafted Tree

Clint Harris posted How To Use ExtJS 4 TreePanels with Both Static Data and Model Stores. He has a great explanation of how to "graft" branches onto an ExtJS TreeView. In his example he is creating an Admin tree form that gives him access to Settings and Users.

I recreated his project using Sencha Architect 2.

The trickiest part was getting the tree to load the data correctly. I used the onLaunch function of the controller to get access to the TreeStores in order to call the userStore.setRootNode() and then to append the userStore to the settingsTreeStore.

Notice how I am using this.getUserTreeStoreStore() and this.getSettingTreeStoreStore(). Yes, I had to use "StoreStore" because I named my stores UserTreeStore and SettingTreeStore, then the Controller tacks on the second "Store" when it creates the get function.

Ext.define('GraftedTreeApp.controller.TreePanelController', {
 extend: 'Ext.app.Controller',

 models: ['UserModel'],
 stores: [
 'SettingTreeStore',
 'UserTreeStore'
 ],

 onLaunch: function() {
  var userStore = this.getUserTreeStoreStore();
  userStore.setRootNode({
   text: 'Users',
   leaf: false,
   expanded: false
 });

  // Graft our userTreeStore into the settingsTreeStore. Note that the call
  // to .expand() is what triggers the userTreeStore to load its data.
  var settingsTreeStore = this.getSettingTreeStoreStore();
  settingsTreeStore.getRootNode().appendChild(userStore.getRootNode()).expand();
 }

});



Pretty much everything else was setting properties in the Sencha Architect UI. The project files are here.

NOTE: There is a known bug in this solution, when you collapse and expand the User node it loads more User data, after which the User node cannot be collapsed.

Monday, March 26, 2012

C# Spirograph Point Generators

Spirograph's  are cool.  See here and here.



I put together three ways to generate points for a Spirograph, first using a Brute Force straight generate the points, second using a Parallel.For and third using LINQ.