Author Archives: purpleblob

Caliburn Micro, convention based binding

Caliburn Micro implements a convention based system for binding to “default” properties on controls and actions etc. This is on by default but can be switched off using ViewModelBinder.ApplyConventionsByDefault and setting this to false. You can also enable/disable conventions on a view-by-view basis by setting the attached property View.ApplyConventions to true in a view, for example (with other namespace etc. removed)

<UserControl x:Class="Sample.ShellView"
   xmlns:Micro="clr-namespace:Caliburn.Micro;assembly=Caliburn.Micro"
   Micro:View.ApplyConventions="False">

Let’s take a look at some basics of the convention based code.

In a view model class (assuming you’re derived from PropertyChangedBae) we can add the following code

private string fullName;

public string FullName
{
   get { return fullName; }
   set
   {
      if(fullName!= value)
      {
         fullName= value;
         NotifyOfPropertyChange(() => FullName);
      }
   }
}

Now, in the corresponding view add the following

<TextBox Name="FullName" />

Note the use of the name property and no binding property, this is because Caliburn Micro creates the link for us based upon the name and the binding property defined within it for mapping within Caliburn Micro (in other words, it’s binding based upon a naming convention).

For example the convention for a TextBox is that the binding property to be used is the Text property. Internally this looks like this

AddElementConvention<TextBlock>(TextBlock.TextProperty, "Text", "DataContextChanged");

We can, in fact, add our own convention based bindings for any controls not supported by default such as our own controls. For example, let’s create a convention based binding for a Rectangle object (which isn’t one of the default supported controls) whereby the convention based binding will look like this in XAML.

<Rectangle Width="100" Height="100" Name="Colour" />

and in our code (somewhere such as the top most view model or boostrapper) we would have something like this

ConventionManager.AddElementConvention<Rectangle>(Shape.FillProperty, "Fill", "FillChanged");

This will then create a binding from the name “Colour” on our viewmodel to the Fill property of the Rectangle (or Shape). Thus the Colour (obviously assuming it is a Brush) will be bound to the Fill property automatically.

The last argument in the AddElementConvention method is for the eventName and this can be used to use the same mechanism to fire commands on our view model.

Let’s add a Button to our code that looks like this, in XAML

<Button Name="Save" Content="Save" />

Now in the associated view model simply add the following code

public void Save()
{
}

It’s as simple as that to bind to the click event of the button. So, when the button is clicked the Save method on the view model is called. This is because of the following code, which Caliburn Micro

AddElementConvention<ButtonBase>(ButtonBase.ContentProperty, "DataContext", "Click");

One very useful bit of code in Caliburn Micro is for situations where you might wish to pass an argument to a method in a view model. Let’s assume that you have multiple save buttons on a form, one for draft save and one for full save (for want of a better example).

We want to use the same code on the view model to handle both saves, however depending on the argument pass to the method we will do a draft of full save. Maybe these two operations share a bunch of code so it suits us to reuse this code. Okay so it’s a slightly convoluted example, but you get the idea.

So our method will look like this

public void Save(string draftOrFull)
{
   // do a load of shared things

   if(draftOrFull == "draft")
   {
      // draft save
   }
   else if(draftOrFull == "save")
   {
      // full save
   }
}

Now add the following code to the XAML

<Button Content="Draft" cal:Message.Attach="[Event Click] = [Action Save('draft')]"/>
<Button Content="Save" cal:Message.Attach="[Event Click] = [Action Save('full')]"/>

The attached property Message.Attach will attach the Click event to the action “Save” passing the string draft or full – note the single quotes for the string.

We can achieve the same as above in a longer format as follows

<Button Content="Draft">
   <i:Interaction.Triggers>
      <i:EventTrigger EventName="Click">
         <cal:ActionMessage MethodName="Save">
            <cal:Parameter Value="draft" />
         </cal:ActionMessage>
      </i:EventTrigger>
   </i:Interaction.Triggers>
</Button>

Caliburn Micro and inversion of control using Ninject

Caliburn Micro comes with it’s own built in mechanism for creating objects as and when required. However it’s bootstrapper comes with methods which allow us to override the default behaviour. The methods such as GetInstance, GetAllInstances and BuildUp are used to resolve dependencies in a user supplied IoC container.

I’m the built in mechanism is more than adequate for most peoples usage, but I tend to rather like Ninject. So here are the steps (which ofcourse can be used with your own preferred IoC framework).

