I’ve been working on my Gold Exam and a good chunk of it is written in SIMPL#. It’s been a good reminder that getting code in SIMPL# to play nicely with SIMPL logic can sometimes turn into a chore. We have SIMPL+ to thank for most of the hair-pulling. I thought a post about delegates and getting them to work in SIMPL+ would be a good thing to write down.
What is a delegate?
Before we dive into any code, what exactly is a delegate? Microsoft has a ton of great information posted on their learning site that you should read. If we boil down that page of text we get:
A delegate defines a method signature that can be passed around or used as a callback. They are fully object-oriented and have an instance object and method.
A method signature is just the return type and parameter types. Say we have a method to calculate the area of a circle:
public double CircleArea(double radius) { ... }
The signature of this method is that it returns a double value and takes one double parameter. This probably isn’t a good way of doing this, but say we wanted to turn this into a delegate type instead? We would define the delegate at the class level:
public delegate double AreaCalculation(double side);
Now our program is free to plug-in any compatible method to calculate an area. We could have a few methods defined to do the actual work:
public double CircleArea(double radius) { ... }
public double SquareArea(double side) { ... }
public double StarArea(double leg) { ... }
But remember, AreaCalculation is a delegate type, so we need to define an instance variable to assign our callbacks to:
public AreaCalculation Area;
Lets put all of this into a Console application that we can play with:
using System;
namespace ConsoleApplication1
{
class DelegateExample
{
public delegate double AreaCalculation(double side);
public AreaCalculation Area;
}
class Program
{
static void Main(string[] args)
{
var ex1 = new DelegateExample();
ex1.Area = CircleArea;
Console.WriteLine("Area of circle: {0}", ex1.Area(1.0));
ex1.Area = SquareArea;
Console.WriteLine("Area of square: {0}", ex1.Area(1.0));
ex1.Area = StarArea;
Console.WriteLine("Area of star: {0}", ex1.Area(1.0));
}
static double CircleArea(double radius)
{
return Math.PI * radius * radius;
}
static double SquareArea(double side)
{
return side * side;
}
static double StarArea(double leg)
{
return CircleArea(leg) * 2 / 3;
}
}
}
For simplicity, lets just say the area of a star is 2/3 the area of an equal-sized circle. We first create a new DelegateExample object on line 16. After assigning each of our AreaCalculation methods to the Area property, we perform a calculation and print the result.
Area of circle: 3.14159265358979
Area of square: 1
Area of star: 2.0943951023932
Just what exactly is happening on line 18 where we assign ex1.Area?
ex1.Area = CircleArea;
This is actually shorthand for the following code:
ex1.Area = new DelegateExample.AreaCalculation(CircleArea);
Remember that the Area property is a delegate object. Calling Area is shorthand for this code:
Console.WriteLine("Area of circle: {0}", ex1.Area.Invoke(1.0));
A delegate instance is just an object that knows how to call a method. If we wanted to ask the delegate which method it’s going to call, we could inspect it by using:
ex1.Area = new DelegateExample.AreaCalculation(CircleArea);
Console.WriteLine("Method.Name = {0}", ex1.Area.Method.Name);
The result would be:
Method.Name = CircleArea
C# is a very object-oriented language! What’s even cooler about delegates is that they can be multicast, meaning we can chain a bunch of handlers together. We’ll use a different example for this one:
using System;
namespace ConsoleApplication2
{
class MulticastDelegateExample
{
public delegate void Procedure();
public Procedure Start;
public Procedure Stop;
}
class Program
{
static void Main(string[] args)
{
var multi = new MulticastDelegateExample();
multi.Start += PreheatOven;
multi.Start += MixBatter;
multi.Start += PutCakeIn;
multi.Stop += TakeCakeOut;
multi.Stop += TurnOffOven;
Console.WriteLine("Bake a cake:");
multi.Start();
Console.WriteLine("Wait a while...");
multi.Stop();
Console.WriteLine("Now eat!");
}
static void PreheatOven()
{
Console.WriteLine(" Preheating oven");
}
static void MixBatter()
{
Console.WriteLine(" Mixing batter");
}
static void PutCakeIn()
{
Console.WriteLine(" Putting cake into oven");
}
static void TakeCakeOut()
{
Console.WriteLine(" Taking cake out of oven");
}
static void TurnOffOven()
{
Console.WriteLine(" Turning off oven");
}
}
}
I’ve highlighted the two delegate calls that result in this output:
Bake a cake:
Preheating oven
Mixing batter
Putting cake into oven
Wait a while...
Taking cake out of oven
Turning off oven
Now eat!
You can see that the += assignment allows us to chain another handler onto the delegate, and they are called in the order that they are added. We can also remove a handler using -= like so:
static void Main(string[] args)
{
var multi = new MulticastDelegateExample();
multi.Start += PreheatOven;
multi.Start += MixBatter;
multi.Start += PutCakeIn;
multi.Stop += TakeCakeOut;
multi.Stop += TurnOffOven;
multi.Start -= MixBatter;
Console.WriteLine("Bake a cake:");
multi.Start();
Console.WriteLine("Wait a while...");
multi.Stop();
Console.WriteLine("Now eat!");
}
This results in a gross, un-finished cake:
Bake a cake:
Preheating oven
Putting cake into oven
Wait a while...
Taking cake out of oven
Turning off oven
Now eat!
Don’t forget that += and -= are still shortcuts for the long-hand:
multi.Start += new MulticastDelegateExample.Procedure(PreheatOven);
multi.Start -= new MulticastDelegateExample.Procedure(PreheatOven);
What is an event?
Microsoft has good information on events as well. Events are delegates that follow some simple guidelines:
- An event can have multiple subscribers
- Events are typically used to signal user actions (such as button clicks)
- Events are based on the EventHandler delegate and EventArgs base class
Lets look at a simple example to see when an event might be useful. Say we create a class that can download a web page for us. Here we’re going to see that the age of the .NET 3.5 runtime breaks in the modern world since we’re stuck with System.Net.WebClient (since System.Net.HTTP didn’t exist yet). The WebClient class breaks on modern HTTPS so we’re limited to downloading HTTP-only traffic. For the sake of this demo, I’m (temporarily) letting my dev site offer pages over HTTP.
using System;
using System.Net;
using System.Text.RegularExpressions;
namespace ConsoleApplication3
{
class WebPageDownloader : IDisposable
{
private WebClient _wc;
public WebPageDownloader()
{
_wc = new WebClient();
}
public void Dispose()
{
if (_wc != null)
_wc.Dispose();
}
public string Get(string url)
{
return _wc.DownloadString(url);
}
}
class Program
{
static void Main(string[] args)
{
var links = new Regex("href=\"(?<link>[a-z0-9+=:/.-]+)\"",
RegexOptions.Compiled | RegexOptions.IgnoreCase);
using (var web = new WebPageDownloader())
{
var page = web.Get("http://dev.kielthecoder.com/");
var matches = links.Matches(page);
Console.WriteLine("Found {0} links:", matches.Count);
foreach (Match m in matches)
{
Console.WriteLine(m.Groups["link"].Value);
}
Console.WriteLine();
}
}
}
}
This program prints the number of links on my homepage (currently at 24) and then the URL for each.
My homepage downloads pretty fast since it’s small, but say we had a larger page that took several seconds to download. What if we had to wait until that download finished before we could click on anything else in our program? It would be much better to download the page in the background, then signal when it was ready to be interacted with. Lets change DownloadString to an async version instead:
using System;
using System.Net;
using System.Text.RegularExpressions;
using System.Threading;
namespace ConsoleApplication3
{
class WebPageDownloader : IDisposable
{
private WebClient _wc;
public event EventHandler<DownloadStringCompletedEventArgs> StringComplete;
public WebPageDownloader()
{
_wc = new WebClient();
_wc.DownloadStringCompleted += DownloadStringCompleted;
}
public void Dispose()
{
if (_wc != null)
_wc.Dispose();
}
public void Get(string url)
{
_wc.DownloadStringAsync(new Uri(url));
}
private void DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e)
{
if (StringComplete != null)
StringComplete(sender, e);
}
}
class Program
{
static Regex _links;
static void Main(string[] args)
{
_links = new Regex("href=\"(?<link>[a-z0-9+=:/.-]+)\"",
RegexOptions.Compiled | RegexOptions.IgnoreCase);
using (var web = new WebPageDownloader())
{
web.StringComplete += PrintResults;
web.Get("http://dev.kielthecoder.com/");
}
Thread.Sleep(2000); // simulate a slow web site
}
static void PrintResults(object sender, DownloadStringCompletedEventArgs e)
{
if (!e.Cancelled)
{
var matches = _links.Matches(e.Result);
Console.WriteLine("Found {0} links:", matches.Count);
foreach (Match m in matches)
Console.WriteLine(m.Groups["link"].Value);
Console.WriteLine();
}
}
}
}
I had to throw a Thread.Sleep at the end of Main to give the program a chance to print the results before exiting. Notice how we check on line 33 if StringComplete is null? As a publisher of events, we need to make sure that we have actual subscribers (such as line 49), otherwise, we might raise a NullReferenceException by trying to call StringComplete if it hasn’t been assigned to.
I feel like that’s all you really need to know about delegates and events on the C# side. The rest of this post is going to focus on struggling to get SIMPL+ to play nice.
SIMPL+
If you visit https://help.crestron.com/simpl_sharp/ you can still read the old help file for SIMPL#. No idea if this is going to be around forever, so I thought it would be good to capture some of the ideas from it here where I won’t arbitrarily move content.
If you look at Chapter 3 – Events, you can see a full example getting an event handler written in SIMPL+ to work. I feel like I look at this page every time I try to get events and delegates working in my SIMPL programs.
Let’s adapt our webpage link counting example to a SIMPL# library for use in a SIMPL Windows program. Create a new SIMPL# library project. We’ll work on getting the text of my page into SIMPL Windows first:
using System;
using Crestron.SimplSharp;
using Crestron.SimplSharp.Net.Http;
namespace SIMPLSharpLibrary1
{
public class TextEventArgs : EventArgs
{
public string Text;
}
public class WebPageDownloader : IDisposable
{
private HttpClient _http;
public event EventHandler<TextEventArgs> StringComplete;
public WebPageDownloader()
{
_http = new HttpClient();
}
public void Dispose()
{
if (_http != null)
_http.Dispose();
}
public void Get(string url)
{
_http.GetAsync(url, DownloadStringCompleted);
}
private void DownloadStringCompleted(string userObj, HTTP_CALLBACK_ERROR error)
{
if (error == HTTP_CALLBACK_ERROR.COMPLETED)
{
if (StringComplete != null)
StringComplete(this, new TextEventArgs() { Text = userObj });
}
}
}
}
Once we target SIMPL# we’re limited to what Crestron provides in their sandbox (on 3-series at least). WebClient is gone, but Crestron has back-ported HttpClient instead. We also create a TextEventArgs class that can be used to pass the text of the webpage back to SIMPL+. Build this project then copy the resulting SIMPLSharpLibrary1.clz to a new folder where we’ll create this SIMPL+ wrapper:
// COMPILER DIRECTIVES //
#ENABLE_DYNAMIC
#DEFAULT_VOLATILE
#ENABLE_STACK_CHECKING
#ENABLE_TRACE
// LIBRARIES //
#USER_SIMPLSHARP_LIBRARY "SIMPLSharpLibrary1"
// INPUTS //
STRING_INPUT URL[255];
// OUTPUTS //
STRING_OUTPUT Text;
// STRUCTURES //
WebPageDownloader web;
// EVENT HANDLERS //
CHANGE URL
{
web.Get(URL);
}
// CALLBACKS AND DELEGATES //
EVENTHANDLER GetResult (WebPageDownloader sender, TextEventArgs args)
{
Text = args.Text;
}
// MAIN //
FUNCTION Main()
{
RegisterEvent(web, StringComplete, GetResult);
WaitForInitializationComplete();
}
The RegisterEvent function in SIMPL+ is nearly equivalent to this in C#:
web.StringComplete += GetResult;
Save and Compile this module, then you can drop it into a SIMPL Windows program:

