I'm in the process of creating the world's most complex “Hello World” Silverlight application. Hopefully,for once,I'll see it through to the end and also post the source and an insightful blog entry to help everybody who's struggling to do the same. Anyone who follows my blog kNows that I don't always follow through on a series to the end. But,“I can dream can't I?”.
Anyhow,I've taken the recent release of Prism v2,which introduced Silverlight support,to dig in and get to kNow both Prism and Silverlight. As background,I have one production WFP application under my belt. We didn't use Prism,although I looked at it. Instead,I had modeled it after Jonas Follesoe's Presentation Model implementation which he had demoed at TechEd 2008. For the project,Prism seemed a little like overkill. But for my upcoming “Hello World” application it's perfect :P.
My goals for this demo application are that it would be fully testable and modular. It would incorporate WCF with Silverlight 2 and the tests Could be run without the Silverlight Test Framework. About the last requirement,I have nothing against the SL Test Framework. In fact I see it has great potential,but I think as a rule if my code is really decoupled then it shouldn't require the framework for testing. That said I really think it Could be useful for functional testing and for compatibility testing between browsers.
UPDATE 3/12/09: While looking for a way to avoid using partial classes and explicit interface implementations I ran into a post about avoiding generated proxies I thought it seemed cleaner. The source code is a bit of a work in progress,so it doesn't have the solution described here in this post. It has the one which doesn't have proxies. But it does finally include an example of testing without using the Silverlight Test Framework which I'll blog about soon.
UPDATE 5/10/09: I've since finished working through this and have an updated post along with source code. This post documents where I started out and when read together with my more recent post shows the progression of my understanding of recommended practices.
UPDATE 3/12/2010: For a more recent,updated post (yes,I revised it again) see MVVM with Prism 101 – Part 6b: Wrapping IClientChannel.
Problem
One thing that has dogged me in this exercise has been the WCF client proxy generated when I add a service reference to my Silverlight project. I ran into a couple things that either didn't work,or just didn't smell right (eg. didn't quite meet my goals listed above).
I like that the service generates a proxy class for me,I also like that it creates an interface for me as well. However,the first problem I ran into was trying to use the interface. Here's what the generator creates:
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel","3.0.0.0")]
[System.ServiceModel.ServiceContractAttribute(Namespace="http://helloworld.org/messaging",ConfigurationName="Web.Services.HelloWorldMessageService")]
public interface HelloWorldMessageService {
[System.ServiceModel.OperationContractAttribute(AsyncPattern=true,Action="http://helloworld.org/messaging/HelloWorldMessageService/UpdateMessage",ReplyAction="http://helloworld.org/messaging/HelloWorldMessageService/UpdateMessageResponse")]
System.IAsyncResult BeginUpdateMessage(string message,System.AsyncCallback callback,255)">object asyncState);
void EndUpdateMessage(System.IAsyncResult result);
[System.ServiceModel.OperationContractAttribute(AsyncPattern="http://helloworld.org/messaging/HelloWorldMessageService/GetMessage",128)">"http://helloworld.org/messaging/HelloWorldMessageService/GetMessageResponse")]
System.IAsyncResult BeginGetMessage(System.AsyncCallback callback,255)">object asyncState);
HelloWorld.Model.Message EndGetMessage(System.IAsyncResult result);
}
And for reference here's my service deFinition:
class Message
{
public DateTime Date { get; set; }
public String Value { get; set; }
}
[ServiceContract(Namespace = "http://helloworld.org/messaging")]
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
class HelloWorldMessageService
{
private static Message _message = new Message { Value = "Hello from WCF",Date = DateTime.Now };
[OperationContract]
void UpdateMessage(string message)
{
_message.Value = message;
_message.Date = DateTime.Now;
}
[OperationContract]
public Message GetMessage()
{
return _message;
}
}
My first WPF project did not require asynchronous handling – you may scoff,but it only operated on small local files,converting them from one format to another and so asynchronous communication would have been better but would not have made any difference to the end user.
When I first attempted to work with asynchronous communication (first in a xaml-type application) I was a little flustered. All the examples I Could find (in books or online) used the XXXAsync() methods and Completed events generated in the proxy class. But my problem was that these where not part of the interface generated for the class,so I Couldn't use them.
The interface includes methods which are standard to the asynchronous programming model. I've used them before for network communication,file system operations and other common operations which often block the current thread. So here's what my first attempt looked like:
public HelloWorldPresenter(IHelloWorldView view,IMessageServiceClient service)
{
// store dependencies
_service = service;
// load data asyncronously
IAsyncResult result = _service.BeginGetMessage(new AsyncCallback(BeginGetMessageComplete),255)">null);
// do some other stuff
}
void BeginGetMessageComplete(IAsyncResult result)
{
// get result
Message output = _service.EndGetMessage(result);
this.Message = output;
}
But when I ran it I got the following error:
UnauthorizedAccessException: Invalid cross-thread access.
Which translates to,“you can't update the UI thread from a background thread,dummy”.
Solution
After digging (and missing the solution a couple of times),I found that I had to use the System.Threading.dispatcher class to correct the issue. The reason I had missed it was because I didn't understand dispatcher (I did say I only had one WPF app under my belt and it didn't use async methods). I remember reading during my search suggestion to store a reference to dispatcher in the container so I Could resolve it,but it didn't seem right. But once I read the documentation I realized that this was OK.
As it turns out there is only one dispatcher in your application (you can create more,but everything I've read says you should have a really good reason for it). The dispatcher is attached to the CurrentThread and so that's why it's a good way to get back to the UI thread. So when your application starts,you register a reference to dispatcher with your container. There's a couple ways to access it,but I'm grabbing it from the view I'm registering as my RootVisual object.
{
protected override IModuleCatalog GetModuleCatalog()
{
// setup module catalog
}
override DependencyObject CreateShell()
{
// calling Resolve instead of directly initing allows use of dependency injection
Shell shell = Container.Resolve<Shell>();
// set shell as RootVisual
Application.Current.RootVisual = shell;
// store reference to shell.dispatcher so background threads can update UI
Container.RegisterInstance<dispatcher>(shell.dispatcher);
return shell;
}
}
Now my presenter looks like this:
_service = service;
_container = container;
// initialize locals
UpdateMessage = new DelegateCommand<string>(this.OnUpdateMessageExecute,255)">this.OnUpdateMessageCanExecute);
// load data asyncronously
IAsyncResult result = _service.BeginGetMessage(null);
// set reference to view (needed for RegionManager)
View = view;
View.Model = this;
}
void BeginGetMessageComplete(IAsyncResult result)
{
// need dispatcher to execute on UI thread
dispatcher dispatcher = _container.Resolve<dispatcher>();
// update Message on UI thread
dispatcher.BeginInvoke(() => this.Message = output);
}