Create a bootstrapper as per the following (where ShellViewModel is replaced with your view model name)

public class AppBootstrapper : Bootstrapper<ShellViewModel>
{
   protected override void Configure()
   {
   }

   protected override void OnExit(object sender, EventArgs e)
   {
   }

   protected override object GetInstance(Type service, string key)
   {
   }

   protected override IEnumerable<object> GetAllInstances(Type service)
   {
   }

   protected override void BuildUp(object instance)
   {
   }
}

These are the methods we need to override and implement the code for, to allow Caliburn Micro to use our preferred Ioc framework.

Configure

The configure method is used to configure Caliburn Micro to use our IoC framework, so basically this is where we instantiate the StandardKernel in Ninject. Firstly add a class level variable as follows

private IKernel kernel;

Next override the Configure method to both create the kernel and set-up default bindings

protected override void Configure()
{
   kernel = new StandardKernel();

   kernel.Bind<IWindowManager>().To<WindowManager>().InSingletonScope();
   kernel.Bind<IEventAggregator>().To<EventAggregator>().InSingletonScope();
}

We’re going to want to have access to the WindowManager and EventAggregator within our code, so we’ll set up the bindings for them. If we’re passing a class to the generic argument of the Bootstrapper this this is enough code for the Configure method, but if, as I often prefer, we have something like

public class AppBootstrapper : Bootstrapper<IShellViewModel>

i.e. an interface passed as the generic argument, then we need to also set up the bindings within Ninject to resolve this interface. Hence adding the line

kernel.Bind<IShellViewModel>().To<ShellViewModel>().InSingletonScope();

to the end of the Configure method.

OnExit

Now we’ve created the instance of the kernel, the OnExit method allows us to place cleanup code such as

protected override void OnExit(object sender, EventArgs e)
{
   kernel.Dispose();
   base.OnExit(sender, e);
}

