Creating a Blog Application Using Silverlight and WCF



 

figure1.png

                                                                                      Figure 1 - The Silverlight Blog Application



Introduction

A while back I wrote an application that described how to create a Blog application in ASP.NET.  It was a fun exercise, but it doesn't compare to what kind of application you can write in Silverlight.  Although you can create some pretty good visual effects in ASP.NET using JQuery and ExtJS,  it just doesn't compare visually to a Silverlight application, not to mention how simple it is to throw the app together.  The hardest part of writing this application for me was figuring out how the heck to hook up WCF to Silverlight.     
 
The Design

There are two projects included in the design of the Silverlight Blog.  The first project is a WCF Service for saving and retrieving the blog data. This is the model side of our MVVM architecture (Model View ViewModel).  The blog data is saved in an XML file for simplicity.  All BlogItems are retrieved and saved through the IBlogStoreService.  Because the calls are asynchronous in nature, there are two pairs of calls on the service.  BeginAddBlogItem, EndAddBlogItem, BeginGetAllBlogData, EndGetAllBlogData.  (When it comes to calling anything outside Silverlight whether its a WCF service, a Rest Service or an API, you can bet that the call you make in Silverlight needs to be asynchronous, so you will always work in method pairs.)  We'll see how we use these asynchronous calls in Silverlight in a little bit.

figure2.png

Figure 2 - The WCF Server Design Reverse Engineered with WithClass 2000


On the client side, we have one page, our Main.xaml page where our blog application resides. This is the View part of our MVVM architecture (Model View ViewModel). The View separates out all presentation concerns from the rest of our architecture. These concerns include color, layout, animation, fonts, etc.  We also have a ViewModel as part of our MVVM architecture (Model View ViewModel) that binds to the xaml view. The ViewModel consists of a BlogManager and a BlogItem class.  The BlogManager contains an ObservableCollection of all the BlogItems.  The BlogManager and its items are bound to an ItemsControl that displays each blog item.  Also in the client Design is an ICommand implementation for adding a blog item called AddBlogCommand.  The ICommand is bound to the click of the blogging button.
Finally we have the BlogModel class to wrap the functionality  to the Model which is basically the WCF Service.

figure3.png

