More On Caliburn & Silverlight’s SaveFileDialog

I was a bit hasty in my earlier post when I called the SaveFileDialog inadequate.

One complaint I was going to make was simply a matter of my ignorance. It had to do with the dialog thinking that it was not initiated by the user.

In particular, it was some of our Caliburn magic obscuring this. However, it’s powerful white magic that does more good than harm. :-)

The problem is this, the SaveFileDialog can only be shown as a result of a user’s action, such as clicking a button. (I’m sure this is driven by security concerns that aren’t entirely clear to me.)

If your code follows this sequence:

  • user clicks a button
  • the event handler initiates a web request
  • the callback for the web request tries to show a SaveFileDialog
  • the world ends (not with a bang)

Then you will get the “Dialogs must be user-initiated” exception, because Silverlight doesn’t trace the web request callback back to the button click.

Deep Magic

In regards to the Caliburn magic that obscures this, I had a method that looked something like this:

public IEnumerable<IResult> DownloadFile()
{
    var result = new Result();
    var query = new GetBinaryContent(_data.ContentId);
    yield return new ProcessQuery(query, result);

    if (result.HasErrors)
    {
        yield return Show.Errors(result);
        yield break;
    }

    var saveFileResult = new SaveFile();
    yield return saveFileResult;

    if (saveFileResult.FileStream == null) yield break;

    var content = result.FindResponse(query).Data;
    using (var writer = new BinaryWriter(saveFileResult.FileStream))
    {
        writer.Write(content);
        writer.Flush();
    }
}

This method lives on the ViewModel, and _data is part of that model. It contains the metadata for a file (name, type, last updated) as well as an id for fetching the actual binary content.

First, it processing a query, which is actually an asynchronous web request. If there are errors then it displays them, otherwise it opens a save file dialog. If the users provides a file, then the binary content is written out. The problem here, that is not obvious, is that the save dialog is being opened as a result of the web request and not directly from the user’s click. If I reorder the code in the method, so that the save dialog is shown before processing the query then the exception goes away. In other words, the code below does not throw an exception.

public IEnumerable<IResult> DownloadFile()
{
    //open the dialog _before_ the web request
    var saveFileResult = new SaveFile();
    yield return saveFileResult;

    if (saveFileResult.FileStream == null) yield break;

    var result = new Result();
    var query = new GetBinaryContent(_data.ContentId);
    yield return new ProcessQuery(query, result);

    if (result.HasErrors)
    {
        yield return Show.Errors(result);
        yield break;
    }

    var content = result.FindResponse(query).Data;
    using (var writer = new BinaryWriter(saveFileResult.FileStream))
    {
        writer.Write(content);
        writer.Flush();
    }
}

N.B. All of these implementations of IResult are specific to our project and are not a core part of Caliburn. However, Rob is creating similar (and improved) versions of a few in the samples.


Posted 10-06-2009 5:20 PM by Christopher Bennage
Filed under: ,

[Advertisement]

About The CodeBetter.Com Blog Network
CodeBetter.Com FAQ

Our Mission

Advertisers should contact Brendan

Subscribe
Google Reader or Homepage

del.icio.us CodeBetter.com Latest Items
Add to My Yahoo!
Subscribe with Bloglines
Subscribe in NewsGator Online
Subscribe with myFeedster
Add to My AOL
Furl CodeBetter.com Latest Items
Subscribe in Rojo

Member Projects
DimeCasts.Net - Derik Whittaker

Friends of Devlicio.us
Red-Gate Tools For SQL and .NET

NDepend

SlickEdit
 
SmartInspect .NET Logging
NGEDIT: ViEmu and Codekana
LiteAccounting.Com
DevExpress
Fixx
NHibernate Profiler
Unfuddle
Balsamiq Mockups
Scrumy
JetBrains - ReSharper
Umbraco
NServiceBus
RavenDb
Web Sequence Diagrams
Ducksboard<-- NEW Friend!

 



Site Copyright © 2007 CodeBetter.Com
Content Copyright Individual Bloggers

 

Community Server (Commercial Edition)