Entries in Silverlight (17)

Wednesday
Jan192011

Silverlight + SharePoint - add to web part gallery

This article covers how to go about packaging a webpart for the Silverlight XAP file and deploy it to Web Part Gallery.

In the previous article I described the steps to create a SharePoint project and deploy the XAP file via a site feature.

Silverlight + SharePoint 2010 - package XAP file in a sandbox WSP Solution

But the user still needs to manually:

  1. Find the XAP file URL
  2. Manually insert a Microsoft out of box Silverlight Web Part
  3. Paste the XAP URL
  4. Save the Silverlight Web Part (may have to configure InitParams too).

Let's simplify this a little.

Go back to the <Elements> Module, and lets add a webpart

<?xml version="1.0" encoding="utf-8"?>
<Elements xmlns="http://schemas.microsoft.com/sharepoint/">
  <Module Name="SilverlightCamera">
    <File Path="SilverlightCamera\SilverlightCamera.xap" Url="Style Library/SilverlightCamera.xap" />
    <File Path="SilverlightCamera\SilverlightCamera.webpart" Url="_catalogs/wp/SilverlightCamera.webpart" />
  </Module>
</Elements>

 

The webpart file itself is very simple: go to Web Parts Gallery, find and export Microsoft's Silverlight.webpart

<?xml version="1.0" encoding="utf-8"?>
<webParts>
  <webPart xmlns="http://schemas.microsoft.com/WebPart/v3">
    <metaData>
      <type name=
"Microsoft.SharePoint.WebPartPages.SilverlightWebPart, Microsoft.SharePoint, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" />
      <importErrorMessage>Cannot import this Web Part.</importErrorMessage>
    </metaData>
    <data>
      <properties>
        <property name="Title" type="string">Silverlight Camera Web Part</property>
        <property name="Description" type="string">A web part to display a Silverlight Camera.</property>
        <property name="Url" type="string">~sitecollection/Style Library/SilverlightCamera.xap</property>
        <property name="Height" type="int">300</property>
      </properties>
    </data>
  </webPart>
</webParts>

 

You may have noticed that the Url is a SiteCollection relative (~sitecollection/) path, so that no matter whether it's a root site collection or a managed path, we want the user's experience to be perfect.

Unfortunately - that syntax doesn't work, not without one extra hack via the Feature Event Receiver.

Feature Activated Event Receiver

 

Right click on the feature and add a new event receiver.  VS.NET will generate the CS file.

clip_image002

Figure: right click on the feature (.feature) and add an event receiver. 

 

Waldek has a magical event handler code that fixes this problem

http://blog.mastykarz.nl/inconvenient-content-query-web-part-server-relative-urls/

So I'll just post my bit of code here

public override void FeatureActivated(SPFeatureReceiverProperties properties)
{
    // one day when I meet Waldek in person I will buy him beer
    // http://blog.mastykarz.nl/inconvenient-content-query-web-part-server-relative-urls/
    SPSite site = properties.Feature.Parent as SPSite;
    if (site != null)
    {
        SPList webPartsGallery = site.GetCatalog(SPListTemplateType.WebPartCatalog);
        SPListItemCollection allWebParts = webPartsGallery.Items;
        SPListItem webPart = (from SPListItem wp in allWebParts
                              where wp.File.Name == "SilverlightCamera.webpart"
                              select wp).SingleOrDefault();
        if (webPart != null)
        {
            string siteCollectionUrl = site.ServerRelativeUrl;
            if (!siteCollectionUrl.EndsWith("/"))
            {
                siteCollectionUrl += "/";
            }
            string fileContents = Encoding.UTF8.GetString(webPart.File.OpenBinary());
            fileContents = fileContents.Replace("~sitecollection/", siteCollectionUrl);
            webPart.File.SaveBinary(Encoding.UTF8.GetBytes(fileContents));
        }
    }
}

 

During Feature Activated - find the webpart and fix the ~sitecollection marker with the real site collection URL.

 

Deploy and activate the feature again.

clip_image002[5]

 

Success!  Our .webpart now appearing in the web part gallery.

Wednesday
Jan192011

Silverlight + SharePoint 2010 - package XAP file in a sandbox WSP Solution

This is a long series of blog posts on developing, debugging and deploying Silverlight and SharePoint solutions.