Load to a Crestron processor and test. Now I can send http://dev.kielthecoder.com/ into url$ and see the web page show up in text$. Make sure to only pass HTTP URLs for now, anything with HTTPS will likely crash our program.
Event handling in SIMPL+ isn’t so bad; just make sure you don’t try to access any types that are unsupported (like Boolean or Double).
SIMPL+ and Delegates
What happens if our SIMPL# library is trying to perform some work delegated to our SIMPL+ module? First off: why would you torture yourself that way? Secondly: if you’re going through all this effort to do anything in SIMPL#, do as much as you can there and only use SIMPL+ to pass what you need back into SIMPL Windows. Business logic in SIMPL Windows, number crunching in SIMPL#. It would be great if we had a way to bypass SIMPL+.
But for the sake of this post, lets bring back our link matching from before. Update the SIMPL# library like so:
using System;
using System.Text.RegularExpressions;
using Crestron.SimplSharp;
using Crestron.SimplSharp.Net.Http;
namespace SIMPLSharpLibrary1
{
public class TextEventArgs : EventArgs
{
public string Text;
}
public class WebPageDownloader : IDisposable
{
public delegate void PrintDelegate(string text);
private HttpClient _http;
private Regex _links;
public PrintDelegate PrintLink;
public event EventHandler<TextEventArgs> StringComplete;
public WebPageDownloader()
{
_http = new HttpClient();
_links = new Regex("href=\"(?<link>[a-z0-9+=:/.-]+)\"",
RegexOptions.Compiled | RegexOptions.IgnoreCase);
}
public void Dispose()
{
if (_http != null)
_http.Dispose();
}
public void Get(string url)
{
_http.GetAsync(url, DownloadStringCompleted);
}
private void DownloadStringCompleted(string userObj, HTTP_CALLBACK_ERROR error)
{
if (error == HTTP_CALLBACK_ERROR.COMPLETED)
{
if (StringComplete != null)
StringComplete(this, new TextEventArgs() { Text = userObj });
foreach (Match m in _links.Matches(userObj))
{
if (PrintLink != null)
PrintLink(m.Groups["link"].Value);
}
}
}
}
}
A lot of this code is just recycled from before, but we’ve created a new PrintDelegate type and PrintLink that will handle work in SIMPL+ for us. Build this library and copy it over to your SIMPL+ folder. We won’t change anything in SIMPL+ yet, but go ahead and Save and Compile against this new version of the library. Now when we open up the API, we notice some problems:
class WebPageDownloader
{
// class delegates
// class events
EventHandler StringComplete ( WebPageDownloader sender, TextEventArgs e );
// class functions
FUNCTION Dispose ();
FUNCTION Get ( STRING url );
SIGNED_LONG_INTEGER_FUNCTION GetHashCode ();
STRING_FUNCTION ToString ();
// class variables
INTEGER __class_id__;
// class properties
};
Where’s our PrintLink delegate? SIMPL+ is very picky about how it accesses delegates, so we need to expose it as a Property instead. Back in SIMPL#, give our delegate some default accessors to make SIMPL+ happy:
public PrintDelegate PrintLink { get; set; }
Rebuild the library, copy over to SIMPL+, check the API again and magically our delegate appears:
class WebPageDownloader
{
// class delegates
delegate FUNCTION PrintDelegate ( STRING text );
// class events
EventHandler StringComplete ( WebPageDownloader sender, TextEventArgs e );
// class functions
FUNCTION Dispose ();
FUNCTION Get ( STRING url );
SIGNED_LONG_INTEGER_FUNCTION GetHashCode ();
STRING_FUNCTION ToString ();
// class variables
INTEGER __class_id__;
// class properties
DelegateProperty PrintDelegate PrintLink;
};
Now we can register a callback function in our SIMPL+ code. Here’s our entire module again:
// COMPILER DIRECTIVES //
#ENABLE_DYNAMIC
#DEFAULT_VOLATILE
#ENABLE_STACK_CHECKING
#ENABLE_TRACE
// LIBRARIES //
#USER_SIMPLSHARP_LIBRARY "SIMPLSharpLibrary1"
// INPUTS //
STRING_INPUT URL[255];
// OUTPUTS //
STRING_OUTPUT Text;
STRING_OUTPUT Link;
// STRUCTURES //
WebPageDownloader web;
// EVENT HANDLERS //
CHANGE URL
{
web.Get(URL);
}
// CALLBACKS AND DELEGATES //
EVENTHANDLER GetResult (WebPageDownloader sender, TextEventArgs args)
{
Text = args.Text;
}
CALLBACK FUNCTION PassLinkText (STRING text)
{
Link = text;
}
// MAIN //
FUNCTION Main()
{
RegisterEvent(web, StringComplete, GetResult);
RegisterDelegate(web, PrintLink, PassLinkText);
WaitForInitializationComplete();
}
Save and Compile this module and the compiler spits out this handy error:
Error (Line 49) - No overload for 'UserModule_WEB_PAGE_DOWNLOADER_WRAPPER_V1.UserModuleClass_WEB_PAGE_DOWNLOADER_WRAPPER_V1.PASSLINKTEXT(Crestron.SimplSharp.SimplSharpString)'

