Cutting Edge Silverlight4 COM+ Features
Hi Folks,
The new Silverlight 4 COM+ Automation feature can be used to perform a myriad of previously unavailable tasks in Silverlight.
In this blog post I’ll review some of these tasks, what COM+ Automation is, some related issues and helpful introduce developer tools.
The code we’ve created in this blog post is available @ http://Justinangel.net/Storage/SL4Com/SL4Com.zip
And a Live demo is available @ http://Justinangel.net/Storage/SL4Com/testpage.html
What is Silverlight 4 COM+ Automation support?
Silverlight 4 Beta 1 offers the ability to initialize and use COM+ classes from Silverlight.
It should be noted that these features are only available in elevated privileges Out-Of-Browser mode and are not available in-browser.
In Silverlight 4 Beta1 Com+ Automation only works on Windows machines (but more on that later).
The majority of work around Silverlight 4 COM+ support is centered around the ComAutomationFactory and ComAutomationEvent classes.
Nooo! COM+ killed my inner child back in the 90s!
Get over it.
Silverlight 4 COM+ Automation is not about authoring, deploying or versioning COM+ components, that is a worst practice for this feature.
The best practice for Silverlight 4 COM+ is to only use common operating system COM+ classes, and not ship your own.
Given that best practice, you are just the consumer of some time tested Windows APIs.
Using COM+ Windows APIs is no different then consuming the .Net framework, 3rd party frameworks or p/Invoke features.
Stop yapping and show me how to setup a Silverlight 4 OOB project!
We’ll start by creating a new Silverlight 4 OOB (Out Of Browser) project with Elevated Privileges in Visual Studio 2010.
For our test project we don’t need a Server-Side project, and we need to make sure we’re using Silverlight 4.
Right-Click on the SL4Com project and choose “Properties”.
Check the “Enable Running Application out of browser”.
Click “Out of Browser Settings” and set “Require Elevated Trust when running outside the browser”.
Next add a Button to MainPage that’ll install our Out-Of-Browser application.
<Button x:Name="btnInstall" Click="btnInstall_Click" Content="Install Out-Of-Browser App" />
private void btnInstall_Click(object sender, RoutedEventArgs e)
{
if (Application.Current.InstallState == InstallState.NotInstalled)
Application.Current.Install();
}
Note: This article is about COM+, not best practices for wiring up events.
If this was a real application, we should have used Prism Commands or Blend Behaviours to hook up these events.
We’ll run our app and install it.
Next, we’ll need to tell Visual Studio 2010 to run the Out-Of-Browser app when debugging.
Go to Project properties –> Debug –> Installed out of browser application –> SL4Com.
One last step we have to do is add a reference to Microsoft.CSharp.dll so we could use the new Silverlight 4 dynamic keyword.
(on my machine the DLL is at c:\Program Files (x86)\Microsoft SDKs\Silverlight\v4.0\Libraries\Client\Microsoft.CSharp.dll)
Feature #1: Write files anywhere on the local machine
Using the FileSystemObject we have virtually unlimited access to the user’s file storage.
<Button x:Name="btnWriteFile" Content="Write file to C:\test.txt" Click="btnWriteFile_Click" />
private void btnWriteFile_Click(object sender, RoutedEventArgs e)
{
using(dynamic fsoCom = ComAutomationFactory.CreateObject("Scripting.FileSystemObject"))
{
dynamic file = fsoCom.CreateTextFile(@"c:\test.txt", true);
file.WriteLine("Bloody Hell!");
file.WriteLine("Silverlight is writing to C:\\");
file.Close();
}
}
When we run our app and click the button we can see that a new file has been created on C:\test.txt.
Feature #2: Reading any file from the user’s machine
Using FileSystemObject.ReadAll method we can read any file off the user’s machine.
<Button x:Name="btnReadFile" Content="Read file C:\test.txt" Click="btnReadFile_Click" />
private void btnReadFile_Click(object sender, RoutedEventArgs e)
{
using (dynamic fsoCom = ComAutomationFactory.CreateObject("Scripting.FileSystemObject"))
{
dynamic file = fsoCom.OpenTextFile(@"c:\test.txt", 1, true);
MessageBox.Show(file.ReadAll());
file.Close();
}
}
Running the app and clicking the button shows us the file contents:
What else can FileSystemObject do for us?
The FSO object let’s us essentially own the user machine.
It provides a way to read, write, delete or modify any file or directory on the user’s file system.
Here’s a list of Methods you can explore independently:
Feature #3: Execute/Run any command or file
Using the WShell Run Method we can execute any command.
<Button x:Name="btnExecuteCmdPing" Content="Cmd Ping 127.0.0.1" Click="btnExecuteCmdPing_Click" />
private void btnExecuteCmdPing_Click(object sender, RoutedEventArgs e)
{
using(dynamic shell = ComAutomationFactory.CreateObject("WScript.Shell"))
{
shell.Run(@"cmd /k ping 127.0.0.1");
}
}
Clicking the button opens up the CMD and runs the specified command:
Feature #4: Emulate user input
Using the WShell SendKeys method we can send input to the currently focused application.
This feature is extremely useful for integration testing.
<Button x:Name="btnExecuteNotepadAndSendKeys" Content="Run Notepad and write something" Click="btnExecuteNotepadAndSendKeys_Click" />
private void btnExecuteNotepadAndSendKeys_Click(object sender, RoutedEventArgs e)
{
using (dynamic shell = ComAutomationFactory.CreateObject("WScript.Shell"))
{
shell.Run(@"c:\windows\notepad.exe");
shell.SendKeys("Justin Angel{Enter}Rocks!");
}
}
Looking at the SendKeys syntax it’s easy to see there are special keys (like Enter, Tab, escape, ctrl, etc) that can be specified using a special notation.
Feature #5: Pin files to the Windows 7 Taskbar
Using The Shell.Application class we can iterate over all applications, check their Verbs and execute those verbs accordingly.
Here are the Verbs for the Calculator application:
However, this method allows us to Pin Calculator to the Win7 taskbar even when the Calculator is closed.
It does that by checking the Windows verbs on the LNK (Link) file to Calculator.
<Button x:Name="btnPinCalculator" Content="Pin Calcuator to the Win7 Taskbar" Click="btnPinCalculator_Click" />
private void btnPinCalculator_Click(object sender, RoutedEventArgs e)
{
using (dynamic ShellApplication = ComAutomationFactory.CreateObject("Shell.Application"))
{
dynamic commonPrograms = ShellApplication.NameSpace(23);
string allUsersPath = commonPrograms.Self.Path;
dynamic directory = ShellApplication.NameSpace(allUsersPath + @"\Accessories");
dynamic link = directory.ParseName("Calculator.lnk");
dynamic verbs = link.Verbs();
for (int i = 0; i < verbs.Count(); i++)
{
dynamic verb = verbs.Item(i);
if (verb.Name.Replace(@"&", string.Empty).ToLower() == "pin to taskbar")
{
verb.DoIt();
}
}
}
}
Essentially, what this method does is:
1. Gets the path for all the users start menu links.
2. Gets the specific link for Calculator.
3. Checks what Verbs are available for that link.
4. Execute the “Pin to Taskbar” verb.
And when running this application we can see Calculator is now pinned to the user’s taskbar:
Feature #6: Read any Registry values
Using the Shell.Application RegRed method you can read values from the machine’s registry.
At the "HKLM\Software\Microsoft\Windows\CurrentVersion\CommonFilesDir" registry key we can see the location of the machine’s Common Files:
We can easily access that value using RegRead.
<Button x:Name="btnReadRegistry" Content="Read HKLM registry" Click="btnReadRegistry_Click" />
private void btnReadRegistry_Click(object sender, RoutedEventArgs e)
{
using (dynamic WShell = ComAutomationFactory.CreateObject("WScript.Shell"))
{
string reg = WShell.RegRead(@"HKLM\Software\Microsoft\Windows\CurrentVersion\CommonFilesDir");
MessageBox.Show("The Program files on this machine are at: " + reg);
}
}
Clicking the button shows the value of the registry key.
Feature #7: Add OOB App to Windows’ Startup
Using a combination of the LNK technique we’ve seen earlier and Shell.Application RegWrite method we can add our OOB app to the Windows’ startup applications.
Every program listed under “HKLM\Software\Microsoft\Windows\CurrentVersion\Run\” will be executed when the machine first starts up.
So if we can get our OOB link and add it to that Registry location, we can add our OOB application to Windows’ Startup.
<Button x:Name="btnAddOOBAppToStartup" Content="Add out-of-browser application to Startup" Click="btnAddOOBAppToStartup_Click" />
private void btnAddOOBAppToStartup_Click(object sender, RoutedEventArgs e)
{
using (dynamic ShellApplication = ComAutomationFactory.CreateObject("Shell.Application"))
{
dynamic commonPrograms = ShellApplication.NameSpace(11);
string allUsersPath = commonPrograms.Self.Path;
dynamic directory = ShellApplication.NameSpace(allUsersPath + @"\Programs");
dynamic link = directory.ParseName(Deployment.Current.OutOfBrowserSettings.ShortName + ".lnk");
string OOBLink = link.Path;
using (dynamic WShell = ComAutomationFactory.CreateObject("WScript.Shell"))
{
WShell.RegWrite(@"HKLM\Software\Microsoft\Windows\CurrentVersion\Run\"
+ Deployment.Current.OutOfBrowserSettings.ShortName,
OOBLink);
MessageBox.Show("Please restart your machine and this application will load on startup.");
}
}
}
This code snippet isn’t all that complicated.
First, we get the LNK to the current out-of-browser application by checking the start menu links directory.
Next, we add that LNK file Uri to the registry node we mentioned earlier on.
And after running clicking the button we can see the registry value was added:
Task #8: Pinning the OOB Application to Windows’ 7 Taskbar
We’ve previously seen how to get the LNK for the OOB application and we’ve seen how to pin those LNKs to the Taskbar.
It’s fairly easy to combine those two techniques to pin the current OOB app to the taskbar.
<Button x:Name="btnPinOOBApp" Content="Pin Out-of-Browser application to Taskbar" Click="btnPinOOBApp_Click" />
private void btnPinOOBApp_Click(object sender, RoutedEventArgs e)
{
using (dynamic ShellApplication = ComAutomationFactory.CreateObject("Shell.Application"))
{
dynamic commonPrograms = ShellApplication.NameSpace(11);
string allUsersPath = commonPrograms.Self.Path;
dynamic directory = ShellApplication.NameSpace(allUsersPath + @"\Programs");
dynamic link = directory.ParseName(Deployment.Current.OutOfBrowserSettings.ShortName + ".lnk");
dynamic verbs = link.Verbs();
for (int i = 0; i < verbs.Count(); i++)
{
dynamic verb = verbs.Item(i);
if (verb.Name.Replace(@"&", string.Empty).ToLower() == "pin to taskbar")
{
verb.DoIt();
MessageBox.Show("Close this application and start it up again from Win7 Taskbar");
}
}
}
}
And we can see that even when our OOB application is closed, it’s still showing up on the Win7 taskbar.
Feature #9: Text to Speech
Using Windows’ built-in SAPI engine we can easily do Text to Speech and even Speech to Text.
We’ll demo using the SAPI.SpVoice class for Text2Speech.
Feel free to explore the SAPI namespace since it’s full of other useful speech related features.
<Button x:Name="btnText2Speech" Content="Use Text to Speech" Click="btnText2Speech_Click" />
private void btnText2Speech_Click(object sender, RoutedEventArgs e)
{
using (dynamic ISpeechVoice = ComAutomationFactory.CreateObject("SAPI.SpVoice"))
{
ISpeechVoice.Volume = 100;
ISpeechVoice.Speak("<rate speed=\"0\"><pitch middle=\"0\">Hello World! My HoverCraft is full of eels.");
}
}
Clicking the button does indeed cause Windows to start speaking.
For a relatively acceptable use of this feature, have a look at my Molecular Biology DNA Visualizer.
It is also possible to do limited speech recognition using SAPI using the SAPI.SpInprocRecognizer class.
Feature #10: Execute SQL with Local Databases
Using ODBC features available in Windows, we can easily connect to local databases and execute SQL.
Here’s a sample of executing a simple SQL Insert with the local Northwind database:
<Button x:Name="btnExecuteSQL" Content="Execute SQL Insert statement" Click="btnExecuteSQL_Click" />
using (dynamic IDbConnection = ComAutomationFactory.CreateObject("ADODB.Connection"))
using (dynamic IDbCommand = ComAutomationFactory.CreateObject("ADODB.Command"))
{
IDbConnection.ConnectionString = "driver={SQL Server};" +
"server=.\\;uid=sa;pwd=password;database=Northwind";
IDbConnection.Open();
IDbCommand.ActiveConnection = IDbConnection;
IDbCommand.CommandText =
@"INSERT INTO [Northwind].[dbo].[Region]
([RegionID], [RegionDescription]) VALUES (10, 'JustinLand')";
IDbCommand.Execute();
}
Note: this code snippet does not show best practices for using ODBC for Silverlight. For one thing, Commands should use parameters and not strings.
If you were to execute this code sample you’d probably get the following error:
Since you don’t have the Northwind Database installed or the username & password are wrong you’d see some exception or another.
Allow me to assure you “It works on my machine”!
It is important to discuss best practices at this point.
It is not my intent to advocate executing local SQL statements in conjunction with a local database.
However, It is possible to convert any 3rd party OR/M or Persistence layer to run on ODBC.
Which would make for a very comfortable EF/Linq2SQl/NH like syntax. In that case, it would be much more acceptable to use ODBC.
Another issue with this method is the end-user might not have any local database available.
And you’ll end up resorting using Access or the likes.
Personally I believe that for local Silverlight storage we should use local OODBs, and not use ODBC.
Feature #11: Automate Scanners and Cameras
Using the WIA (Windows Image Acquisition) infrastructure we can automate external Scanners, Cameras and Video cameras.
Many very unique samples (converting file formats, getting scanners to scan, taking pictures, etc) are available at the WIA Samples page.
One possible task is to iterate over all Cameras and Scanners currently connected to the local machine and their commands:
<Button x:Name="btnIterateWIA" Content="Iterate over external Cameras and Scanners" Click="btnIterateWIA_Click" />
private void btnIterateWIA_Click(object sender, RoutedEventArgs e)
{
StringBuilder sb = new StringBuilder();
sb.AppendLine("List of available external devices and commands:");
using (dynamic DeviceManager = ComAutomationFactory.CreateObject("WIA.DeviceManager"))
{
var deviceInfos = DeviceManager.DeviceInfos;
for (int i = 1; i <= deviceInfos.Count; i++)
{
var IDevice = deviceInfos.Item(i).Connect();
var DeviceID = IDevice.DeviceID;
var DeviceName = IDevice.Properties("Name").Value;
var Commands = IDevice.Commands;
for (int j = 1; j <= Commands.Count; j++)
{
var IDeviceCommand = Commands.Item(j);
var CommandName = IDeviceCommand.Name;
var CommandDescription = IDeviceCommand.Description;
sb.AppendLine(" " + DeviceName + " (" + DeviceID + "), " + CommandName + ": " + CommandDescription);
// Execute with: IDevice.ExecuteCommand(IDeviceCommand.CommandID);
}
}
}
MessageBox.Show(sb.ToString());
}
The above code is actually pretty simple.
1. We initialize a DeviceManager.
2. We ask for all the DeviceInfos.
3. We then iterate over all devices, getting their commands.
4. We display the command name and device name.
On my dev machine with an External Scanner and External Camera, this is what we see:
Another thing we can do is select Images from external Image devices.
<Button x:Name="btnGetExternalImage" Content="Acquire External Image from Scanner/Camera" Click="btnGetExternalImage_Click" />
private void btnGetExternalImage_Click(object sender, RoutedEventArgs e)
{
using (dynamic CommonDialog = ComAutomationFactory.CreateObject("WIA.CommonDialog"))
{
dynamic imageFile = CommonDialog.ShowAcquireImage();
if (imageFile != null)
{
imageFile.SaveFile(@"c:\myImage.jpg");
MessageBox.Show("Saved c:\\myImage.jpg");
}
}
}
When clicking the button on my local machine we’ll see the following dialogs:
And we can see c:\myImage.jpg is indeed that same photo:
Feature #13: Use the Windows 7 Location API
As part of Windows 7 the Location API was added to provide developers with the current machine GPS location.
If the proper hardware is connect, Using the Location API we can get the current Latitude and Longitude of the current Machine.
<Button x:Name="btnWin7LocationAPI" Content="Activate Windows 7 Location API" Click="btnWin7LocationAPI_Click" />
private void btnWin7LocationAPI_Click(object sender, RoutedEventArgs e)
{
using (dynamic LatLongReportFactory = ComAutomationFactory.CreateObject("LocationDisp.LatLongReportFactory"))
{
uint curStatus = LatLongReportFactory.Status;
if (curStatus == (uint)4)
{
using (dynamic LatLongReport = LatLongReportFactory.LatLongReport)
{
MessageBox.Show("Latitude: " + LatLongReportFactory.Latitude + "\r\n Longitude: " + LatLongReportFactory.Longitude);
}
}
LatLongReportFactory.ListenForReports(5000);
ComAutomationEvent NewLatLongReportEvent = ComAutomationFactory.GetEvent(LatLongReportFactory, "NewLatLongReport");
NewLatLongReportEvent.EventRaised += (s, args) =>
{
using (dynamic LatLongReport = LatLongReportFactory.LatLongReport)
{
MessageBox.Show("Latitude: " + LatLongReportFactory.Latitude + "\r\n Longitude: " + LatLongReportFactory.Longitude);
}
LatLongReportFactory.StopListeningForReports();
};
}
}
The above code will display the current Latitude and longitude and then listen in for new reports every 5 seconds.
Feature #14: Use Classes from the Full .Net framework
While it is not recommended, you can consume some classes from the full .Net framework.
The reason it is not recommended, is because all of your users would need to have the full .Net framework installed.
Here’s an example of initializing the ReaderWriterLock class used for multithreading multiple readers or a single writer.
<Button x:Name="btnUseFullDotNet" Content="Use a class from the full .Net frameowrk" Click="btnUseFullDotNet_Click"/>
private static int foo = 0;
private void btnUseFullDotNet_Click(object sender, RoutedEventArgs e)
{
using (dynamic ReaderWriterLock = ComAutomationFactory.CreateObject("System.Threading.ReaderWriterLock"))
{
ReaderWriterLock.AcquireWriterLock(1);
MessageBox.Show("System.Threading.ReaderWriterLock.AcquireWriterLock() - acquired lock");
foo++;
ReaderWriterLock.ReleaseWriterLock();
MessageBox.Show("System.Threading.ReaderWriterLock.ReleaseWriterLock() - released lock");
ReaderWriterLock.AcquireReaderLock(1);
MessageBox.Show("While in reader lock - Foo value is " + foo);
ReaderWriterLock.ReleaseReaderLock();
}
}
Executing this code we’ll see the expected messages.
The interesting part here is that we’re using classes from the Full desktop .Net framework which are not available in Silverlight.
Feature #15: Use WMI to build FileSystemWatcher
Using the WbemScripting.SWbemLocator class we can execute WMI queries in Silverlight.
WMI allows querying for many operating system related information, like: Performance (CPU & Memory), Security, Networking, Storage, Kernel, Active Directory, etc.
In this sample we’ll rebuild the .Net FileSystemWatcher using WMI Events
<Button x:Name="btnActivateFileSystemWatcher" Content="Active FileSystemWatcher on C:\" Click="btnActivateFileSystemWatcher_Click" />
private void btnActivateFileSystemWatcher_Click(object sender, RoutedEventArgs e)
{
new Thread(() =>
{
using (dynamic SWbemLocator = ComAutomationFactory.CreateObject("WbemScripting.SWbemLocator"))
{
SWbemLocator.Security_.ImpersonationLevel = 3;
SWbemLocator.Security_.AuthenticationLevel = 4;
dynamic IService = SWbemLocator.ConnectServer(".", @"root\cimv2");
string fileSystemWatcherQuery =
@"SELECT * FROM __InstanceOperationEvent WITHIN 3 WHERE Targetinstance ISA 'CIM_DirectoryContainsFile' and TargetInstance.GroupComponent= 'Win32_Directory.Name=""c:\\\\""'";
dynamic monitor = IService.ExecNotificationQuery(fileSystemWatcherQuery);
Dispatcher.BeginInvoke(() => MessageBox.Show(@"Now listening to file changes on c:\"));
while (true)
{
dynamic EventObject = monitor.NextEvent();
string eventType = EventObject.Path_.Class;
string path = EventObject.TargetInstance.PartComponent;
Dispatcher.BeginInvoke(() => MessageBox.Show(eventType + ": " + path));
}
}
}).Start();
}
This code might look complex, but it really isn’t.
1. We’ve initialized a
WbemScripting.SWbemLocator and connected to the local Machine with proper security.
2. We’ve executed a WMI Query for all storage changes on C:\.
3. When that event fired we’ve shown a messagebox.
Since Wbem doesn’t directly support events, we have to constantly query it. So we’re executing the whole thing in a background thread.
Executing this code and adding c:\foo.txt file will cause the following messagebox to occur:
Feature #16: Iterate over valid ProgIDs
This one is just a shout-out to the COM infrastructure.
If you’ve been paying attention you’ve noticed some magic strings when calling ComAutomationFactory.CreateObject.
Those magic strings are the ProgIDs of registered COM+ components.
We can get a list of all valid ProgIDs using WMI.
<Button x:Name="btnIterateOverProgIDs" Content="Iterate over valid COM+ ProgIDs" Click="btnIterateOverProgIDs_Click" />
private void btnIterateOverProgIDs_Click(object sender, RoutedEventArgs e)
{
StringBuilder sb = new StringBuilder();
sb.AppendLine("The first 25 valid ProgIDs: ");
using (dynamic SWbemLocator = ComAutomationFactory.CreateObject("WbemScripting.SWbemLocator"))
{
SWbemLocator.Security_.ImpersonationLevel = 3;
SWbemLocator.Security_.AuthenticationLevel = 4;
dynamic IService = SWbemLocator.ConnectServer(".", @"root\cimv2");
dynamic QueryResults = IService.ExecQuery(
@"SELECT Caption, Description, InprocServer32, ProgID FROM Win32_ClassicCOMClassSetting where progid is not null");
for (int i = 1; i < 25 /*QueryResults.Count*/; i++)
{
string progID = QueryResults.ItemIndex(i).ProgID;
sb.AppendLine(" " + progID);
}
}
MessageBox.Show(sb.ToString());
}
After clicking the button we can see this list of the first 25 valid Classes for Com+ Automation:
Feature #17: Automate Microsoft Office
Office exposes a set of COM+ automation classes allowing to automate PowerPoint, word, excel, outlook, etc.
There’s been quite a lot written on this one so It’ll suffice to provide links to what’s already been said by folks more knowledgeable than myself:
Silverlight 4's New COM Automation Support by Jeff Prosise (Outlook)
Silverlight 4 Rough Notes: Trusted Applications by Mike Taulty (Excel)
Silverlight 4: New features overview (Part 3) – Elevated Out-of-browser applications by Alex Golesh (Word)
Silverlight TV Episode 1: The Joys of Trusted Out-of-Browser Applications by Keith Smith & John Papa (PowerPoint)
Tools for Silverlight 4 COM+ Development
When developing Silverlight COM+ applications these 4 tools will prove to be invaluable source of information.
Tool #1: WMI Explorer
WMI Explorer is a general purpose tool used to visualize WMI queries. (Which we’ve seen earlier)
Using WMI Explorer and the following query we can see a list of all available COM+ Components on the relevant machine:
SELECT Caption, Description, InprocServer32, ProgID FROM Win32_ClassicCOMClassSetting where progid is not null
This list is extremely helpful when examining which ProgIDs are available for COM Automation.
Tool #2: DLL Export Viewer
When working with dynamic types there’s no intellisense available.
But fear not, using DLL Export Viewer we essentially get the same information reflector would have provided us on an assembly.
First, find the COM+ DLL file address from WMI Explorer and load it into DLL Export Viewer:
Make sure “Scan COM Type Libraries” is checked.
And now we can see an exported list of members from the ODBC DLL:
Tool #3: Resharper
Though development with dynamic types doesn’t offer intellisense, Resharper 5 makes this a little better by offering intellisense on members that have been previously used.
Notice the intellisense for the “Run” and “SendKeys” methods.
Tool #4: MSDN and Search Engines
Most core Windows’ COM+ components have adequate documentation up on MSDN when searching for the type name on google:
In case the documentation on MSDN is missing or just irrelevant (and that’s quite common) search “CreateObject <ProgID>” and the VBScript results are easily portable to C#.
But What about Macs?
COM+ Automation isn’t supported for Macs in Silverlight 4 Beta 1.
For a good reason, Mac doesn’t have COM+.
However, Microsoft have stated they are looking into getting COM+ support working in Silverlight 4 RTM on a Mac.
Macs have similar programmatic access to COM known as AppleScript.
It’s not that hard to read once you remove the added spaces and add some indents.
Once you get past the syntax, it’s easy to see those are the same classes and members on a Mac as are available for Windows.
The point here is that if Microsoft wants to enable Com Automation-like features on a Mac there are 2 options:
1) Enable executing AppleScripts.
This option will let us have the same amount of control on a mac machine as we do on a windows machine.
2) Add an overload to ComAutomationFactory.CreateObject() that calls the “Tell Application” command under the scenes and gets a AppleScript object.
This option would work extremely well for Office automation. For any other operating system feature, you’ll have to code OS access twice.
If Microsoft chooses to not go ahead with Mac support in Silverlight 4 RTM, well, it’s not because they couldn’t.
What about Linux?
Moonlight has had the superior security model since it first shipped.
Mono supports hosting Moonlight in a GtkWidget that has full-trust capabilities.
So while Silverlight has an elevated privileges model, Mono has a full trust model.
Read more about this model over at Miguel de Icaza’s blog @ Standalone Silverlight Applications.
Can I author my own COM+ Components?
It is possible yet unadvisable.
Authoring COM+ Components has a huge cost of learning COM+, and deploying and versioning COM+ Components.
COM+ was such a successful technology most of us left as soon as we could.
Can I author .Net components and then deploy them as COM+ components?
Jeremiah Morril has a blog post on how to go about doing that.
It is possible yet tremendously cumbersome.
Consider that you’ve just added a requirement for the full desktop CLR when running in Silverlight.
Some of your users might not have .Net (or the right .Net version) installed.
Additionally, you’ll still back into COM+ DLL versioning hell.
If you’re going to take that level of dependency it might be best if you moved over to WPF.
Or, if you insist on taking a .Net dependency you shouldn’t use COM+ for communication.
It’d be best to use WCF for that.
Michael Wolf has a good blog post demoing how to create that connection.
Fin
In this blog post we’ve reviewed many new features enabled by Silverlight 4 COM+ support.
We’ve also covered how to best use this new feature including tools, best practices and the possible future of this feature.
Leave A comment
I’d love to hear what you think of Silverlight 4 COM+ automation?
Better yet, if there’s some novel use of Silverlight 4 COM+ I haven’t shown here, feel free to link to it.
Sincerely,
-- Justin Angel
Mix10 And Molecular Biology DNA Visualizer
Hi Folks,
If you haven’t already done so, I strongly encourage you to sign up for Mix 2010.
Windows Mobile and Silverlight, you say?
I really can’t discuss what’s on the agenda, but I can say it’s cool.
You will define your life as “Before Mix10” and “After Mix10”.
Especially if you join us at the bar afterhours :)
It looks like there’s some interesting Windows Mobile and Silverlight news at Mix10.
I wonder what that could possibly be.
Registering for Mix 2010
If you register before January 15th 2010, there’s a fairly substantial price reduction (600$) and a free night at the hotel.
I’d jump on it if I were you.
http://live.visitmix.com/Registration
Vote for my Mix10 Sessions!
Is Justin talking at Mix 2010? Well, that’s really up to you!
I’ve submitted 2 sessions for Mix 2010.
1) 6 Reasons why Silverlight & WPF are better than ASP.Net & Winforms - Where I talk about the guiding principles behind Silverlight & WPF design.
2) Silverlight Community Q&A w/ Justin Angel – This is an open forum to ask a Silverlight Community Expert and a former Microsoft Silverlight Program Manager any Silverlight question.
This will be the 8th time I’ve done that session and it’s always wildly successful.
So if you’d like to see me talk go to the mix site and vote for my sessions:
http://visitmix.com/opencallvote/?query=Justin%20Angel
Also checkout some other Silverlight sessions up for voting.
There’s some pretty amazing content offered by the world’s best Silverlight Community experts.
If you don’t vote for me I’ll club a baby seal. And it’ll be because of you.
Or if you’re into clubbing baby seals, vote for and I’ll club a baby seal. It’s really up to you.
Mix10K Smart Coding Challenge
As part of Mix10 there’s an online contest for the best Silverlight application in 10KB of C# and XAML.
I’ve submitted my entry: Molecular Biology DNA Visualizer
I was really going for a non-trivial use of the technology. Something that isn’t a game or a neat graphic trick.
But something that would showcase Silverlight as a force that could revolutionize LOB applications. In this case, bioinformatics apps.
If you liked my Mix10K app, feel free to vote for it.
There are also some other amazing 10k entries in Mix10k so you might want to spend 30 minutes going over the gallery.
I’d like to thank my inspiration while writing my Mix10K app:
[SimpleVideo:source=http://justinangel.net/Storage/They Might Be Giants - Science is Real.mp4|width=480 |height=390]
Sincerely,
-- Justin Angel
AutoMagically Implementing INotifyPropertyChanged
There have been lots of discussions in the Silverlight Developer Community recently about how to best implement the INotifyPropertyChanged interface.
In this blog post I’ll submit before the reader, what I perceive to be the simplest and the superior solution.
As you all know I’m a huge supporter of Silverlight open source projects, and in this blog post we'll use Mono.Cecil and PostSharp.
Download the projects from this blog post @ http://Justinangel.net/Storage/PostBuildMSILWeaving.zip
But first, INotifyWhatchaMaCallit?
Right, let’s start explaining this issue from scratch. For that, we need Cows!
Let’s setup a simple Silverlight form used for inputting the names of Cows.
Yes, Cows.
So first, we’ll start out by creating a simple POCO (Plain Old CLR Object) class to hold our Cow data.
public class Cow
{
public string FirstName { get; set; }
public string LastName { get; set; }
public string MiddleName { get; set; }
}
Next, We’ll create super simple form to represent our new Cow class.
Normally, in real-world applications it would be best to use the DataForm control to create this form.
But for demo’s sake we’ll keep it simple with StackPanels and TextBoxs.
<StackPanel HorizontalAlignment="Left">
<StackPanel Orientation="Horizontal">
<TextBlock Text="First Name" Width="150" />
<TextBox Text="{Binding FirstName}" Width="300" />
</StackPanel>
<StackPanel Orientation="Horizontal">
<TextBlock Text="Middle Name" Width="150" />
<TextBox Text="{Binding MiddleName}" Width="300" />
</StackPanel>
<StackPanel Orientation="Horizontal">
<TextBlock Text="Last Name" Width="150" />
<TextBox Text="{Binding LastName}" Width="300" />
</StackPanel>
<Button x:Name="changeName" Content="Change Person Properties" />
</StackPanel>
Note the bolded and underlined {Binding} expressions that specify the connection between our POCO property and UI Elements.
After we’ve setup our bindings we’ll need to initialize the DataContext with some initial data. Like so:
this.DataContext = new Cow() { FirstName = "Bassy", MiddleName = "Won't Change", LastName = "LeCow" };
It’s an odd name for a Cow “Bassy ‘Won’t Change’ LeCow”, but it’s good enough for us.
Let’s fire up the form:
But here’s the core issue that starts off this whole INotifyPropertyChanghed discussion. How do we let the form know when the data changes?
Let’s see this problem in action.
We’ll implement the Button.Click event and change the First, Middle and last name of Bassy LeCow.
private void changeName_Click(object sender, RoutedEventArgs e)
{
Cow p = (Cow)this.DataContext;
p.FirstName = "Changed by manual implementation";
p.LastName = "Changed by Post-IL Weaving";
p.MiddleName = "Changed";
}
But what happens when we click our button?
Nothing. Updating the underlying datasource hasn’t done a single thing to update our form.
Silverlight as a UI Framework has no way of knowing when our POCO property has been updated.
What’s the solution?
There are two solutions that could easily fit into this scenario:
1. Inheriting from DependencyObject and replacing all of our POCO properties with DependencyProperties.
Read more about that option at this SilverlightShow article.
2. Implementing the INotifyPropertyChanged interface and invoking PropertyChanged event in our property setters.
Up until recently, I was squarely in the DependencyObject camp.
But after a tempestuous round of discussions on the WPF Disciples mailing list and this aptly written blog post by Kent Boogaart I’ve moved to the INotifyPropertyChanged camp.
The core reason I decided on INotifyPropertyChanged was that you can’t use DependencyObjects in non-UI Disptacher threads. Which is a deal breaker.
Enough with your jibber jabber, show me the code!
So here’s the basic implementation of INotifyPropertyChanged for the FirstName property:
public class Cow : INotifyPropertyChanged
{
private string _firstName;
public string FirstName
{
get { return _firstName; }
set
{
_firstName = value;
RaisePropertyChanged("FirstName");
}
}
public string LastName { get; set; }
public string MiddleName { get; set; }
#region Implement INotifyPropertyChanged
public event PropertyChangedEventHandler PropertyChanged;
public void RaisePropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
#endregion
}
We won’t go over the specifics, but it’s a fairly easy interface to implement, just fire the PropertyChanged event.
When comparing LastName implementation to FirstName implementation we can clearly see that it was much simpler to declare the LastName property.
It’s more readable, more maintainable, and has less chance of duplicating code throughout our system.
Solving the problem caused by the solution
There are a lot of options that were recently discussed for solving the syntax issue caused by declaring INPC (INotifyPropertyChanged) properties.
1) Ray Huston and Jonas Follesoe talk about Runtime substitution of INPC properties through Castle.DynamicProxy.
public class MyViewModel : IAutoNotifyPropertyChanged
{
public virtual string Name { get; set; }
public virtual int Age { get; set; }
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
However, this method requires you stop using our beloved “New” keyword and start using runtime proxies. I’m not a fan of either of those.
A solid solution should have zero-impact on the way we write code today.
2) Michael Sync, Einar Ingebrigsten and Oren Eini talk about improving the syntax needed to declare an INPC property.
public class Employee : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private string _firstName;
public string FirstName
{
get { return this._firstName; }
set
{
this._firstName = value;
this.PropertyChanged.Notify(() => this.FirstName);
}
}
}
There are various sugar-coated syntaxes that have been suggested for INPC. but all of them require us to change the way we code.
3) Brad Abrams demos Silverlight WCF RIA Services generating the INotifyPropertyChanged and INotifyPropertyChanging interfaces for you.
But that only works if these types are declared on the server and are sent down to Silverlight. Additionally, if the property name changes it’s all still string based.
[DataMember()]
[Key()]
[ReadOnly(true)]
public int EmployeeID
{
get
{
return this._employeeID;
}
set
{
if ((this._employeeID != value))
{
ValidationContext context = new ValidationContext(this, null, null);
context.MemberName = "EmployeeID";
Validator.ValidateProperty(value, context);
this._employeeID = value;
this.OnPropertyChanged("EmployeeID");
}
}
}
Solving the problem caused by the Solution to the Solution of the Problem
Confused? Tired? About to start crying? Be a man for gods sakes!
Each of the existing solutions we’ve seen up until now has it’s own set of unique challenges.
All of which either creates weird, unnatural and repeatable C# Syntaxes, or relays heavily on string based solutions.
Post Build MSIL Weaving
C# becomes MSIL, which later becomes Byte Code.
Remember those good old .Net 1.1 days when this chart looked important?
Well, I believe we’ve exhausted all the possible C# hacks to create sustainable and readable INPC properties in straight C# code.
The Silverlight & WPF developer community has been working on this for 2 years. Let’s think outside the C# box.
Specifically, let’s think in the MSIL box.
Post Build MSIL Weaving is the practice of taking compiled MSIL assemblies and manipulating them after compilation.
So, we can write C# code that manipulates our assemblies after they’ve been compiled.
In this article we’ll look at 2 approaches to doing Post-IL weaving: Mono.Cecil and PostSharp.
Low Level MSIL Weaving with Mono.Cecil
This method is super low-level and takes us down to writing code in MSIL with a custom MSBuild Task.
It’s not for everyone, but try and follow.
This is the C# Syntax I want us to end up with:
public class Cow : INotifyPropertyChanged
{
[Property]
public string LastName { get; set; }
1) We’ll start off by creating the PropertyAttribute in our Silverlight project:
[AttributeUsage(AttributeTargets.Property)]
public class PropertyAttribute : Attribute
{
}
2) Next, we’d like to create a Custom “AfterBuild” MSBuild task we can integrate into our project to read this attribute.
Let’s create a new Desktop project to hold that MSBuild Task:
3) Add a reference to the MSBuild V3.5 DLLs:
4) Now that we’ve got our MSBuild references we’ll create a custom MSBuild task:
public class WeavingInpcTask : Task
{
public override bool Execute()
{
SearchForPropertiesAndAddMSIL();
return true;
}
private void SearchForPropertiesAndAddMSIL()
{
}
[Required]
public string SolutionDir { get; set; }
}
5) We’ll need to tell our Silverlight project to execute this task after building our project.
To do that we’ll unload our Silverlight project and add a reference to this task after build.
<UsingTask
TaskName="WeavingINPC.MSBuildTask.WeavingINPCTask"
AssemblyFile="$(SolutionDir)WeavingINPC.MSBuildTask\bin\$(Configuration)\WeavingINPC.MSBuildTask.dll" />
<Target Name="AfterBuild">
<WeavingINPCTask SolutionDir="$(SolutionDir)" />
</Target>
6) Download the latest Mono.Cecil binaries from http://mono.ximian.com/daily/ MonoCharge.
7) Add a reference from our MSBuild project to the unzipped Mono.Cecil.DLL.
Now that we’re done with all the grunt work of setting up an MSBuild Mono.Cecil project we can get down to business.
Here’s how our class looks like:
public class Cow : INotifyPropertyChanged
{
private string _firstName;
public string FirstName
{
get { return _firstName; }
set
{
_firstName = value;
RaisePropertyChanged("FirstName");
}
}
[Property]
public string LastName { get; set; }
public string MiddleName { get; set; }
#region Implement INotifyPropertyChanged
public event PropertyChangedEventHandler PropertyChanged;
public void RaisePropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
#endregion
}
FirstName manually implements INotifyPropertyChanged.
LastName should be auto implemented by Mono.Cecil.
And Middle Name won’t have any INPC support at all.
Let’s open up reflector and see the differences between FirstName and LastName:
But wait, we don’t really care about the C# differences, do we? We care about MSIL!
So let’s change reflector to show us the IL code.
Here’s the set_LastName method MSIL and you can see it doesn’t call RaisePropertyChanged:
And here’s the set_firstName method MSIL that does invoke RaisePropertyChanged:
We can see that if we’re going to add MSIL directly to set_lastName we’re going to need to add these 5 lines of MSIL:
Let’s write the code to load up all the assemblies in our solution and find all properties that have the PropertyAttribute:
foreach (string assemblyPath in Directory.GetFiles(SolutionDir, "*.dll", SearchOption.AllDirectories))
{
AssemblyDefinition sourceAssembly = AssemblyFactory.GetAssembly(assemblyPath);
foreach (TypeDefinition type in sourceAssembly.MainModule.Types)
foreach (PropertyDefinition prop in type.Properties)
foreach (CustomAttribute attribute in prop.CustomAttributes)
if (attribute.Constructor.DeclaringType.FullName == typeof(PropertyAttribute).FullName)
{
Here’s the object model we have to go through to find usages of PropertyAttriubte:
Next, we’ll add those 5 lines of MSIL:
CilWorker MSILWorker = prop.SetMethod.Body.CilWorker;
Instruction ldarg0 = MSILWorker.Create(OpCodes.Ldarg_0);
Instruction propertyName = MSILWorker.Create(OpCodes.Ldstr, prop.Name);
Instruction callRaisePropertyChanged =
MSILWorker.Create(OpCodes.Call, raisePropertyChanged);
MSILWorker.InsertBefore(prop.SetMethod.Body.Instructions[0], MSILWorker.Create(OpCodes.Nop));
MSILWorker.InsertBefore(prop.SetMethod.Body.Instructions[prop.SetMethod.Body.Instructions.Count - 1],
ldarg0);
MSILWorker.InsertAfter(ldarg0, propertyName);
MSILWorker.InsertAfter(propertyName, callRaisePropertyChanged);
MSILWorker.InsertAfter(callRaisePropertyChanged, MSILWorker.Create(OpCodes.Nop));
This isn’t the simplest code you’d ever seen for sure.
But It’s not that hard to see how these 5 InsertBefore/InsertAfter become our 5 MSIL lines.
Look for the words “nop", “ldarg_0”, “ldstr” and “call”. And then it becomes pretty clear we’ve just implemented the missing MSIL.

Next we’ll build our project.
And reflect into set_LastName:
Isn’t that cool? We’ve added 5 lines of MSIL directly into our property.
Let’s run our solution:
So FirstName was changed by our manual INPC implementation, and LastName was changed by our Post-Build MSIL Weaving.
We can now take this solution all the way home and end up with this syntax:
public class Cow : INotifyPropertyChanged
{
[Property] public string LastName { get; set; }
[Property] public string MiddleName { get; set; }
[Property] public string LastName { get; set; }
}
High Level MSIL Weaving with PostSharp
Some people would find MSIL intimidating, writing your own build tasks frightening and reinventing AOP a daunting task.
Those developers are essentially pansy little girls, but let’s see a more straightforward way of doing Post-Build MSIL Weaving.
Step #1: Go to the PostSharp website, register to the website, and install PostSharp. Make sure you close down Visual Studio during installation. Seriously.
Step #2: Add a reference to PostSharp.Loas.SL.dll and PostSharp.Public.SL.dll from your Silverlight project.
(On my dev box PostSharp installed to: C:\Program Files (x86)\PostSharp 1.5\Reference Assemblies\Silverlight 2.0\)
Step #3: What? We’re done?
That was pretty much all the setup you had to do.
Next, we’ll add a the PropertyAttribute so it inherits from OnMethodInvocationAgent and override OnInvocation:
public class PropertyAttribute : OnMethodInvocationAspect
{
public override void OnInvocation(MethodInvocationEventArgs eventArgs)
{
eventArgs.Proceed();
}
}
We’ll make sure that the attribute was applied on a Setter.
public class PropertyAttribute : OnMethodInvocationAspect
{
public override void OnInvocation(MethodInvocationEventArgs eventArgs)
{
eventArgs.Proceed();
if (eventArgs.Method.Name.StartsWith("~set_"))
{
}
}
}
And lastly we’ll call the RaisePropertyChange method with the property Name.
public class PropertyAttribute : OnMethodInvocationAspect
{
public override void OnInvocation(MethodInvocationEventArgs eventArgs)
{
eventArgs.Proceed();
if (eventArgs.Method.Name.StartsWith("~set_"))
{
MethodInfo method = eventArgs.Instance.GetType().GetMethod("RaisePropertyChanged");
method.Invoke(eventArgs.Instance, new object[] {(eventArgs.Method.Name.Replace("~set_", string.Empty))});
}
}
}
This is how our class looks like:
public class Cow: INotifyPropertyChanged
{
private string _firstName;
public string FirstName
{
get { return _firstName; }
set
{
_firstName = value;
RaisePropertyChanged("FirstName");
}
}
public string LastName { get; [Property] set; }
public string MiddleName { get; set; }
#region Implement INotifyPropertyChanged
public event PropertyChangedEventHandler PropertyChanged;
public void RaisePropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
#endregion
}
Let’s run our sample:
And Indeed, Post Build MSIL Weaving worked great here as well.
In reflector we can see the Post-IL weaved code in Last Name:
It isn’t really clear when you first look at it.
But basically, this code just invokes our PropertyAttribute method at runtime.
So PostSharp weaved some MSIL to invoke our code, but not the code itself.
We can take this solution all the way home and end up with this syntax:
public class Cow : INotifyPropertyChanged
{
public string LastName { get; [Property] set; }
public string MiddleName { get; [Property] set; }
public string LastName { get; [Property] set; }
}
What’s next?
In this article I’ve shown 2 Post-Build MSIL weaving techniques:
1) Mono.Cecil – that changes MSIL directly at compile time.
2) PostSharp – that changes MSIL to invoke our code at runtime.
If you’re going to use any of these options, you’ll have to consider where it’s best for you to apply your attribute – on the class? on each property? on the entire assembly?
Are you going to implement INPC with default values, raising notifications only on changes, cross-property change notifications, and a myriad of other features? Or just stick to the basics?
If you’re going to go with Mono.Cecil you’ll have to work a bit more on the infrastructure of the MSBuild tasks and Visual Studio integration.
If you’re going to go with PostSharp you’ll have to spend some time looking at the various classes and overloads offered in the framework.
Plus, you’ll have to consider the performance and payload ramifications.
Fin
In my opinion, Post-Build MSIL Weaving provided us with the best and simplest solution.
We can support the property INPC syntax:
public class Cow : INotifyPropertyChanged
{
public string LastName { get; [Property] set; }
public string MiddleName { get; [Property] set; }
public string LastName { get; [Property] set; }
}
or the class INPC syntax:
[INPC]
public class Cow : INotifyPropertyChanged
{
public string LastName { get; set; }
public string MiddleName { get; set; }
public string LastName { get; set; }
}
or even go with the assembly Wide syntax:
Each of these syntaxes affords us total control of our INotifyPropertyChanged scenario without having to change the look, feel and flow of our code.
Sincerely,
-- Justin Angel
Silverlight Weblog Features Overview
Hi folks,
Welcome to my new Silverlight Weblog!
Join me on a tour of the numerous features this blogging engine offers.
But first: Why Silverlight?
Simply put: my time is worth money.
I blog as a labour of love, devotion and personal passion.
As such, I’d like to spend the minimum amount of time required to setup a blog.
Upon deciding to move out of my previous blog, I faced two prospects:
- Move to another HTML based weblog – which normally takes me a week (60 hours~) of fiddling around with HTML, CSS, Javascript and deployment.
- Recreate my blog in Silverlight – which I estimated would take about the same and I’d finally be rid of this horrible nuisance – HTML, Javascript and CSS.
For me, it was an easy choice.
Now I get to experiment with which UI patterns work best for blogging.
Not just use the same handy down comprise-driven-UI I’ve always had.
Using accordions for the front page, expanding panels for comments, responsive user interface, and many others are just some of the benefits Silverlight afforded this blog.
What Technologies were used?
Silverlight - User Interface front end.
WCF RIA Services - Server<->Client Communication.
PRISM – Commanding and Messaging support.
Managed Extensibility Framework (MEF) – Dynamically loading blog widgets.
ASP.Net Webforms & ASP.Net MVC – Processing client requests and returning shell HTML.
ASP.Net Dynamic Data (AJAX, Webforms) – The back office management portal.
WCF Syndication Services – RSS 2.0 and Atom 1.0 Feeds.
SQL Server 2008 – Database.
Entity Framework V3.5 – Data Access Layer.
MSTest - Unit testing Server side.
Microsoft Silverlight Unit Testing Framework – Unit testing Client side.
RhinoMocks – Unit testing Mocking.
Unity – IoC Unit Testing framework.
3rd Party commercial component – rendering out HTML in Silverlight.
Everything else – home grown.
Is Silverlight Weblog open source?
Yes. Get it @ http://SilverlightWeblog.codeplex.com under Ms-PL license.
Posting from Windows Live Writer
A major part of my workflow when authoring blog posts is using Windows Live Writer.
Support for Windows Live Writer is enabled through the Metaweblog API.
Here’s a sample blog post in Windows Live Writer:
After publishing this blog post to Silverlight Weblog it’ll render out as expected:
Hierarchical Blog Categories Widget
When posting from Windows Live Writer the blog post author can tag blog posts.
The Silverlight Weblog blogging engine support endlessly nested hierarchical categories.
The Print screen above shows 3 levels of Categories, but in theory the blog supports infinite nesting.
Clicking on a Category Link in the Categories Tree or In a Blog post would navigate the user to a list of all blog posts in that category.
Hierarchical Comments
Silverlight Weblog supports leaving infinitely nested comments on blog posts.
Having properly nested comments elevates the mess of replying to 6 people in one comment.
As you can see from the above print screen each comment is accompanied with a Gravatar and can be infinitely nested to a parent.
RSS 2.0 and Atom 1.0 Feeds
Silverlight Weblog supports RSS and Atom feed formats through the Links Widget.
Here’s a print screen of the main blog RSS feed:
Moreover, Silverlight Weblog supports RSS/Atom feeds for each individual category:
Clicking the RSS Icon next to each category would provide the user with a link to it’s RSS link.
Twitter Integration
Silverlight Weblog displays the latest non-reply tweets from the blog author on the Sidebar.
When mouseovering the Twitter widget it smoothly expands it’s vertical height to accoemdate easier reading.
At the end of each blog post, it is suggested to the user to retweet this blog post:
Clicking the “Twitter this blog post” link would open up a new tweet on twitter.com.
User Friendly URLs
Silverlight Weblog generates easy to remember user-friendly URLs.
This enables people to easily link to your Rich Internet Application and have it load up the relevant blog post.
Single and Multi-user Support
When hosting blogs, some blogs are meant for only one user while others are used by multiple users.
Silverlight Weblog supports both schemes with it’s easy to customize UI and user-relevant URLs.
In the print screens above it’s easy to see how the blog supports multiple users through a convenient URL schema.
Authoring Rich Content with Macros
When authoring blog posts It’s always helpful to introduce richer content.
Macros translate text into visual elements.
For instance, the following macro (presented here as an image) would translate into a video player:
![[SimpleVideo:source=http://mschannel9.vo.msecnd.net/o9/mix/09/wmv/key01.wmv | width=650 |height=400] [SimpleVideo:source=http://mschannel9.vo.msecnd.net/o9/mix/09/wmv/key01.wmv | width=650 |height=400]](http://justinangel.net/BlogFiles/WindowsLiveWriter/SilverlightWeblogFeaturesOverview_7BB2/image_67.png)
[SimpleVideo:source=http://mschannel9.vo.msecnd.net/o9/mix/09/wmv/key01.wmv|width=650|height=400]
This next macro will translate a literal string into a Visual XAML element:
[Xaml:string=<Rectangle Width="250" Height="250" Stroke="Black" StrokeThickness="1" Fill="Red" />]
And this last built-in macro would load up an external XAP file and present it’s main visual element:
[Xap:source=SilverlightTestApp.xap|type=SilverlightTestApp.MainPage]
SEO (Search Engine Optimization)
Silverlight weblog is fully indexable by Google, Bing and Yahoo.
Each Silverlight weblog has a Robots.txt and a Site Map which lets the search engine know of the available blog posts.
Each blog post page emits the proper HTML as to be indexed by google and accessible to disabled users.
In the following print screen you can see the Text-Only Webrowser Lynx fully reading the content of a Silverlight Weblog blog post:
If you’re interested in this SEO technique you can read more about it at Brad Abrams’ blog post.
Deep Linking and Back/Forward Support
When navigating in Silverlight Weblog the URL Anchor fragment changes accordingly.
Which enables Silverlight Weblog to support Refresh, Back and Forward uses.
Search Deep Linking
Category Deep Linking
Blog Post Deep Linking
Home Page Deep Linking
Google Analytics Support
Every user action performed in Silverlight Weblog can be logged into Google Analytics.
In the above print screen there’s a list of all User initiated actions (which did not cause a server postback) and have been logged in Google analytics.
Primarily, Loading Blog Posts, Performing Searches, Leaving Comments, Viewing categories and other user initiated actions.
Silverlight weblog also supports drilling down into specific google analytics data.
Here’s a drill down of which searches were performed and which categories were viewed:
On the left there’s a list of search terms searched, and on the right a list of categories viewed.
Management Back office
Using ASP.Net Dynamic Data an easy to use back office is generated for silverlight weblog.
Extensibility
Silverlight Weblog is fully extensible.
Each piece of the UI is considered a Widget and each response is an independent Service.
Widgets and Services communicate through Messages.
Both of which are loaded dynamically by MEF which ends up composing the UI.
I’ll go into greater detail on this topic in future blog posts, but here’s an easy to understand breakdown of the UI:
Implementing custom Widgets and changing the default layout and behaviour is much easier than it appears to be.
Personalized Widgets
Through the Extensibility mechanism in Silverlight Weblog I easily implemented two of my favourite widgets.
Custom Get Silverlight Screen
When first approaching a Silverlight Weblog, users will get an easy to understand “Get Silverlight” screen.
Customized Splash Screen
Silverlight Weblog supports changing the default loading splash screen into a more relevant user exprience.
In my case I wanted to let people know they were going into my blog.
Fin
There are many other features included in Silverlight Weblog.
Hopefully this first Silverlight weblog blog post was enough to give you a quick overview of this project.
One final note is I’d like to thank all Beta Testers who took part in making sure Silverlight Weblog actually works:
Scott Hanselman, Rob Eisenberg, Velvárt András, Yasser Makram, Rui Marinho, Ian Smith, Juan Puebla, Mark Woodhall and Jim Wightman.
Feel free to leave a comment and let me know your thoughts!
Sincerely,
-- Justin Angel
About Justin Angel
This is a place holder blog post for some interesting “About Me” information for Justin Angel.
In the meanwhile, here are some pictures of Justin.
Talking at Vancouver TechFest 2009 on Silverlight Unit Testing
Striking a pose at “Party With Palermo” MVP Summit 2008
Striking another pose with Joey Devilla at Coffee and Code Vancouver
Snowboarding (as such) in Whistler
Yeah, these poses only work if you photoshop in a fireball.