http://johnliu.net/blog/2010/6/18/develop-and-deploy-silverlight-sharepoint-2010-solutions.html
http://johnliu.net/blog/2010/6/22/develop-and-deploy-silverlight-sharepoint-2010-solutions-par.html
http://johnliu.net/blog/2010/6/28/develop-and-deploy-silverlight-sharepoint-2010-solutions-par.html
http://johnliu.net/blog/2010/10/18/silverlight-sharepoint-2010-did-you-just-deploy-customizatio.html

 

Assuming by this point, you have built a XAP file from a Silverlight project, and can deploy it manually to SharePoint by uploading to a SharePoint document library and link up Microsoft's Silverlight web part to "play" your XAP file.

Now let's see how we can build a WSP package.

Create a SharePoint Project in your solution

 

clip_image002

Figure: Add a new Empty SharePoint Project

 

clip_image002[5]\

Figure: Provide a debug site, and choose Sandboxed solution

 

clip_image002[7]

Figure: Add a module to this project - you should give it a good name

 

clip_image002[9]

Figure: Select Properties of this module…

 

The following is very special.  If you blink you will miss this!

 

From the Module's properties, find Project Output References and open this dialog.
Then from the dialog

  • select "ElementFile" for deployment type
  • select the project output of your Silverlight project

This step ensures that the Silverlight project output (XAP file), is automatically included as an element file in your SharePoint module. 

Magical!  But very well hidden UI.  Most people don't know it's there!

clip_image002[11]

Figure: Add the Project Output to this module.

 

Open up the module.xml file and check:

<?xml version="1.0" encoding="utf-8"?>
<Elements xmlns="http://schemas.microsoft.com/sharepoint/">
  <Module Name="Module1">
    <File Path="Module1\SLSPConf.xap" Url="Style Library/SLSPConf.xap" />
  </Module>
</Elements>

I set the destination URL to be Style Library.  You can put it elsewhere but you'll need somewhere where people can actually have read-access.

 

Deploy to Solution Gallery

 

clip_image002[13]
Figure: Deploy the Silverlight solution to Solutions Gallery - you can activate the sandbox solution here.

 

clip_image002[15]

Figure: Activate the site feature

 

VS.NET debugging tip

 

Go to the properties for the SharePoint project.  Select the SharePoint tab, and scroll right down.

  • Tick "Enable Silverlight debugging"
  • Untick "Auto-retract after debugging" - this one makes VS.NET deploy and activate when F5.  But as soon as you stop debugging VS.NET will retract the solution - so your Silverlight stops working!

 

clip_image002[17]

Figure: VS.NET SharePoint project settings.

Wednesday
Sep152010

Silverlight - sharing a common class between Silverlight and .NET

Because Silverlight is compiled against a separate set of Silverlight runtime, we can not reference or share a common library between a Silverlight project and a normal .NET project.

One very common and simple workaround is then to create a common project for .NET, and a common project for Silverlight, and then add the files as existing links from the .NET project to the Silverlight project.  This ensures that the same files are shared by the two sides and we have our matching class definitions.

 

With RIA, there is a new way.

image

In the .NET project, name your extra files with xxx.shared.cs

Compile your .NET project.  This triggers the RIA toolkit to run and generate some files for us:

image

 

So you no longer need to share a file via external link.

Wednesday
Sep152010

Silverlight - the magic of Silverlight RIA Toolkit

 

I checked in some code.  Moments later, my colleague asked me:

"did you check in the service reference for this ServiceReference.DispatchKing.Web.Services.RunboardDuplexService"

I pondered, and answered:

"no need - it was generated for me"

In deed - my Silverlight project started to do something magical for me.  It was creating GeneratedWcfClientCode\ServiceReference.cs

Because this was generated like RIA services, it was not included in the project, and thus - if you didn't have the right tools installed, it appears that your colleague has forgotten to check in files!

 

image

 

Memory Lane

In the old days before RIA services, we rolled out WCF Service and added our own Service Reference via the Silverlight project.  This generated all sorts of service reference proxy classes.

The biggest downside, is that each time we updated the service, we had to regenerate the service reference, otherwise they'd become out of sync and Silverlight will crash and burn.  (or in the case where it didn't crash… we got really scared).

 

In Silverlight 3 we got RIA and the DomainService, having Silverlight project linked to a Web Project meant that Visual Studio automatically started generating all the RIA/DomainService code, as well as the data objects (or entity) required, and making sure everything's available and synchronized between the Web (service) and Silverlight (client).  There was much rejoicing.

What's this magic?

What made this case really special then, is that I'm not using only Domain Services.

image

 