Go back to SIMPL# and update the delegate to match this signature:
public class WebPageDownloader : IDisposable
{
public delegate void PrintDelegate(SimplSharpString text);
Rebuild the library, copy to SIMPL+, Save and Compile, voila: we finally got the compiler to accept our delegate. Update our SIMPL Windows program now so we can see the link text:

The PassLinkText callback will update link$ for each link address found on the page pointed to by url$. Exciting!
But why didn’t a normal String work? Remember that in C#, almost everything is an object and strings are immutable. SIMPL+ can be pretty rough on strings and treats them way differently. Think about functions like Remove that chop away parts of a string. So our delegate needed to use SimplSharpString as its parameter type instead.
Conclusion
Events are awesome and an easy way to signal that things are ready to be handled in your program. EventArgs provides a convenient way to build an object that can be used in SIMPL+ to deliver information to SIMPL.
I wouldn’t bother with delegates and SIMPL+ since that means you’re asking SIMPL+ to do work that SIMPL# is likely better suited to handling. And you’re going to have to contort your programming to satisfy the requirements of the SIMPL+ compiler. All of that translates to wasted time and effort.
Great explanation! Thank you for this.
LikeLike
Thanks! I received a comment on LinkedIn about this post that made me realize I was too harsh on SIMPL+, so I do plan to explore cases where a delegate callback makes more sense than an event. Something for a future post.
LikeLike