Figure 3 - The Silverlight Client Design (Reverse Engineered with WithClass 2000

The XAML

The XAML for the blogging app is shown in Listing 1.  Here you can see the bindings for the ViewModel.  For example the input fields for the blog are bound to the BlogInput inside the BlogManager.  The ItemSource for the Blog's ItemsControl is Items inside the BlogManager and each BlogItem in Items binds to the BlogEntryTemplate DataTemplate.

Listing 1 - The XAML Presentation for the Blog

    <Grid x:Name="LayoutRoot" Background="Lavender">
       
<Grid.RowDefinitions>
       
     
<RowDefinition Height="Auto" />
       
     
<RowDefinition Height="*" />
       
</Grid.RowDefinitions>
       
<ScrollViewer Height="500" >
       
     
<ItemsControl ItemsSource="{Binding Items}" ItemTemplate="{StaticResource BlogEntryTemplate}" HorizontalAlignment="Center"   >
               
<ItemsControl.ItemsPanel>
       
                  
<ItemsPanelTemplate>
       
                         
<StackPanel Orientation="Vertical" />
       
                  
</ItemsPanelTemplate>
               
</ItemsControl.ItemsPanel>
       
     
</ItemsControl>
       
</ScrollViewer>
       
<Grid Grid.Row="1" x:Name="BlogInput" >
       
     
<Grid.Background>
       
            
<LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
        
                  
<GradientStop Color="#FFDBE0EF" Offset="0"/>
       
                  
<GradientStop Color="#FF3E58AB" Offset="1"/>
       
            
</LinearGradientBrush>
       
     
</Grid.Background>
           
<Grid.RowDefinitions>
           
 
<RowDefinition Height="68*" />
           
 
<RowDefinition Height="150*" />
           
 
<RowDefinition Height="109*" />
           
</Grid.RowDefinitions>
           
<Grid.ColumnDefinitions>
           
 
<ColumnDefinition Width="0.228*" />
           
 
<ColumnDefinition Width="0.494*" />
           
 
<ColumnDefinition Width="0.279*"/>
           
</Grid.ColumnDefinitions>
           
<TextBlock Grid.Row="0" Text="Title"  d:LayoutOverrides="Height" HorizontalAlignment="Right" VerticalAlignment="Center"
                FontSize
="14.667" FontWeight="Bold"  Margin="0,0,10,0"  />

           
<TextBlock Grid.Row="1" Text="Blog Entry" HorizontalAlignment="Right"   VerticalAlignment="Center" FontWeight="Bold" FontSize="14.667" Margin="0,0,10,0"  />
     
     
<TextBox Grid.Column="1" x:Name="TitleTextBox" Height="30"  HorizontalAlignment="Stretch" Text="{Binding BlogInput.Title, Mode=TwoWay}" Margin="0,0,10,0"   />
     
     
<TextBox x:Name="BlogEntryTextBox" HorizontalAlignment="Stretch"  Grid.Column="1" Grid.Row="1" AcceptsReturn="True"
                       TextWrapping
="Wrap"  Text="{Binding BlogInput.Entry, Mode=TwoWay}"/>

       
     
<Button x:Name="button" Grid.Row="2" Content="Blog It" Height="40" Width="100"
                 Command
="{Binding BlogCommand}" CommandParameter="{Binding BlogInput}" Grid.Column="1"
                 HorizontalAlignment
="Right" Loaded="button_Loaded" Click="OnClick"  />
       
</Grid>
   
</Grid>

The DataTemplate (Listing 2), binds to an BlogItem in the Items collection much like the BlogInput binds to the input section of the blog.  This makes sense since the input is carrying the same information as the blog entry.

Listing 2 - Blog Item Data Template

DataTemplate x:Key="BlogEntryTemplate">
   
         
<Grid d:DesignWidth="343" d:DesignHeight="103" RenderTransformOrigin="0.816,0.553" Width="648" Background="#FF3E58AB">
   
                
<Grid.RowDefinitions>
   
                      
<RowDefinition/>
   
                      
<RowDefinition/>
   
                      
<RowDefinition/>
   
                
</Grid.RowDefinitions>
   
                
<Grid.ColumnDefinitions>
   
                      
<ColumnDefinition />
   
                
</Grid.ColumnDefinitions>
   
                
<Grid Grid.ColumnSpan="2" Margin="20,0,0,0" VerticalAlignment="Top" d:LayoutOverrides="Width">
   
                      
<Grid.ColumnDefinitions>
   
                             
<ColumnDefinition Width="448" />
   
                             
<ColumnDefinition MinWidth="180" />
   
                      
</Grid.ColumnDefinitions>
   
                      
<TextBlock  x:Name="Title" Text="{Binding Title}" Foreground="#FFF3EFEF" HorizontalAlignment="Left"  
                                
VerticalAlignment
="Top"  FontWeight="Bold" FontSize="21.333" Margin="20" TextWrapping="Wrap">

   
                             
<TextBlock.Effect>
   
                                    
<DropShadowEffect/>
   
                             
</TextBlock.Effect>
   
                      
</TextBlock>
   
                      
<TextBlock Grid.Column="1" x:Name="Date" Text="{Binding Date}" Foreground="#FFF7F1F1"  
                              
Height
="40" VerticalAlignment="Top"  HorizontalAlignment="Left" d:LayoutOverrides="HorizontalAlignment" Margin="5" />

   
                
</Grid>
   
                
<TextBlock Foreground="White" Grid.Row="1" x:Name="BlogEntry" FontSize="12" Text="{Binding Entry}"
                                TextWrapping
="Wrap" Grid.ColumnSpan="2" Margin="20,8,8,10"  />
   
                
<Rectangle Height="2" HorizontalAlignment="Stretch" Fill="Red" RadiusX=".5" RadiusY=".5" Grid.Row="2" Grid.ColumnSpan="2" />
   
         
</Grid>
   
  
</DataTemplate>

  

In the blog entries of the Item Control, we use a log of grids to layout everything nicely.   There is nothing like grid layout for when you want precise control over how items behave on the screen.  Also notice that we had some fun with the BlogItem Title by adding a drop shadow effect around the text title.

Also we decided to have some additional fun by causing the text on the button to grow and shrink when you press it.  Animation is so easy in Silverlight, especially once you get the hang of the Blend Tool.  The Blend tool allows you animate without using XAML, but that is outside  the scope of this article.  The nice thing is that once we design the animation, we just point it at the target and we're done.  Listing 2a shows the XAML in the resource section of our Blog Control for animating the font.  The StoryBoard describes the full animation.  We are using Double Animation to alter the size of the font every 1/10 of a second.  AutoReverse set to true tells our animation to go forward through all the key frames and then go backward through the key frames.  This gives a nice growing and shrinking effect of our button text.

Lising 2a - XAML for animating the text inside our button

        <Storyboard x:Name="ButtonAnimate">

            <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(Control.FontSize)" Storyboard.TargetName="button" AutoReverse="True">

                <EasingDoubleKeyFrame KeyTime="0" Value="11.5"/>

                <EasingDoubleKeyFrame KeyTime="0:0:0.1" Value="12"/>

                <EasingDoubleKeyFrame KeyTime="0:0:0.2" Value="12.5"/>

                <EasingDoubleKeyFrame KeyTime="0:0:0.3" Value="13.0"/>

                <EasingDoubleKeyFrame KeyTime="0:0:0.4" Value="13.5"/>

                <EasingDoubleKeyFrame KeyTime="0:0:0.5" Value="14"/>

                <EasingDoubleKeyFrame KeyTime="0:0:0.6" Value="14.5"/>

                <EasingDoubleKeyFrame KeyTime="0:0:0.7" Value="15.0"/>

                <EasingDoubleKeyFrame KeyTime="0:0:0.8" Value="15.5"/>

                <EasingDoubleKeyFrame KeyTime="0:0:0.9" Value="16.0"/>

                <EasingDoubleKeyFrame KeyTime="0:0:0.10" Value="16.5"/>

            </DoubleAnimationUsingKeyFrames>

        </Storyboard>

 

In order to kick of the animation, we do this in Code Behind by simply calling Begin on the ButtonAnimate control after the button is clicked

Listing 2b - Animating the Button in the Click Event Handler

        private void OnClick(object sender, RoutedEventArgs e)

        {

            ButtonAnimate.Begin();

        }


The Code

These days, when I'm coding Silverlight, I'm thinking all about MVVM.  I say it like a Mantra M-V-V-M, M-V-V-M.   It really makes the difference between a well-designed Silverlight application and what amounts to unmaintainable UI code.  At the heart of MVVM is the ViewModel.  Our ViewModel starts with a class called BlogManager shown in listing 3.  Inside the BlogManager you'll see the collection of Items holding each of our blogs, the BlogInput for entering a blog, and a BlogModel for accessing the Data.  There is also a BlogCommand that exercising sending the blog to the server. 

Listing 3 - The BlogManager

   

using System.Collections.ObjectModel;

using WebBlogInSilverlight.Commands;

using WebBlogInSilverlight.Model;

namespace WebBlogInSilverlight.ViewModel

{

    public class BlogManager

    {

        public ObservableCollection<BlogItem> Items { get; set; }

        public BlogItem BlogInput { get; set; }

        protected BlogModel BlogModel { get; set; }

        private AddBlogCommand _saveCommand;

        public AddBlogCommand BlogCommand { get { return _saveCommand ?? (_saveCommand = new AddBlogCommand(BlogModel)); }

        }

        public BlogManager()

        {

            Items = new ObservableCollection<BlogItem>();

            BlogInput = new BlogItem();

            BlogModel = new BlogModel(this);

            BlogModel.RetrieveAllBlogItems();

        }

    }

}

The BlogCommand calls the BlogModel inside the Execute method.  The Execute command takes the bound BlogItem and sends it to the BlogModel to be processed by the server as shown in listing 4.

Listing 4  - Execute Command inside the implemented ICommand

 

        public void Execute(object parameter)

        {

            _item = parameter as BlogItem;

            _item.Date = DateTime.Now;

            BlogModel.AddBlogItemToServer(_item);

        }

 

The BlogModel wraps all the functionality that we need to get at the server.  Generally, I like  to wrap the server calls to make them as manageable as possible rather than go directly through the service interface.  Inside the BlogModel we can also do things that are not part of the service such as serialize the BlogItem into XML.  We also handle the asynchronous nature of calling our WCF Service from Silverlight such as retrieving data from the callback.  After a user executes the command by pressing the button, the AddBlogItemToServer is called from the ICommand as we saw in listing 4.  Inside the AddBlogItemToServer, the item is serialized into an XML string (via the BlogSerializer) and passed to the WCF Service via the BlogClient. The BlogClient was created by adding a service reference to our Silverlight Project.  Our service uses asynchronous calls as we shall soon see.  The AddBlogItemAsync call takes the BlogItem XML fragment and adds it to the collection of  XML blog fragments in an XML file on the server.  After the service returns, we want to repost the entire blog collection, so we return the entire blog inside the callback coming from the server.  WCF gives us a convenient wy of retrieving the data in the asynchronous callback by allowing us to subscribe to an AddBlogItemCompleted event.  Inside of our AddBlogItemCompleted event handler, we parse the XML return through our serializer and turn the XML into a collection of BlogItems which we can bind directly to the Items in our BlogManager.

Listing 5 - The BlogModel

 

using System;

using System.Xml.Linq;

using WebBlogInSilverlight.BlogStoreServiceClient;

using WebBlogInSilverlight.ViewModel;

namespace WebBlogInSilverlight.Model

{

    public class BlogModel

    {

        public BlogManager Manager { get; set; }

        protected BlogStoreServiceClient.BlogStoreServiceClient BlogClient { get; set; }

        protected BlogSerializer BlogSerializer { get; set; }

        public BlogModel(BlogManager manager)

        {

            Manager = manager;

            BlogSerializer = new BlogSerializer();

            BlogClient = new BlogStoreServiceClient.BlogStoreServiceClient();

        }

        public void AddBlogItemToServer(BlogItem item)

        {

            var rawValue = BlogSerializer.SerializeObj(item);

            BlogClient.AddBlogItemCompleted += AddBlogCommandAddBlogItemCompleted;

            BlogClient.AddBlogItemAsync(rawValue);

        }

        public void RetrieveAllBlogItems()

        {

            BlogClient.GetAllBlogDataCompleted += OnAllBlogDataCompleted;

            BlogClient.GetAllBlogDataAsync();

        }

        private void OnAllBlogDataCompleted(object sender, GetAllBlogDataCompletedEventArgs e)

        {

            var rawData = e.Result;

            if (!String.IsNullOrEmpty(rawData.Trim()))

            {

                ParseBlogItemsIntoView(rawData);

            }

        }

        void AddBlogCommandAddBlogItemCompleted(object sender, System.ComponentModel.AsyncCompletedEventArgs e)

        {

            var rawData = ((AddBlogItemCompletedEventArgs)e).Result;

            ParseBlogItemsIntoView(rawData);

        }

        private void ParseBlogItemsIntoView(string rawData)

        {

            var serializer = new BlogSerializer();

            XDocument doc = XDocument.Parse(rawData);

            // get first blog node

            var items = doc.Descendants("BlogItem");

            Manager.Items.Clear();

            foreach (var xElement in items)

            {

                var item = serializer.DeserializeObj(xElement.ToString());

                Manager.Items.Add(item);

            }

        }

    }

}

 


The Service

The service consists of a simple interface shown in listing 6.  Each method on the interface has a special attribute to tell the client to treat this as an asynchronous service instead of a synchronous service.  This is a necessary evil for Silverlight, because when you are programming in Silverlight, no matter what server or service you are talking to, you are talking to it asynchronously, because Silverlight lives in the browser.  In asynchronous programming, you call a method that you don't expect to get any results back from.  You pass the service a callback, and when the service is good and ready, it will call a method back to the client.  WCF seems to hide some of the details of what is happening here, but its still basically doing what we just described.  In WCF, asynchronicity has a pair of methods for each call.  For our AddBlogItem, we have BeginAddBlogItem and EndAddBlogItem.  BeginAddBlogItem does the processing.  Although it appears that BeginAddBlogItem is returning a result, we don't actually get the result back until EndGetAllBlogData is called internally by some magic happening under the hood.  In fact our client never needs to call EndGetAllBlogData, because somehow, through some internal mechanism, the data comes back from our BeginAddBlogItemCompleted event.
 
Listing 6 - Blog Storage Service

 public interface IBlogStoreService

    {

        [OperationContract(AsyncPattern = true)]

        IAsyncResult BeginAddBlogItem(string xmlFragment, AsyncCallback callback, object state);

        string EndAddBlogItem(IAsyncResult result);

        [OperationContract(AsyncPattern = true)]

        IAsyncResult BeginGetAllBlogData(AsyncCallback callback, object state);

        string EndGetAllBlogData(IAsyncResult result);

        [OperationContract]

        CompositeType GetDataUsingDataContract(CompositeType composite);

        // TODO: Add your service operations here

    }


The implementation of the BlogStore Service is shown in listing 7.  It takes advantage of Linq To XML to process the document.  The file is read in from the server as a string and the string is parsed into an XML XDocument.  We also parse the passed in BlogItem that was serialized into an XML string into an XElement.  Now all we need to do is add the XElement to the first item in the XDocument.  the AddFirst method in XLinq gives us this capability.  When we are done, we create an overriden IAsyncResult called BlogAsyncResult, and return the total blog string in our payload.
 

Listing 7  - The Service calls to add an item to the blog file

        public IAsyncResult BeginAddBlogItem(string xmlFragment, AsyncCallback callback, object state)

        {

            try

            {

                string blog = GetBlogData();

                if (String.IsNullOrEmpty(blog))

                {

                    blog = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?><BlogDocument></BlogDocument>";

                }

                XDocument doc = XDocument.Parse(blog);

                // strip the header

                xmlFragment = xmlFragment.Substring(xmlFragment.IndexOf('>') + 1);

                var blogItem = XElement.Parse(xmlFragment);

                var elementParent = doc.Descendants("BlogDocument").FirstOrDefault();

                elementParent.AddFirst(blogItem);

                // write it back out

                var dir = AppDomain.CurrentDomain.BaseDirectory;

                var fswrite = new FileStream(FormBlogPath("blog.xml"), FileMode.Open, FileAccess.Write);

                StreamWriter sw = new StreamWriter(fswrite);

                sw.Write(doc.ToString(SaveOptions.None));

                sw.Close();

                IAsyncResult result = new BlogAsyncResult(null, doc.ToString());

                return result;

            }

            catch (Exception ex)

            {

                System.Diagnostics.Debug.WriteLine(ex.ToString());

                return null;

            }

        }

        public string EndAddBlogItem(IAsyncResult result)

        {

            return (result as IBlogAsyncResult<string>).Payload;

        }


Conclusion
For a UI programmer, Silverlight is like being a kid in a candy store.  There is so many rich features you can take advantage of in the UI design, that you can really crank out some visually appealing applications quickly.  One thing we didn't illustrate is how easy it is to also incorporate multimedia such as pictures, movies and even sound into our Silverlight blog.  I guess we'll save his for another article.  Anyway, don't get blogged down in the details, and enjoy the blogosphere using Silverlight, C# and .NET.


Similar Articles