Hmm it doesn't work for my colleague.  We quickly compared out installed Programs and Features, and found out why:

image

 

You can grab this via Web platform installer, or directly from:

http://www.microsoft.com/downloads/en/details.aspx?FamilyID=7b43bab5-a8ff-40ed-9c84-11abb9cda559&displaylang=en

The RIA Services Toolkit enables the following 5 features:

 

1. LinqToSql DomainService
2. Soap endpoint - This enables you to expose a soap endpoint for your DomainService
3. JSON endpoint - This enables you to expose a JSON endpoint for your DomainService
4. ASP.net DomainDataSource - This control will enable you to create an ASP.net application that can talk to your DomainService
5. WCF client proxy auto generation/updating for WCF Core Service – This enables you to get up-to-date WCF proxy and configuration each time you build your solution, when you add Silverlight-enable WCF service in your Silverlight application.

 

The magic, is number 5.

Tuesday
Aug312010

Silverlight - RadScheduler and required field data validation

In my current project I'm having the pleasure of working with the Silverlight RadScheduler control.  While you may read and feel it is a very bloated control, I argue otherwise, it is highly extensible for such a complex control.  I really think the Telerik guys did a very good job.

My particular scenario is interesting.  I need to:

  1. Extend the appointment dialog to support addresses
  2. The addresses aren't always required (so I can't just use RequiredAttribute)
  3. When fields are required, I'd like the validation to kick off and prevent me from saving that appointment

Here we go:

Updating the RadScheduler template:

This is pretty simple - find the EditAppointmentTemplate

  <ControlTemplate x:Key="EditAppointmentTemplate" TargetType="telerik:AppointmentDialogWindow">
  1. Customize the control template, we added additional fields
  2. NotifyOnValidationError and ValidatesOnException are important.
..snip

          <input:ValidationSummary x:Name="ValidationSummary" />

          <TextBlock Text="Suburb" Style="{StaticResource FormElementTextBlockStyle}"/>
          <TextBox Text="{Binding Suburb, Mode=TwoWay, NotifyOnValidationError=True, 
ValidatesOnExceptions=True}" />