This is (as you can see from the arguments, a event handler that Caliburn Micro hooks up to the Application.Exit event.

GetInstance

This must be overridden when providing our own IoC container. The method is used get the service for a given service, so we can simply call the following

protected override object GetInstance(Type service, string key)
{
   if (service == null)
      throw new ArgumentNullException("service");

   return kernel.Get(service);
}

GetAllInstances

This method must be overridden when supplying our own IoC container and is used to get all instances of a service. We can override it thus

protected override IEnumerable<object> GetAllInstances(Type service)
{
   return kernel.GetAll(service);
}

BuildUp

Finally, and again required when supplying our own IoC container, we need to override the BuildUp method. This is used inject instances into the IoC container and can be written as

protected override void BuildUp(object instance)
{
   kernel.Inject(instance);
}

Full Code

The full code for this is as follows

public class AppBootstrapper : Bootstrapper<ShellViewModel>
{
   private IKernel kernel;

   protected override void Configure()
   {
      kernel = new StandardKernel();

      kernel.Bind<IWindowManager>().To<MetroWindowManager>().InSingletonScope();
      kernel.Bind<IEventAggregator>().To<EventAggregator>().InSingletonScope();

      kernel.Bind<IShellViewModel>().To<ShellViewModel>().InSingletonScope();
   }

   protected override void OnExit(object sender, EventArgs e)
   {
      kernel.Dispose();
      base.OnExit(sender, e);
   }

   protected override object GetInstance(Type service, string key)
   {
      if (service == null)
         throw new ArgumentNullException("service");
			
      return kernel.Get(service);
   }

   protected override IEnumerable<object> GetAllInstances(Type service)
   {
      return kernel.GetAll(service);
   }

   protected override void BuildUp(object instance)
   {
      kernel.Inject(instance);
   }
}

Beginning with Caliburn Micro

Let’s take a look at some of the basics of Caliburn Micro.

Let’s start with the bare minimum, skeleton app to get us started…

  1. Create a WPF application is Visual Studio
  2. Use NuGet to add Caliburn.Micro via the references context menu
  3. Open App.xaml and remove the StartupUri=”MainWindow.xaml”
  4. Delete the file MainWindow.xaml as Caliburn Micro will create the main window for our application
  5. Caliburn Micro uses naming convention to allow it to load the relevant view for the view model, so the view should be prefixed with the same name as the viewmodel, i.e. create a class named ShellViewModel which will become the entry point to the application. Then create a UserControl named ShelllView
  6. Open ShellViewModel and drived the class from PropertyChangedBase (ass the using clause for Caliburn.Micro).

    PropertyChangedBase gives us the implementation of INotifyPropertyChanged for the databinding. So the code should look like

    public class ShellViewModel : PropertyChangedBase
    {
    }
    

    Obviously don’t forget to add Caliburn.Micro as a using clause

  7. Open the ShellView.xaml file and (just so we can easily see the user control’s usage, change the Background to a colour of your choosing. Also changed the Width and Height to a sensible starting value otherwise the application will open just as the caption bar. The user control size gives the initial size of the main window when it’s displayed
  8. Now we need to create the code to actually create the main window and hook display our intial control. Create a new class, it can be named anything you like but I’ll named mine AppBootstrapper to fit in with the App.xaml name
  9. Derive AppBootstrapper from Bootstrapper as per
    public class AppBootstrapper : Bootstrapper<ShellViewModel>
    {
    }
    

    Obviously don’t forget to add Caliburn.Micro as a using clause.

    As you’ll see we set the Bootstrapper up with the view model, not the view. Caliburn Micro’s naming conventions will work out the view name as ShellView and handle the composition of the views from this.

  10. At this point we have all the bits for Caliburn Micro to work but we need to actually get the bootstrapper to load. The simplest way is to edit App.xaml and enter the following code between the Application.Resources

    <ResourceDictionary>
    <ResourceDictionary.MergedDictionaries>
    <ResourceDictionary>
    <local:AppBootstrapper x:Key="bootstrapper" />
    </ResourceDictionary>
    </ResourceDictionary.MergedDictionaries>
    </ResourceDictionary>

    Obviously added the namespace as required.

Now if you run this you should see an application window with the dimensions based upon the size of the ShellView control and if you chose a different colour background this should be displayed.

Attributes on return values in C#

What’s the C# syntax for applying an attribute to a return type ?

[return: SomeAttribute()]
public string GatValue()
{
    return"Some Value";
}

Here’s a simple example of a custom attribute used for return types

[AttributeUsage(AttributeTargets.ReturnValue, AllowMultiple = false)]
class MyAttribute : Attribute
{
   public string Description { get; set; }
}

public class SomeClass
{
   [return: MyAttribute(Description = "A Return Value")]
   public string GetValue()
   {
      return "Hello World";
   }
}

class Program
{
   static void Main(string[] args)
   {
      object[] attributes = typeof (SomeClass).
                  GetMethod("GetValue").
                  ReturnTypeCustomAttributes.
                  GetCustomAttributes(false);
   }
}

This will return a single item in the object[] of type MyAttribute.

A Real Time Data (RTD) Server using Excel-DNA in Excel

Creating an RTD server/function in Excel-DNA is very simple.

An RTD function in Excel is basically a function that continually updates Excel, for example a cell might show the current price of a stock without the Excel user having to refresh/recalc.

I’m going to take the code a sample written by Govert van Drimmelen’s on the Excel-DNA google group and work through it to show what does what. His code demonstrates code for a time service/server. i.e. it will supply the current time to Excel and update it every 500ms.

  1. Create a class library named RtdFunctions (or whatever you like)
  2. Using NuGet add the Excel-DNA package to the project
  3. Add the packages\Excel-DNA.0.30.3\tools\ExcelDna.xll file to your project and rename to match the project but suffixed -AddIn.xll, i.e. mine is RtdFunctions-AddIn.xll set it’s “Copy to Output Directory” property to “Copy if newer”. You could ofcourse handle the copy within the post build step if you prefer.
  4. Rename the Class1.cs file to TimerServer.cs

We’ve now created a bare bones of this project, it’s time to now implement the TimeServer. Whilst we could implement the IRtdServer interface ourselves. The Excel-Dna assembly includes a thread-safe implementation already named ExcelRtdServer, so let’s use that so we can concentrate on the actual RTD functionality.

So…

  1. Derive the new TimeServer class from ExcelRtdServer, you’ll need to add the using clause for ExcelDna.Integration.Rtd
  2. If we want the potential to support multiple topics we’ll need a list of topics, ExcelRtdServer wraps topic id’s in a Topic object so add
    private readonly List<Topic> topics = new List<Topic>();
    

    to our TimerServer

  3. We are going to need to override three methods from the ExcelRtdServer, ServerTerminate, ConnectData and DisconnectData, so go ahead and override those and then we’ll implement the functionality in each
  4. As the purpose of this TimerServer is to supply the time using a push style notification, we’re going to add a Timer to the class, I’m using the System.Threading.Timer so add the following
    private Timer timer;
    
    public TimeServer()
    {
       timer = new Timer(Callback);
    }
    
    private void Start()
    {
       timer.Change(500, 500);
    }
    
    private void Stop()
    {
       timer.Change(-1, -1);
    }
    
    private void Callback(object o)
    {			
       Stop();
       foreach (Topic topic in topics)
       {
          topic.UpdateValue(GetTime());
       }
       Start();
    }
    
    private static string GetTime()
    {
       return DateTime.Now.ToString("HH:mm:ss.fff");
    }
    

    As you can see the Callback method, which is triggered on a timer event, will loop through the topics calling UpdateValue with results from GetTime, in other words it will tell each topic to update to the latest time.

  5. Amend the ServerTerminate method to dispose of the timer, this will be called when Excel terminates any use of the TimrServer, so we can safely clean up here, so ServerTerminate should look like this
    protected override void ServerTerminate()
    {
       timer.Dispose();
       timer = null;
    }
    
  6. Next we want to handle the ConnectData implementation. Each time the server is connected to we will cache the topic in our topics like and ensure the timer is started before returning the current time
    protected override object ConnectData(Topic topic, IList<string> topicInfo, ref bool newValues)
    {
       topics.Add(topic);
       Start();
       return GetTime();
    }
    

    The ConnectData method is called when a spreadsheet is opened with the RTD functions or, ofcourse, when you add a function call to the server method

  7. Finally we want to implement the DisconnectData method to remove the cached topic and possible stop the service, so add the following
    protected override void DisconnectData(Topic topic)
    {
       topics.Remove(topic);
       if (topics.Count == 0)
          Stop();
    }
    

So the full code is listed here

public class TimeServer : ExcelRtdServer
{
   private readonly List<Topic> topics = new List<Topic>();
   private Timer timer;

   public TimeServer()
   {
      timer = new Timer(Callback);
   }

   private void Start()
   {
      timer.Change(500, 500);
   }

   private void Stop()
   {
      timer.Change(-1, -1);
   }

   private void Callback(object o)
   {			
      Stop();
      foreach (Topic topic in topics)
         topic.UpdateValue(GetTime());
      Start();
   }

   private static string GetTime()
   {
      return DateTime.Now.ToString("HH:mm:ss.fff");
   }

   protected override void ServerTerminate()
   {
      timer.Dispose();
      timer = null;
   }

   protected override object ConnectData(Topic topic, IList<string> topicInfo, ref bool newValues)
   {
      topics.Add(topic);
      Start();
      return GetTime();
   }

   protected override void DisconnectData(Topic topic)
   {
      topics.Remove(topic);
      if (topics.Count == 0)
         Stop();
   }
}

This is all well and good but to use this we need to use the =RTD syntax in Excel and to do this we’ll need to add a Guid to the class and mark the project, via the project properties (build tab), as Register for COM interop and then register the component.

It’s far simpler and less messy to now create a new class, let’s call it TimeFunctions as follows

public static class Timefunctions
{
   public static object GetCurrentTime()
   {
      return XlCall.RTD("RtdFunctions.TimeServer", null, "NOW");
   }
}

This is now exposed to Excel and will use XlCall.RTD to call into our RTD server so bypassing any issues with COM etc.

Creating an XLL add-in using Excel-DNA

On a previous post I walked through creating an XLL add-in using C# and ManagedXLL for Excel. This is a commercial library which offers many options for interacting and integrating with Excel. But there’s a nice, open source, alternative called Excel-DNA, if you prefer to go this way.

So let’s take a look at creating a Excel add-in using Excel-Dna.

  1. Let’s start off by creating a class library in C# (I’m going to call mine ExcelDNATest, I know it’s not very original)
  2. Using NuGet, add the Excel-DNA package to your solution
  3. Copy and rename packages\Excel-DNA.0.30.3\tools\ExcelDna.xll to projectname-AddIn.xll, so in my case that’s ExcelDNATest-AddIn.xll
  4. We need to copy this file to the output folder so either create a post build step to copy this or I simply added it to the project and marked it’s “Copy to Output Directory” to “Copy if newer”
  5. You’ll notice that the NuGet package added a file named projectname-AddIn.dna to the project. This is the config file for the XLL add-in we’re creating. If you open it you should see an ExternalLibrary element which has the name of the DLL we’re going to build.

At this point we’ve created the barebones for our DLL which the information which allows the XLL file to call into our DLL which we shall use to contain the Excel functions.

You should have found the NuGet package added a file ExcelDna.Integration.dll to our project. This contains the attributes and other code which allows us to expose Excel functions from our code and for us to talk to Excel.

So at this point, to recap, we’ve created a DLL, we’ve added Excel-DNA to the project. We’ve copied/renamed the XLL file to match out project and ensure the .dna configuration file points to our DLL name. Now it’s time to have a little more fun…

Let’s add a function to our class and usable from Excel

  1. Rename your Class1.cs file to MyFunctions.cs (or whatever you prefer)
  2. Open the class file make the class static
  3. Add a static method as follows
    [ExcelFunction(Name="MyFunctions.Hello", Description="Hello X", 
    Category="My Excel Functions"]
    public static string Hello([ExcelArgument("Enter your name")] string name)
    {
       return "Hello " + name;
    } 
    

    We’ve now got a function which will appear in the Excel function wizard in the category “My Excel Functions”. The function will be named MyFunction.Hello and has the description “Hello X”.

  4. Simply type

    =MyFunctions.Hello(“Mark”)

    in your spreadsheet cell to see the function in action.

Alternatively create the code in the DNA file

I’m not sure how useful this is, but you can also create UDF (user-defined functions) inside the .DNA file.

For example

<DnaLibrary Name="ExcelDNATest Add-In" RuntimeVersion="v4.0" Language="CS">
  <![CDATA[
  using ExcelDna.Integration;
  
  public class SomeMoreFunctions
  {
     [ExcelFunction(Description="Test Function")]
     public static string Test()
     {
        return "This is a test function";
     }
  }
  ]]>
</DnaLibrary>

And the rest is as per the compiled source code, i.e. it’s available in the function wizard etc.

Using ManagedXLL to supply functions to Excel

In the last post I went through the steps for creating a class library for use with ManagedXLL to create an Excel add-in. We very briefly encountered the ManagedXLL way of marking methods for use in Excel.

Now I’m going to delve a little deeper into what we can do with ManagedXLL. This will not be comprehensive, but will highlight some of the key features of ManagedXLL.

ExcelAddInEvent

We can mark a method with this attribute to carry out a task when the add-in is loaded (AddInEvent.Open), allowing us to carry out specific functionality, such as maybe getting an SSO token for the user or the likes. We can also use mark a method to be called when an add-in is being closed, i.e. when a workbook is closed, for example to allow us to save data to the cloud using AddInEvent.Close. We can also use AddInEvent.Add and AddInEvent.Remove to carry out a task when the add-in is added to the add-in manager or removed from it.

[ExcelAddInEvent(AddInEvent.Open)]
public static void OnLoaded()
{
}

A couple of subclasses exist to handle Open and Close, named ExcelAutoOpen and ExcelAutoClose.

Excel Object

Before moving onto more attributes, let’s quickly look at the ManagedXLL Excel object. It includes methods to interact with Excel from our code, such as displaying a message box in Excel or getting at the sheets collection and so on, for example

Excel.MessageBox("You are not authorised to connect to the selected service", Excel.MessageBoxType.ErrorOK, "MyExcelAddin");

WorksheetFunction

The WorksheetFunction attribute is used to tell Excel, not only the name of the function (as seen by Excel users), but also how Excel should use the method in the sense of how it should display exceptions and whether to detect the user aborting of the method.

[WorksheetFunction("CalculateFee", DetectUserAbort = true, Exceptions = Echo.Verbatim)]
public static double CalculateFee()
{
}

ExcelDescription

We can mark methods, arguments and return values with the ExcelDescription attribute, allowing us to document what the various arguments and/or return values of a method are as well as what the method itself does.

[ExcelDescription("Gets a string based upon the supplied index")]
[return: ExcelDescription("Returns the selected string")]
public static string DoSomething([ExcelDescription("The Index")] int index)
{
}

ExcelHidden

We can mark methods and hidden to hide them from the function wizard using the ExcelHidden attribute, this doesn’t stop us using the method, it just doesn’t appear in the function wizard

[WorksheetFunction("MyName", DetectUserAbort = true, Exceptions = Echo.Verbatim)]
[ExcelDescription("Returns a string with the name of the add-in.")]
[return: ExcelDescription("A string representing the name of the add-in")]
[ExcelHidden]
public static string Name()
{
}

ExcelCategory

The ExcelCategory is used on a class and allows us to create a category for groups of functions – this aids the function wizard in grouping the methods. You can create multiple classes and give them the same category or group together specific functionality into their own categories.

[ExcelCategory("My Date & Time Functions")]
public static class DateTimeFunctions
{
}

ExcelAbbreviation

We can mark individual arguments of a method using this attribute. In essence we’re defining an alternate name for the parameter as viewed by Excel’s function wizard.

public static string Name([ExcelAbbreviation("Name")] String enterYourName)

ExcelCommand

The ExcelCommand attribute allows us to define methods that are registered as Excel macro functions which can be run from the command’s listed in the Managed.XLL.config

[ExcelCommand(".Run")]
public static void Run()
{
}
<command title="My Run Command" macroName=".Run"
    description="Runs a command" edition="Professional"/>

This will add a command to the Excel menu and when clicked will run our macro/

Creating an XLL add-in using ManagedXLL

Excel allows us to extend it’s programming/functional model by adding in new components known as Excel addin’s (or XLL files). There are several ways of creating these in .NET. For this post I’m going to concentrate on a commercial product known as ManagedXLL.

Let’s just straight into creating a skeleton project for an XLL add-in.

  1. Let’s start by creating a new class library, I’m going to call mine (the nondescript name) MyExcelPlugin.
  2. Now add a reference to ManagedXLL.dll
  3. Next we need to copy and rename the Managed.XLL.config which comes with ManagedXLL. Copy it to your project’s folder (you can ofcourse place it where you like) and name it projectname.XLL.config (obviously replacing projectname with your project’s name) then add it to the project and set it’s “Copy to Output Directory” property in Visual Studio to “Copy if newer”
  4. Next we need to copy the Managed.XLL file to our project. Copy it and rename it projectname.XLL, where project name is your project’s name. Add it to the project and set the “Copy to Output Directory” property in Visual Studio to “Copy if newer”.
    Note: Both the projectname.XLL and projectname.XLL.config files must be available in the same locations.
  5. If you’ve not done so already, change the name of the Class1 file which was created with the project to MyExcelFunctions (or whatever you prefer)

Okay so to recap, we’ve created a class library, reference ManagedXLL.dll and copied (and renamed) Managed.XLL and Managed.XLL.config. Great but what have we really done ?

So the ManagedXLL.dll supplies us with attributes which will will use in a later section to mark out code for use in Excel. It also supplies functions for interacting with Excel. The Managed.XLL file is the add-in we use in Excel. We rename it so that obviously there’s (hopefully) no conflicts with other add-ins and the same goes with renaming the Managed.XLL.config. The Managed.XLL file is the runtime that Excel communicates with and it in turn will communicate with our DLL. So it acts as the proxy to our DLL. As is probably obvious at this point, the Managed.XLL.config will supply information to Managed.XLL about our DLL, such as whether we want menu’s displayed, the name of the add-in and so on.

Let’s make some changes to the Managed.XLL.config (now obviously renamed for our project).

Note: We can also use the XLL.config for any configuration required by our add-in, for example if there’s WCF config required or just plain old appSettings.

  1. The first thing we’ll want to do is change the display name to reflect our add-in. So open the XLL.config file and scroll down to to the managedxll/settings. Here you will see the displayName so change the value attribute to the project name, for example MyExcelPlugin or whatever name you want to have Excel display
  2. We now need to tell ManagedXLL the name of the assembly that it’s acting as a proxy to, so find the assemblies section in the config file and change the location to the name of your assembly. You’ll notice in the config comments for this section that there’s various ways of declaring your assembly’s location.
  3. The last thing we’re going to add, for this section at least, is the license information. Locate the licenses section in the config file and supply the relevant information in the data and hash attributes

At this point we’re created the bare minimum of a DLL that we can load into Excel. Ofcourse it does nothing, but if you want to load up Excel and select Tools | Add-ins, then browse to the bin\debug or bin\release (whichever you’re building) of your project and load the .XLL you named after your project. Then press OK in the Add-ins dialog in Excel to accept it, this will add a menu entry named ManagedXLL (we can change this easily enough) and if all goes well no errors are displayed, i.e. the ManagedXLL debug console does not get displayed with any error messages.

Note: if you now make changes to the project and compile you will get “Unable to copy xxx because it is being used by another process” errors. This is because, you’ve probably got Excel running with the Add-in loaded. Either close Excel or go back to the Add-ins dialog in Excel and remove the Add-in by unchecking it.

Time to add something useful, some code

  1. Open the class file, mine’s called MyExcelFunctions.cs and change the class to a static and add the ExcelCategory attribute to the class, as follows
    [ExcelCategory("My Excel Functions")]
    public static class MyExcelFunctions
    {
    }
    

    This will add a category title for display in the worksheet functions dialog etc.

  2. Now let’s add an actual Excel callable function. This will not be anything special, but will simply return a string. However it also demonstrates how easy it is to now create functions for Excel.

    Add the following code to our class

    [WorksheetFunction("MyName", DetectUserAbort = true, Exceptions = Echo.Verbatim)]
    [ExcelDescription("Returns a string with the name of the add-in.")]
    [return: ExcelDescription("A string representing the name of the add-in")]
    public static string Name()
    {
       return "My Excel Functions";
    }
    

    If you now build this and add the add-in to Excel (it may prompt you to say do you want to overwrite the existing plugin (or words to that effect), say yes to this.

    Once the add-in is loaded, select a cell then go to Insert | Function option in Excel and if you select the “Or select a category” combo box you can scroll down and find the text we supplied for the ExcelCategory, for example “My Excel Functions”. From here we can select our function. As there are no arguments for this function you’ll see very little of interest but if you click on “Help on this function” you’ll see the return description we set up in our class.

    Anyway, click OK on the function dialog and it’ll enter

    =MyName()

    into Excel and display the string our class returns. Ofcourse you do not need to go through the Functions dialog everytime, this was just to show the category and return description are available.

    Note: Notices that I wrote =MyName() and not =Name() as per our method name. As you might have spotted in the WorksheetFunction we declare the name of the function that Excel see’s/exposes. Thus allowing us to alter the name of the method for Excel.

So at this point we should have a basic, but working, Excel add-in.

Minimal steps to create a TIBCO RV message and send it

The previous post was on creating a minimal TIBCO RV listener. So naturally it’s time to look at creating a minimal TIBCO RV “sender”. In other words, creating a message and sending it.

Note: The initial code was take from the C# TIB RV samples, but I wanted to distil these to a quick and simple step by step guide.

For this sample we’ll create a Console application in C# and we’ll assume you are getting the server, network etc. values from some key/value mechanism, whether this is a command line parser or config is down to your specific implementation.

So create a Windows Console project then follow the steps below (the full code will be listed at the end of this rambling)

In case you’ve come to this post without first reading the post on creating a minimal listener, I will repeat a fair amount of code/steps from that post here.

  1. Add references to TIBCO.Rendezvous and ensure that TIBCO.Rendezvous.netmodule resides in the same folder as TIBCO.Rendezvous (just in case you’re not referencing the TIB RV installation itself
  2. For this simple example we’ll hard code a few variables
    string server = arguments["server"];
    string network = arguments["network"];
    string daemon = arguments["daemon"];
    
  3. Next we need to open the Rendezvous environment using

    TIBCO.Rendezvous.Environment.Open();
    

    The above code should be enclosed in a try..catch block, either enclosing all of the following code or if you want the granularity, then each line in essence. For now we’ll just assume it’s all in a try..catch block

  4. We now need to create the transport

    Transport transport = new NetTransport(service, network, daemon);
    
  5. Now it’s time to create the message. The subject is set to “myapp.mysubject” and we add some data to a new field we’ve created named “Data”

    Message message = new Message { SendSubject = "myapp.mysubject" };
    message.AddField("Data", "Some Data");
    
  6. To send the message is simply a case of

    transport.Send(message);
    
  7. Finally when we’ve finished with RV we need to close the environment with

    TIBCO.Rendezvous.Environment.Close();
    

Before we look at the full code, here’s what the listener shows us if we listen to “myapp.>”

2013-11-21 15:58:49 (2013-11-21 15:58:49.281000000Z): subject=myapp.mysubject, message={Data=”Some Data”}

And as promised, the full code is

public void Process(CommandLineArguments arguments)
{
   string service = arguments["server"];
   string network = arguments["network"];
   string daemon = arguments["daemon"];

   try
   {
      TIBCO.Rendezvous.Environment.Open();

      Transport transport = new NetTransport(service, network, daemon);

      Message message = new Message { SendSubject = "myapp.mysubject" };
      message.AddField("Data", "Some Data");
      transport.Send(message);
   }
   catch (Exception e)
   {
      Console.WriteLine(e.Message + "\n\n" + e.StackTrace);
   }
   finally
   {
      TIBCO.Rendezvous.Environment.Close();
   }
}

Minimal steps to create a TIBCO RV listener

Following is a list of the steps (and code) for creating a minimal TIB RV listener which will simply output messages to the console (therefore implementing the same sort of functionality as the tibrvlisten application.

Note: The initial code was take from the C# TIB RV samples, but I wanted to distil these to a quick and simple step by step guide.

For this sample we’ll create a Console application in C# and we’ll assume you are getting the server, network etc. values from some key/value mechanism, whether this is a command line parser or config is down to your specific implementation.

So create a Windows Console project then follow the steps below (the full code will be listed at the end of this rambling)

  1. Add references to TIBCO.Rendezvous and ensure that TIBCO.Rendezvous.netmodule resides in the same folder as TIBCO.Rendezvous (just in case you’re not referencing the TIB RV installation itself
  2. For this simple example we’ll hard code a few variables
    string server = arguments["server"];
    string network = arguments["network"];
    string daemon = arguments["daemon"];
    string topics[] = arguments["topics"].Split(',');
    
  3. Next we need to open the Rendezvous environment using

    TIBCO.Rendezvous.Environment.Open();
    

    The above code should be enclosed in a try..catch block, either enclosing all of the following code or if you want the granularity, then each line in essence. For now we’ll just assume it’s all in a try..catch block

  4. We now need to create the transport

    Transport transport = new NetTransport(service, network, daemon);
    
  5. Now create the listeners and attach a MessageReceived handler to each listener.

    TIBCO.Rendezvous.Listener[] listeners = new TIBCO.Rendezvous.Listener[topics.Length];
    for (int i = 0; i < listeners.Length; i++)
    {
       listeners[i] = new TIBCO.Rendezvous.Listener(Queue.Default, 
                         transport, topics[i], null);
       listeners[i].MessageReceived += OnMessageReceived;
    }
    

    The code for the OnMessageReceived handler looks like

    static void OnMessageReceived(object listener, 
                  MessageReceivedEventArgs messageReceivedEventArgs)
    {
       Message message = messageReceivedEventArgs.Message;
    
       Console.Out.WriteLine("{0}: subject={1}, reply={2}, message={3}",
    	DateTime.Now, message.SendSubject, message.ReplySubject, message);
    
       Console.Out.Flush();
    }
    
  6. Now for this example we’re going to go into a look and just keep calling the Queue.Default.Dispatch method to keep messages dispatching or block whilst no messages exist

    while (true)
    {
       try
       {
          Queue.Default.Dispatch();
       }
       catch (RendezvousException exception)
       {
          Console.Error.WriteLine("Exception dispatching default queue:");
          Console.Error.WriteLine(exception.StackTrace);
          break;
       }
    }
    
  7. Finally when we’ve finished with RV we need to close the environment with

    TIBCO.Rendezvous.Environment.Close();
    

Full code

public void Run(CommandLineArguments arguments)
{
   string service = arguments["server"];
   string network = arguments["network"];
   string daemon = arguments["daemon"];

   string[] topics = arguments["topics"].Split(',');
			
   try
   {
      TIBCO.Rendezvous.Environment.Open();

      Transport transport = new NetTransport(service, network, daemon);

      TIBCO.Rendezvous.Listener[] listeners = new TIBCO.Rendezvous.Listener[topics.Length];
      for (int i = 0; i < listeners.Length; i++)
      {
         listeners[i] = new TIBCO.Rendezvous.Listener(Queue.Default, 
                               transport, topics[i], null);
         listeners[i].MessageReceived += OnMessageReceived;
      }

      while (true)
      {
         try
         {
            Queue.Default.Dispatch();
         }
         catch (RendezvousException exception)
         {
             Console.Error.WriteLine("Exception dispatching default queue:");
             Console.Error.WriteLine(exception.StackTrace);
             break;
         }
      }
   }
   catch (Exception e)
   {
      Console.WriteLine(e.Message + "\n\n" + e.StackTrace);
   }
   finally
   {
      TIBCO.Rendezvous.Environment.Close();
   }
}

static void OnMessageReceived(object listener, 
         MessageReceivedEventArgs messageReceivedEventArgs)
{
   Message message = messageReceivedEventArgs.Message;

   Console.Out.WriteLine("{0}: subject={1}, reply={2}, message={3}",
		DateTime.Now, message.SendSubject, message.ReplySubject, message);

   Console.Out.Flush();
}