Extend the Appointment class:

  1. Create a ValidationEnabled property - sometimes you need to "not validate"
  2. Create a ValidateProperty method - this takes a property name and triggers all validate attributes on that property - Validator.ValidateProperty is the method that will trigger all kinds of fun validation exceptions for us.
  3. Set the appropriate validation attributes on the property e.g. Suburb - I'm using a custom validation property that I've created based on the RequiredAttribute
  4. In the setter, call ValidateProperty.
    public class JobAppointment : Appointment
    {
        private string _suburb;

        public ObservableCollection<ValidationError> Errors { get; set; }

        public bool ValidationEnabled
        {
            get;
            set;
        }

        private void ValidateProperty(string propertyName, object value)
        {
            if (ValidationEnabled)
            {
                var context = new ValidationContext(this, null, null);
                context.MemberName = propertyName;

                Validator.ValidateProperty(value, context);
            }
        }

        [ValidationRequired]
        public string Suburb
        {
            get
            {
                return _suburb;
            }
            set
            {
                ValidateProperty("Suburb", value);

                if (_suburb != value)
                {
                    _suburb = value;
                    OnPropertyChanged("Suburb");
                }
            }
        }

Add our own Validation attributes

  1. This Required attribute checks against our settings to see if "Suburb" happens to be a required field.  If not, then skip the validation and just return success.
 public class ValidationRequiredAttribute : RequiredAttribute
    {
        protected override ValidationResult IsValid(object value, ValidationContext validationContext)
        {
            if (SettingsLifeTimeService.Current != null )
            {
                if (SettingsLifeTimeService.Current.RequiredFields.Contains(validationContext.MemberName))
                {
                    // RequiredAttribute validation
                    return base.IsValid(value, validationContext);
                }
            }
            return ValidationResult.Success;
        }
    }

 

Stop validation on Scheduler's Appointment_Saving event

  1. In the AppointmentSaving event, grab all the children framework elements from the appointment dialog window
  2. Find their binding expression (e.g., for TextBox I want the binding expression for Textbox.Text)
  3. If exists, I want it to push the values back into the datasource (our customized Appointment class) - this triggers the property setter (which triggers our ValidateProperty, which triggers the custom Required attribute)
  4. Finally, check the validation summary to see if we have any binding errors, if we do, cancel the save.
void _scheduler_AppointmentSaving(object sender, AppointmentSavingEventArgs e)
        {
            JobAppointment jobApp = e.Appointment as JobAppointment;
            AppointmentDialogWindow window = e.OriginalSource as AppointmentDialogWindow;
            var children = window.ChildrenOfType<FrameworkElement>();

            if (children != null)
            {
                foreach (var element in children)
                {
                    BindingExpression binding = null;
                    if (element is TextBox)
                    {
                        binding = element.GetBindingExpression(TextBox.TextProperty);
                    }

                    if (binding != null)
                    {
                        // force control to update databound VM.  This triggers the validation.
                        binding.UpdateSource();
                    }
                }
            }

            ValidationSummary summary = window.FindChildByType<ValidationSummary>();

            if (summary != null)
            {
                if (summary.HasErrors)
                {
                    e.Cancel = true;
                    return;
                }
            }
        }

Finished!

 

image

Monday
Jul192010

Silverlight - code behind back to MVVM

Philosophical difference between code-behind and the ViewModel

Because VS.NET is firstly a visual tool, it tries very hard to give developers an easy starting point where you can create your layout and easily add code to various events.

The problem this creates, is that the end result code in the events is a mixture of two type of code:

  • Data access, relating to service calls, maintaining in memory state of the object.
  • Updates to the UI.

The former is what we’re trying to test, whereas the UI is always difficult to test, especially if compounded with animation or frequent changes to the UI.

 

Why do we need to do this?

  • Code-behind is part of the View, application logic doesn't belong here
  • Prolonged mixing of application logic WITH UI login in the code-behind leads to spaghetti code, the main issue is maintenance cost
  • Unable to easily separate and test functionality

 

Refactor and take control - bring Code Behind back into MVVM

I tackle this problem in stages, without stages the problem is too big and you may not be able to stop halfway.

Stage 1 - creating a ViewModel

A ViewModel is a model for a view.  So in your ViewModel you should have any information that is necessary for the UI (view) to render properly.  Remember to implement INotifyPropertyChanged

public class MainViewModel : INotifyPropertyChanged
{
}

 

Add the ViewModel to your View - this can be done in the XAML

 <UserControl.Resources>
    <ViewModel:MainViewModel x:Key="MainViewModelInstance"/>
  </UserControl.Resources>

You can reference the ViewModel in your code behind this way:

var resourceMainViewModel = this.Resources["MainViewModelInstance"];
_mainViewModel = resourceMainViewModel as MainViewModel;

 

Stage 2 - moving application state variables to ViewModel

A typical sign of a lot of code-behind is when we have a lot of member variables in the View.

  1. A majority of them can be moved directly into the ViewModel.
  2. Provide property get/set on the ViewModel and re-wire the Code-behind logic to use the ViewModel for now.

Exercise some judgement here - not all variables should go to the ViewModel, references to UI elements for example, should remain in the code-behind (view).

 

Stage 3 - moving application methods to ViewModel, hooking UI events

Once you've moved your variables to the ViewModel - then you can start looking at moving the methods to the ViewModel.

The trickiest part here, if you have started with code-behind, is that there will be methods that have a mixture of application logic along with UI update calls.

An easy, intermediate way to handle these UI calls is to have the VM raise an event at the specific time, and have the View bind to those events and make the UI updates.

Change any UI update code to UI binding syntax - this should be the normal for most VM entity or collections.

 

Stage 4 - you can start creating Silverlight Unit Tests

By stage 3 - you should have more and more application logic moved back to the ViewModel.  Add a Silverlight Unit Testing project and start instantiating the ViewModel and test the collections and entities can be populated correctly.

From this point onwards because you have the unit tests available you can start to do a lot more refactoring with the methods without worrying that you'll be breaking stuff. 

 

Stage 5 - calling application methods via Commands

Create a DelegateCommand class - unfortunately one isn't provided with Silverlight (I don't know why)

public class DelegateCommand : ICommand
{
    Func<object, bool> canExecute;
    Action<object> executeAction;
    bool _canExecuteCache;

    public DelegateCommand(Action<object> executeAction, Func<object, bool> canExecute)
    {
        this.executeAction = executeAction;
        this.canExecute = canExecute;
    }

    public DelegateCommand(Action<object> executeAction)
        : this(executeAction, null)
    {
    }
    #region ICommand Members

    public bool CanExecute(object parameter)
    {
        if (canExecute == null)
        {
            return true;
        }

        bool temp = canExecute(parameter);
        if (_canExecuteCache != temp)
        {
            _canExecuteCache = temp;
            if (CanExecuteChanged != null)
            {
                CanExecuteChanged(this, new EventArgs());
            }
        }
        return _canExecuteCache;
    }

    public event EventHandler CanExecuteChanged;

    public void Execute(object parameter)
    {
        executeAction(parameter);
    }

    #endregion
}

In the ViewModel - change the direct method call from the view into a property DelegateCommand, then in the View, change the Click event handler to Command property via Binding to the ViewModel.DelegateCommand

 

 

Stage 6 - break UI event hooks by binding storyboards via triggers

This is by far the most trickiest - depending on what sort of UI updates you had in the code-behind, it may be very tricky to convert them into storyboards.

Do your best, but don't go overboard on this one - I don't have a good all-round-fix for this one yet.

Friday
Jul162010

SharePoint 2010 and Silverlight 4.0 Webcam

Tonight's Silverlight play involves Silverlight 4 Web Cam API, and SharePoint 2010 Client Object Model.

SILVERLIGHT 4 WEBCAM API

Silverlight 4's WebCam API is relatively simple:

_captureVideoDevice = CaptureDeviceConfiguration.GetDefaultVideoCaptureDevice();
_captureSource = new CaptureSource();
VideoBrush brush = new VideoBrush();
brush.SetSource(_captureSource);

WebCameraCapture.Fill = brush; // fill rectangle "render" area

var device = _captureVideoDevice.FriendlyName; // check device name during debug
if (CaptureDeviceConfiguration.AllowedDeviceAccess || CaptureDeviceConfiguration.RequestDeviceAccess())
{
    _captureSource.Start();
}

The most interesting part is probably the RequestDeviceAccess call. This call must be initiated in an UI event (click) and will raise the following dialog

clip_image002

 

TAKING SNAPSHOTS

Once the camera is rolling, clicking the button takes a snapshot and saves it back to the document library.

WriteableBitmap bmp = new WriteableBitmap(WebCameraCapture, null);
MemoryStream stream = new MemoryStream();
// didn't want to save as bitmap - convert to JPEG first
EncodeJpeg(bmp, stream);

var fileCreationInfo = new FileCreationInformation();
fileCreationInfo.Content = stream.ToArray();
fileCreationInfo.Url = string.Format("pic_{0}.jpg", DateTime.Now.Ticks);
var _documentLibrary = _clientContext.Web.Lists.GetByTitle("ListName");
var uploadFile = _documentLibrary.RootFolder.Files.Add(fileCreationInfo);
_clientContext.Load(uploadFile);
_clientContext.ExecuteQueryAsync(delegate{}, delegate{});

In this screenshot I've added the Silverlight web part on top of the asset library list.

clip_image004

 

WHAT ABOUT VIDEO?

This is where we'll probably get stuck. Silverlight 4 has API to access camera, and we're able to save bitmap data from the camera, but Silverlight 4 lacks the client side encoding libraries capable of saving the bitmap stream back into any meaningful format such as MPEG.

Until Microsoft provides one, or some 3rd party writes one, saving video back to SharePoint document library is going to get put on hold.

REFERENCE

The code for converting to JPEG I copied from http://stackoverflow.com/questions/1139200/using-fjcore-to-encode-silverlight-writeablebitmap.

DOWNLOAD

The Silverlight Camera XAP binaries are in this XAP file 

The Silverlight Camera Sandbox Solution and read me file 

 

INSTALLATION

  1. Upload the XAP file to a document library, copy the URL to this XAP file.
  2. Create a document library to host all the pictures, open the web part zones on the view page and insert a Silverlight Web Part - use the URL to XAP from step 1.
  3. Remember to refresh the page to see new pictures - the list doesn't refresh automatically.  You can switch on async properties in the web part editor for the pictures and it will update itself on a timer basis.

UPDATE

20 July - Added sandbox solution WSP and a simple read-me file

Tuesday
Jul132010

Silverlight - merging detached object back to the attached data context

This is a short post on something that we did in the days of Silverlight 2~3, before we have RIA services.

Consider two method signatures on the service:

  1. public EntityPerson GetPerson(object key);
  2. public void SavePerson(EntityPerson person);

Silverlight gets an EntityPerson object, exposes it via the ViewModel for databinding.  The user hits the save button, and the data is coming back through the wire.

It comes back to SavePerson, but the object is disconnected.

The way we've always done this is this way:

//Update or Insert person:
if (entityPerson.PersonID > 0) //update 
{
    EntityKey key = entities.CreateEntityKey("tblPerson", entityPerson);
    object originalItem;
    if (entities.TryGetObjectByKey(key, out originalItem))
    {
        // merge changes from the client back to the dataset
        entities.ApplyPropertyChanges(key.EntitySetName, entityPerson);
    }
}
else//insert new 
{
    entities.AddObject("tblPerson", entityPerson);
}

ApplyPropertyChanges works very well and takes a lot of the work out of our hands.  You can attach a very simple conflict detection if you have a Timestamp field, and by setting the ConcurrencyMode=Fixed.

Saving Changes and Managing Concurrency
How to: Manage Data Concurrency in the Object Context
Flexible Data Access With LINQ To SQL And The Entity Framework

I was recently involved in a short discussion on this topic, and was intrigued to go digging and get to the bottom of it all.  Hope these links help someone :-)

Saturday
Jul102010

Silverlight PivotViewer and SharePoint

 

Microsoft just released a new control PivotViewer for Silverlight. 

http://www.microsoft.com/silverlight/pivotviewer/

If you haven't seen this yet - go check out the video then come back, I'll wait here.

 

It's a web-based control to provide pivot functionality for datasets.  For fixed data - you can pre-generate the data set. For dynamic data, the collection could be generated dynamically and served.

So what would happen if you throw it at SharePoint, I ask?

The initial results are astounding!  Check these out:

image

Figure: Connecting it to listdata.svc OData service

At the root level it really is quite boring.

 

image

Figure: Connecting to a document library OData service

OK this is getting fun.  Facets!  Sort/Display/Zoom

image

Figure: Sorting the document library by Modified date

You probably can see I've uploaded a bunch of documents in the last hour :-)

 

The screenshots don't do this justice - when you filter down, change sort… the boxes fly all over the place it's as if I'm literally shaking SharePoint apart to zoom in on my data.

 

So it looks like I'll be extremely busy next couple of weekends and evenings:

  • Generate collection data across site - this will let me filter the "Path" facet to select which list/document library I'm after
    • What does this mean for big sites?
  • Add more visualizers, use Word/PPT thumbnails
  • Create configurable background colours for different content types
  • Add a contextual menu and hook it back through SharePoint actions
  • And the Holy Grail - is it possible to make this work as a Sandbox Solution?
    • The current sample reference application requires IIS HttpHandlers to serve dynamic collection data consumed from OData. 
    • Sandbox solution problem may be tricky.

 

It's times like this I stood back and appreciate what Microsoft does.  You guys are awesome.  Different teams produce different software and they just magically work together.  Makes us look like heroes.

/Salute

Wednesday
Jul072010

Silverlight Unit Testing - adding a timeout to EnqueueConditional

Since a lot of Silverlight work is asynchronous in nature, the Silverlight testing framework has many helper functions to essentially do "non blocking wait until something happens"

The curious one to me is EnqueueConditional(Func<bool> conditionalDelegate

This one essentially waits until the condition is true - so you can call a method to populate your view model with data, and then wait until data.Count > 0

But the method has no support for timeout.  It can, and will, hold the unit testing framework in progress forever.

Here's my little tweak to the method.

        public override void EnqueueConditional(Func<bool> conditionalDelegate)
        {
            DispatcherTimer timer = new DispatcherTimer();
            timer.Interval = new TimeSpan(0, 0, 5);
            timer.Tick += delegate
            {
                // remember to stop timer or it'll tick again
                timer.Stop();
                throw new TimeoutException();
            };
            EnqueueCallback(delegate
            {
                timer.Start();
            });
            base.EnqueueConditional(conditionalDelegate);
            EnqueueCallback(delegate
            {
                timer.Stop();
            });
        }

Here's the unit test to go with it

        [TestMethod]
        [TestProperty("TestCategory", "Silverlight")]
        [Asynchronous]
        [ExpectedException(typeof(TimeoutException))]
        public void Test_Timeout()
        {
            EnqueueConditional(delegate
            {
                // return?  never!
                return false;
            });

            EnqueueTestComplete();
        }

And one more tweak in the App.xaml.cs

        private void Application_UnhandledException(object sender, ApplicationUnhandledExceptionEventArgs e)
        {
            if (e.ExceptionObject is TimeoutException)
            {
                e.Handled = true;
                // stop any timeoutexception here or it'll bubble to the DOM
                return;
            }

            ...
        }

Here's the result picture:  Running just slightly over 5 seconds.

image

 

Updated: added a result picture.