Dear visitor, thanks for stopping by! If you want, you can follow all updates on Snowball.be via RSS. You can also follow me on Twitter or Facebook. More interesting posts from other Microsoft Regional Directors can be found at The Region.
Gill Cleeren     .net | Silverlight | Silverlight Advent Calendar | sl4     December 23, 2009    

Yes, I made it :) Welcome to this last post in my Silverlight Advent Calendar! From December 1st until today, December 24th, I have created each day, a Silverlight article. When I first thought about it, I feared there would be at least one day I would not be able to create my post. Well, this didn’t happen, and this means that after 24 articles, we have finally arrived at Christmas Eve. I really hope you enjoyed these posts and I’ll sure be doing something similar in the future!

On a “real” advent calendar, on December 24th, the final day of the calendar, you get a special surprise: a bigger image, a bigger gift or a larger chunk of chocolate than on other days. Well, I thought this needed to be the case in my advent calendar as well, so I decided to end my series with a special post. It gives a preview of something that can in much more detail be found in my upcoming book AND it brings some more Christmas spirit on this day: a Flickr Christmas image browser. All free as my gift to you!

Below is the interface of the application.

image

We’re in luck when working with Flickr: it exposes a cross-domain file so we can access their API services from a client Silverlight application, even without running with elevated permissions! Therefore, we do not need to create a service layer that sits in between our application and Flickr’s services.

When clicking on the Get Pictures button, we load thumbnails into a templated StackPanel. When clicking on any of these thumbnails, the large image is loaded. To store the information needed for a thumbnail, I created a class called SmallFlickrImage.

public class SmallFlickrImage
{
  public string ImageId { get; set; }
  public string FarmId { get; set; }
  public string ServerId { get; set; }
  public string Secret { get; set; }
 
  public string ImageUrl
  {
    get
    {
      return string.Format(
        "http://farm{0}.static.flickr.com/{1}/{2}_{3}_m.jpg", 
        FarmId, 
        ServerId, 
        ImageId, 
        Secret
        );
    }
  }
}

In the click event, we perform a service call to Flickr. To call Flickr’s API, you’ll need an API key which can be obtained for free at http://www.flickr.com/services/api/. Once obtained, you need to embed this key in each request your application will perform with Flickr. Also on this site, you’ll find a complete overview of all available methods. Here, I’m using the flickr.photos.search method, which accepts a search string and returns me an XML response, containing all the information I need to display images in my client side application.

Working with Flickr is a typical example of a REST service. When working with these services from Silverlight, we actually need to do 3 steps:

  • Create a URL
  • Send a request to this URL
  • Accept the response, mostly in XML, sometimes in JSON

The URL is constructed as follows (as directed by Flickr):

string searchUrl = 
  "http://api.flickr.com/services/rest/?method=flickr.photos.search&api_key={0}
   &text=christmas tree snow";

To send a request to this URL, we can use either the WebClient, or the HttpWebRequest. WebClient is sufficient (there are cases where this is not the case, I spent many pages to this topic in my book ;) ). Working in Silverlight with WebClient is identical to working with this class in the full .NET framework. One thing though: just like any interaction with a service, the service call is to be done asynchronously. The code below sends a request to Flickr:

private void GetPicturesButton_Click(object sender, RoutedEventArgs e)
{
    WebClient client = new WebClient();
    client.DownloadStringCompleted += 
      new DownloadStringCompletedEventHandler(client_DownloadStringCompleted);
    client.DownloadStringAsync(new Uri(string.Format(searchUrl, api_key)));
}

In the callback, which is invoked automatically when the service returns, using some very simple LINQ-To-XML, we can parse the results. The returned XML is always of the same format and can be seen as the contract between Flickr and my application. Because we cannot set the Source property of an Image control directly, we need to go around using a BitmapImage.

protected void client_DownloadStringCompleted(object sender, 
  DownloadStringCompletedEventArgs e)
{
  XDocument xml = XDocument.Parse(e.Result);
 
  var photos = from results in xml.Descendants("photo")
               select new SmallFlickrImage
               {
                 ImageId = results.Attribute("id").Value.ToString(),
                 FarmId = results.Attribute("farm").Value.ToString(),
                 ServerId = results.Attribute("server").Value.ToString(),
                 Secret = results.Attribute("secret").Value.ToString()
               };
 
  foreach (var photo in photos)
  {
    Image image = new Image();
 
    BitmapImage bitmapImage = new BitmapImage(new Uri(photo.ImageUrl, UriKind.Absolute));
    image.Source = bitmapImage;
    image.Width = 160;
    image.Height = 160;
    image.Stretch = Stretch.Uniform;
    image.Tag = photo;
    image.Margin = new Thickness(3);
    image.HorizontalAlignment = HorizontalAlignment.Center;
    image.MouseLeftButtonDown += new MouseButtonEventHandler(img_MouseLeftButtonDown);
    PicListBox.Items.Add(image);
  }
}

At this point, the ListBox is filled. This ListBox is templated; the code for this can be seen in the sample code download.

When clicking (we attached a MouseLeftButtonDown event on each dynamically added image) on an image, we’ll load the "full” image into an Image control. This requires some code, mostly concerning which type of URL is used by Flickr for the image.

void img_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
  Image img = sender as Image;
  SmallFlickrImage smallFlickrImage = img.Tag as SmallFlickrImage;
  if (smallFlickrImage != null)
  {
    WebClient detailClient = new WebClient();
    detailClient.DownloadStringCompleted += 
      new DownloadStringCompletedEventHandler(detailClient_DownloadStringCompleted);
    detailClient.DownloadStringAsync(
      new Uri(string.Format(detailUrl, api_key, smallFlickrImage.ImageId)));
  }
}
 
void detailClient_DownloadStringCompleted(object sender, 
  DownloadStringCompletedEventArgs e)
{
  XDocument xmlDocument = XDocument.Parse(e.Result);
  XElement photoElement;
  Uri detailImageUrl;
 
  if (xmlDocument.Descendants("photo").Count<XElement>() > 0)
  {
    photoElement = xmlDocument.Descendants("photo").First<XElement>();
 
    string serverId = photoElement.Attribute("server").Value;
    string farmId = photoElement.Attribute("farm").Value;
    string imageId = photoElement.Attribute("id").Value;
    string secret;
    
    if (photoElement.Attribute("originalsecret") != null)
    {
      secret = photoElement.Attribute("originalsecret").Value;
      detailImageUrl = new Uri(string.Format
        ("http://farm{0}.static.flickr.com/{1}/{2}_{3}_o.jpg", 
          farmId, 
          serverId, 
          imageId, 
          secret)
         );
    }
    else
    {
      secret = photoElement.Attribute("secret").Value;
      detailImageUrl = new Uri(string.Format
        ("http://farm{0}.static.flickr.com/{1}/{2}_{3}.jpg", 
          farmId, 
          serverId, 
          imageId, 
          secret)
         );
    }
 
    BitmapImage bitmapImage = new BitmapImage(detailImageUrl);
    DetailImage.Source = bitmapImage;
  }
}

 

With that, the sample is working!

The complete code sample can be downloaded here: SLChristmasFlickr.zip (31.34 KB)

MERRY CHRISTMAS!

  Posted on: Thursday, December 24, 2009 12:50:15 AM (Romance Standard Time, UTC+01:00)   |   Comments [0]
         
Gill Cleeren     .net | Silverlight | Silverlight Advent Calendar | sl4     December 20, 2009    

In the before-last post of my Silverlight Advent Calendar, I would like to spend some time with a Silverlight 3 feature that’s most helpful in real world projects: merged resource dictionaries. By most helpful, I mean that in real applications, resource files can become really really big. Files of 1000 lines of pure mark-up are no exception and are really hard to find your way in.

Silverlight 3 added support for merged dictionaries. Basically, using these, we can split resources over several files, dictionaries. Using them though is nothing different than using resources located in App.xaml or Resources of the current page/control.

Let’s take a look at using these merged resource dictionaries. I created some styles to style the ChristmasSong application we have been using a few times already.

 

image

In the beginning of this series, we already looked at implicit styles. For this application, I’m defining a few of these, meaning that this very project will only work for Silverlight 4. Of course, the merged dictionary function works also in Silverlight 3.

To define a resource dictionary, in the project, we need to add a XAML file (either via creating an empty text file and renaming the extension or creating a new user control and deleting the *.cs file). In this file, we can create a ResourceDictionary element. Below is the XAML code for the implicit styles, which I grouped into a file called DefaultStyles.xaml. I created a default style for a Button, TextBlock and TextBox.

<ResourceDictionary 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <Style TargetType="Button">
        <Setter Property="Foreground" Value="Black" />
        <Setter Property="Background" Value="Red" />
        <Setter Property="FontWeight" Value="Bold" />
        <Setter Property="FontSize" Value="15" />
        <Setter Property="HorizontalAlignment" Value="Center"></Setter>
        <Setter Property="VerticalAlignment" Value="Center"></Setter>
    </Style>
 
    <Style TargetType="TextBlock">
        <Setter Property="Foreground" Value="Green" />
        <Setter Property="FontWeight" Value="Bold" />
        <Setter Property="FontSize" Value="15" />
        <Setter Property="HorizontalAlignment" Value="Left"></Setter>
        <Setter Property="VerticalAlignment" Value="Center"></Setter>
        <Setter Property="Margin" Value="2"></Setter>
    </Style>
 
    <Style TargetType="TextBox">
        <Setter Property="Width" Value="150" />
        <Setter Property="FontSize" Value="15" />
        <Setter Property="HorizontalAlignment" Value="Center"></Setter>
        <Setter Property="VerticalAlignment" Value="Center"></Setter>
        <Setter Property="Margin" Value="2"></Setter>
    </Style>
</ResourceDictionary>

Next, I created a second ResourceDictionary which has just one Style, designed for the title, in a file called SpecificStyles.

<ResourceDictionary 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <Style TargetType="TextBlock" x:Key="TitleStyle">
        <Setter Property="Foreground" Value="Red" />
        <Setter Property="FontStyle" Value="Italic"></Setter>
        <Setter Property="FontWeight" Value="Bold" />
        <Setter Property="FontSize" Value="28" />
        <Setter Property="HorizontalAlignment" Value="Center"></Setter>
        <Setter Property="VerticalAlignment" Value="Center"></Setter>
    </Style>
</ResourceDictionary>

Now we need to let our application know about these resources. To do so, I have in the App.xaml added the following code. In the MergedDictionaries element, we link the seperate XAML files.

<Application 
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
  x:Class="SLMergedDictionaries.App"
  >
  <Application.Resources>
    <ResourceDictionary>
      <ResourceDictionary.MergedDictionaries>
        <ResourceDictionary 
          Source="/SLMergedDictionaries;component/DefaultStyles.xaml">
        </ResourceDictionary>
        <ResourceDictionary 
          Source="/SLMergedDictionaries;component/SpecificStyles.xaml">
        </ResourceDictionary>
      </ResourceDictionary.MergedDictionaries>
    </ResourceDictionary>
  </Application.Resources>
</Application>

We can now use the styles declared in the dictionaries just like we would do with regular defined styles.

The complete sample can be downloaded here: SLMergedDictionaries.zip (310.39 KB)

  Posted on: Monday, December 21, 2009 12:46:11 AM (Romance Standard Time, UTC+01:00)   |   Comments [0]
         
Gill Cleeren     .net | Silverlight | Silverlight Advent Calendar | sl4     December 20, 2009    

Next to the full support of the data binding engine for the IDataErrorInfo interface, Silverlight 4 adds a new interface called INotifyDataErrorInfo, offering more options for particular scenario's. In this post, we’ll look at a small example using this interface.

IDataErrorInfo allows retrieving the error on a per-property basis. It’s not possible however to validate all properties of the entity in one go. This becomes possible with the INotifyDataErrorInfo. Let’s look at the interface first.

public interface INotifyDataErrorInfo
{
    bool HasErrors { get; }
 
    event EventHandler ErrorsChanged;
 
    IEnumerable GetErrors(
                    string propertyName);
}

This interface has some nice advantages. As said, we can check if the entity as a whole is in an invalid state through the HasErrors property. Retrieving the errors using the GetErrors can now retrieve other things than just strings which was all that we could do with the IDataErrorInfo. On top of that, a property can have more than just one validation error at the same time

The ErrorsChanged event can come in handy if there’s a long running process needed to perform validation: assume that for some validation, we need to go to the database over a service. The ErrorsChanged event allows us to notify the UI if the validation errors change. If ValidatesOnNotifyDataErrors is set to true on the UI, Silverlight will listen for the ErrorsChanged event and will display any errors if they are added afterwards.

That’s it for this interface, let’s now look at it in an example. I have changed the example created for the IDataErrorInfo to work with INotifyDataErrorInfo, so the interface has remained the same.

The errors I want to return to my interface are more than just strings. I created a custom error as follows:

public class ChristmasSongError
{
    public ErrorLevel Severity{ get; set; }
    public string ErrorName { get; set; }
    public string ErrorMessage { get; set; }
 
    public override string ToString()
    {
        return ErrorName + ": " + ErrorMessage;
    }
}

The ChristmasSong class now implements the INotifyDataErrorInfo interface. Let’s focus on the Title property. When its value is set, it calls a validation method. In this method, I create an instance of the custom error, specifying all the property values. If there’s an error, here the title being empty, I add it to a Dictionary called errors using the AddError method. If validation is satisfied, it’s removed using the RemoveError method. The errrors Dictionary is used so it is possible for each property to have more than one error: each property has as its value a List.

public class ChristmasSong : INotifyDataErrorInfo
{
    private string title;
    private string performedBy;
    private TimeSpan duration;
    private DateTime published;
 
    private string error;
 
    public Dictionary<string, List> errors = 
      new Dictionary<string,List>();
 
    public event PropertyChangedEventHandler PropertyChanged;
 
    protected void NotifyPropertyChanged(string propertyName)
    {
        if (PropertyChanged != null)
            PropertyChanged(
                    this, new PropertyChangedEventArgs(propertyName));
    }
 
    public string Title
    {
        get
        {
            return title;
        }
        set
        {
            title = 
                    value;
            ValidateTitleProperty();
        }
    }
 
    private void ValidateTitleProperty()
    {
        ChristmasSongError christmasSongError =
new ChristmasSongError() 
        { Severity = ErrorLevel.Error, ErrorName =
"TitleRequired", 
          ErrorMessage = 
                    "Title should not be empty" };
        if (string.IsNullOrEmpty(title))
        {
            AddError(
                    "Title", christmasSongError);
        }
        else
        {
            RemoveError(
                    "Title", "TitleRequired");
        }
    }
 
    public string PerformedBy
    {
        get
        {
            return performedBy;
        }
        set
        {
            performedBy = 
                    value;
            ValidatePerformedByProperty();
        }
    }
 
    private void ValidatePerformedByProperty()
    {
        ChristmasSongError christmasSongError =
new ChristmasSongError() 
        { Severity = ErrorLevel.Error, ErrorName =
"PerformedByRequired", 
          ErrorMessage = 
                    "The artist should not be empty" };
        if (string.IsNullOrEmpty(performedBy))
        {
            AddError(
                    "PerformedBy", christmasSongError);
        }
        else
        {
            RemoveError(
                    "PerformedBy", "PerformedByRequired");
        }
    }
 
    public TimeSpan Duration
    {
        get
        {
            return duration;
        }
        set
        {
            duration = 
                    value;
            ValidateDuration();
        }
    }
 
    private void ValidateDuration()
    {
        ChristmasSongError durationNullError =
new ChristmasSongError() 
        { Severity = ErrorLevel.CriticalError, ErrorName =
"DurationNull", 
          ErrorMessage = 
                    "The duration should not be empty" };
        ChristmasSongError durationTooLongError =
new ChristmasSongError() 
        { Severity = ErrorLevel.Error, ErrorName =
"DurationTooLong", 
          ErrorMessage = 
                    "The duration is too long" };
        
        if (duration == TimeSpan.Zero)
        {
            AddError(
                    "Duration", durationNullError);
        }
        else
        {
            RemoveError(
                    "Duration", "DurationNull");
        }
 
        if (duration.TotalSeconds > 500)
        {
            AddError(
                    "Duration", durationTooLongError);
        }
        else
        {
            RemoveError(
                    "Duration", "DurationTooLong");
        }
    }
 
    public DateTime Published
    {
        get
        {
            return published;
        }
        set
        {
            published = 
                    value;
            VaidatePublished();
        }
    }
 
    private void VaidatePublished()
    {
        ChristmasSongError publishedTooLow =
new ChristmasSongError() 
        { Severity = ErrorLevel.CriticalError, ErrorName =
"PublishedTooLow", 
          ErrorMessage = 
                    "The date is too small" };
        ChristmasSongError publishedTooHigh =
new ChristmasSongError() 
        { Severity = ErrorLevel.CriticalError, ErrorName =
"PublishedTooHigh", 
          ErrorMessage = 
                    "The date is too high" };
 
        if (published < new DateTime(1900, 1, 1))
        {
            AddError(
                    "Published", publishedTooLow);
        }
        else
        {
            RemoveError(
                    "Published", "PublishedTooLow");
        }
 
        if (published > new DateTime( 2010, 1, 1))
        {
            AddError(
                    "Published", publishedTooHigh);
        }
        else
        {
            RemoveError(
                    "Published", "PublishedTooHigh");
        }
    }
 
    private void AddError(string propertyName, ChristmasSongError error)
    {
        
 
        if (!errors.ContainsKey(propertyName))
        {
            errors.Add(propertyName, 
                    new List() { error });
        }
        else// adding the error to the already existing list
        {
            var list = errors[propertyName];
            list.Add(error);
        }
 
        if (ErrorsChanged != null)
            ErrorsChanged(
                    this, new DataErrorsChangedEventArgs(propertyName));
    }
 
    private void RemoveError(string propertyName, string errorName)
    {
        if (errors.ContainsKey(propertyName))
        {
            var christmasSongError = errors[propertyName]
                .Where
(e => e.ErrorName == errorName).FirstOrDefault();
            var list = errors[propertyName];
            list.Remove(christmasSongError);
 
            if (list.Count == 0)//no more errors for this property 
            {
                errors.Remove(propertyName);
            }
 
            if (ErrorsChanged != null)
                ErrorsChanged(
                    this, new DataErrorsChangedEventArgs(propertyName));
        }
    }
 
    public event EventHandler ErrorsChanged;
 
    public System.Collections.IEnumerable GetErrors(string propertyName)
    {
        if (string.IsNullOrEmpty(propertyName))//retrieve
                        errors for entire entity
        {
            return errors.Values;
        }
        else
        {
            if (errors.ContainsKey(propertyName))
                return errors[propertyName];
            return null;
        }
    }
 
    public bool HasErrors
    {
        get
        {
            if (errors.Count == 0)
                return false;
            return true;
        }
    }
}

With the entity in place, we can look at the UI. The XAML code is similar to the IDataErrorInfo example, although we should now use the ValidatesOnNotifyDataErrors and set it to True. Below is part of the code, for the entire listing, see the download.

<TextBox x:Name="TitleTextBox" Grid.Row="1" Grid.Column="1" Width="200" VerticalAlignment="Center" HorizontalAlignment="Left" Margin="2"

Text="{Binding Path=Title,Mode=TwoWay,ValidatesOnNotifyDataErrors=True,NotifyOnValidationError=True}" />

The complete sample can be downloaded here: SLINotifyDataErrorInfo.zip (602.56 KB)

  Posted on: Monday, December 21, 2009 12:42:27 AM (Romance Standard Time, UTC+01:00)   |   Comments [0]
         
Gill Cleeren     .net | Silverlight | Silverlight Advent Calendar | sl4     December 20, 2009    

Services and data access are the focus of the book I’m writing (more very very soon!). Working with services to get data into a Silverlight application is so vital, it should be code that’s known by every Silverlight developer by heart. Working with duplex bindings is somewhat more complicated. However, there are situations in which duplex communication is really necessary. Think of the typical example: a stock application that should send stock updates to the client without the client requesting for an update.

Duplex communications are possible with WCF. They “seem” initiated by the server: the service will send updates without the client requests the service to do so. This way, the client can receive updates whenever new information is available on the server (new stock info that becomes available) and act accordingly. I put seem between quotes. The reason for that is that a duplex binding is in fact still a normal request/receive process. Initially, the client has to do a request to the service and from then on, the service can send its updates to the client.

In the example I have created for this post, I created an application that posts updates about snow throughout Belgium (while writing this article, it has snowed quite a lot in Belgium, so that’s where the inspiration came from and Tienen is where I live :) ). Below is a screenshot.

image

Of course, you’re here to learn something and see some code, right? No problem! Let’s first focus on the service. For the data exchange, I created a small type called SnowUpdate.

public class SnowUpdate
{
    public string Location { get; set; }
    public double centimeter { get; set; }
}

One very important thing that we need to do, is adding a reference to an assembly available in the Silverlight SDK called System.Service.PollingDuplex.dll. By default, it’s installed in C:\Program Files (x86)\Microsoft SDKs\Silverlight\v4.0\Libraries\Server. Add a reference to this assembly. (Note the x86, that’s because I’m running 64bit).

The contract of the service makes it clear that we are working with a duplex service. There are some special things to note in this code. As you can see, two interfaces are defined here. The first one is the regular contract, the second one is what’s called the callback contract and defines the contract for the call to the client.

Also note that both OperationContracts have IsOneWay set to true. For the Connect method, we’re actually saying: a client can call Connect to make itself known to the service but it expects no response, hence the IsOneWay.

[ServiceContract(Namespace = "Silverlight", CallbackContract = typeof(ISnowServiceClient))]
public interface ISnowService
{
    [OperationContract(IsOneWay = true)]
    void Connect();
}
 
[ServiceContract]
public interface ISnowServiceClient
{
    [OperationContract(IsOneWay = true)]
    void SendSnowUpdate(SnowUpdate snowUpdate);
}

In the service implementation, I have created a Timer, which will periodically send a new message to the client. The OperationContext.Current.GetCallbackChannel gives us access to the callback channel which we can use to send messages to the client side. On a tick of the Timer, we’re sending a message.

private ISnowServiceClient client;
private Timer timer;
 
public void Connect()
{
    client = OperationContext.Current.GetCallbackChannel<ISnowServiceClient>();
    timer = new Timer(new TimerCallback(TimerTick), null, 500, 5000);
}
 
void TimerTick(object state)
{
    try
    {
        if (client != null)
        {
            client.SendSnowUpdate(GetLatestUpdate());
        }
    }
    catch (Exception)
    {
 
        client = null;
        timer.Dispose();
    }
}

With that, the service code is done, but it does require some specific configuration code to work. Take a look at the sample for this code. We can now look at the client code. The process to add a service reference is exactly the same, although you’ll notice that the ServiceReferences.clientconfig file remains empty (which it does not when referencing a normal service). Therefore, the code that we need to write is somewhat more complex: we have to create the binding and the address from code and pass this along with the proxy instantiation. Below you can see the client-side code.

private void UserControl_Loaded(object sender, RoutedEventArgs e)
{
  EndpointAddress address = new EndpointAddress("http://localhost:59485/SnowService.svc");
 
  CustomBinding binding = new CustomBinding(
    new PollingDuplexBindingElement(),
    new BinaryMessageEncodingBindingElement(),
    new HttpTransportBindingElement());
  
  client = new SLDuplexSnow.SnowService.SnowServiceClient(binding, address);
 
  client.SendSnowUpdateReceived += 
    new EventHandler<SLDuplexSnow.SnowService.SendSnowUpdateReceivedEventArgs>
     (client_SendSnowUpdateReceived);
    client.ConnectAsync();
}
 
void client_SendSnowUpdateReceived(object sender, 
  SLDuplexSnow.SnowService.SendSnowUpdateReceivedEventArgs e)
{
    SnowDetailsGrid.DataContext = e.snowUpdate;
}

Note that we are calling ConnectAsync to let the service know about the client so it can send updates to it BUT we are handing SnowUpdateReceived events.

NOTE: This sample is written for Silverlight 3/VS2008. At this point, I have issues with VS2010 when generating the client proxy with SL4/VS2010 when using a duplex service.

The complete code can be downloaded here: SLDuplex.zip (111.91 KB)

  Posted on: Monday, December 21, 2009 12:38:33 AM (Romance Standard Time, UTC+01:00)   |   Comments [4]
         
Gill Cleeren     .net | Silverlight | Silverlight Advent Calendar | sl4     December 19, 2009    

In today’s post, we’ll take a look at Silverlight’s Isolated Storage, a feature already available in Silverlight since version 2. With this post, I want to show all there is possible with this handy option.

We all know cookies, small text files that are stored on our machines by sites. Most of the time, the goal of a cookie, is storing informational data for next visits of the user to that particular site. This data can vary from login information to perhaps a background color the user favors. Cookies are limited to containing just plain text as well as in size.

Isolated storage does not differ that much from cookies. It’s basically a file store for Silverlight: Silverlight applications can store in the Isolated Storage files during a session and retrieve them in a later session. Isolated storage is a per-user per-application storage, meaning that when userA works with SilverlightAppA, he’ll have a different isolated storage from userB working with the same application. Isolated storage information is not deleted when you clear your temporary internet files, it can be deleted through the Silverlight configuration, as shown below.

image

We can use isolated storage in a number of ways. Let’s take a look at the sample for this post. Below is a screenshot of the very simple UI I created for this sample. It allows a user to enter a text, representing his preferred background color for the application. We’ll store this value using the IsolatedStorage. Secondly, using another feature called IsolatedStorageSettings, I’m allowing the user to store a second value, which is used for the color of a StackPanel ( the blue bar in the screenshot).

image

First, let’s look at the code for the background color, using the regular IsolatedStorage. Below you can see the code. Working with IsolatedStorage is done using the IsolatedStorageFile class. It defines among others, the GetUserStoreForApplication static method, which returns the data store, for the specific user and the current application. From there on, working with IsolatedStorage is similar to working with the regular file system. Isolated storage is not to be seen as one file, it’s an entire file system, that can contain directories and files. In this code, we are creating using System.IO code a file called color.txt and writing some data to this file.

private void ColorButton_Click(object sender, RoutedEventArgs e)
{
  try
  {
    IsolatedStorageFile isoStore = IsolatedStorageFile.GetUserStoreForApplication();
 
    using (IsolatedStorageFileStream fs = isoStore.CreateFile("color.txt"))
    {
      StreamWriter writer = new StreamWriter(fs);
      writer.Write(ColorTextBox.Text);
      writer.Close();
    }
    ChangeColor(ColorTextBox.Text);
  }
  catch (Exception)
  {
    //handle error here, not for demo
  }
}

On load of the application, we need to check if this file already exists and if yes, read the file and set the background to that specific color. This is shown in the code below.

private void UserControl_Loaded(object sender, RoutedEventArgs e)
{
  try
  {
    IsolatedStorageFile isoStore = IsolatedStorageFile.GetUserStoreForApplication();
 
    if (isoStore.FileExists("color.txt"))
    {
      using (IsolatedStorageFileStream fs = isoStore.OpenFile("color.txt", FileMode.Open))
      {
        StreamReader reader = new StreamReader(fs);
        ChangeColor(reader.ReadLine());
        reader.Close();
      }
    }
  }
  catch (Exception)
  {
    //handle error here, not for demo
  }
}

In ASP.NET, we have the Application and Session objects, which provide an easy and quick way to store information between requests. Through a feature of the IsolatedStorage, we can have something quite similar. The IsolatedStorageSettings allow us a key/value lookup to store data. We don’t have to write code to actually store the files, it does that for us. Take a look at the following code. We allow the user to store a second string for the background of the StackPanel.

private void AppColorButton_Click(object sender, RoutedEventArgs e)
{
  IsolatedStorageSettings.ApplicationSettings["AppColor"] = AppColorTextBox.Text;
  ChangeStackPanelColor(AppColorTextBox.Text);
}

Using this approach, we say for the AppColor value, store the contents of the TextBox as value. Behind the scenes, this is persisted, so it can be retrieved after the application has been shutdown (it is thus not stored in application memory). Retrieving the value can be done as follows:

if (IsolatedStorageSettings.ApplicationSettings.Contains("AppColor"))
{
  string appColor = IsolatedStorageSettings.ApplicationSettings["AppColor"].ToString();
  ChangeStackPanelColor(appColor);
}

The complete sample can be downloaded here: SLIsolatedStorage.zip (30.84 KB)
  Posted on: Sunday, December 20, 2009 12:26:40 AM (Romance Standard Time, UTC+01:00)   |   Comments [0]
         
Gill Cleeren     .net | Silverlight | Silverlight Advent Calendar | sl4     December 19, 2009    

The day before yesterday, we looked at an sample with Twitter: we retrieved the tweets from the public timeline in an OOB trusted application. The public timeline feed on Twitter requires no credentials to be passed along. Some other feeds however do, such as the user timeline (containing your own tweets) and the friends timeline (containing the tweets of your friends).

Up until Silverlight 4, passing credentials to a service was not supported. Silverlight 3 added support for the ClientHttp stack, however, it was not possible to modify the authorization header, resulting in it being impossible to pass credentials. Silverlight 4 adds this support however. In combination with the support for cross-domain access for services for Silverlight if they are executed with elevated permissions, we can easily build a more complete Twitter client in Silverlight. We already did some work on this application, we will now extend it to make it possible to execute requests to services that require authorization.

Below is a screenshot of the modified application, running OOB as a trusted application.

image

We will not be looking at the code for the public timeline, that was already covered two days ago. Instead, let’s focus on the code we need to write to make the authorized request possible. Of course, we need the user’s credentials, therefore, I added a username/password field.

When clicking on the Submit button, two request are being executed:

Here’s the code for the first one. Note a few things. Passing credentials is only possible if using the ClientHttp stack. This is enabled by WebRequest.RegisterPrefix("http://", System.Net.Browser.WebRequestCreator.ClientHttp). This selects the ClientHttp stack instead of the browser http stack, which is used by default. To pass the credentials, we use the Credentials property and set it to an instance of NetworkCredentials. Here’s the code.

WebRequest.RegisterPrefix("http://", System.Net.Browser.WebRequestCreator.ClientHttp);
//try finding the user tweets and friends tweets
string userTimeLine = "http://twitter.com/statuses/user_timeline/" + 
  UserNameTextBox.Text + ".xml";
WebClient client = new WebClient();
client.Credentials = new NetworkCredential(UserNameTextBox.Text, 
  PasswordTextBox.Password);
client.UseDefaultCredentials = false;
client.DownloadStringCompleted += new DownloadStringCompletedEventHandler
  (user_DownloadStringCompleted);
 
client.DownloadStringAsync(new Uri(userTimeLine, UriKind.Absolute));

A very important line is setting the UseDefaultCredentials to false. If we set it to true, Silverlight will pass the credentials of the logged-on user on the machine (NTLM). If this is the wanted behavior however, simply setting this property to true will of course enable this.

The complete code for this application can be downloaded here:

SLTwitterCredentials.zip (82.89 KB)
  Posted on: Saturday, December 19, 2009 9:24:22 PM (Romance Standard Time, UTC+01:00)   |   Comments [0]
         
Gill Cleeren     .net | Silverlight | Silverlight Advent Calendar | sl4     December 18, 2009    

After looking at Twitter yesterday, we have arrived at another data binding-related feature new in Silverlight 4, namely the support for the IDataErrorInfo interface.

The fact that Silverlight supports this interface now, means we can fully use it to help with the display of validation errors in a data binding scenario. The interface itself is quite simple, as shown below:

public interface IDataErrorInfo
{
    string Error { get; }
 
    string this[string columnName] { get; }
}

When we are binding an object of a class that implements the IDataErrorInfo, Silverlight will automatically check each field for validation errors using the indexer. In here, we can write validation code that will check if the specified value is valid and if not, return the error message for that field. Silverlight will then automatically display the error for the field.

I have created a small sample that does exactly that. Being it almost Christmas, I have created a sample where the user can enter his christmas music collection, as shown below.

image

The type we are binding to is called ChristmasSong. This class implements both the INotifyPropertyChanged and the IDataErrorInfo interfaces. Both have been implemented in the code below. In the indexer, we are checking per column if the entered value is valid. If not, we are returning a message containing the validation error. Note that when we are entering “Last Christmas” by “Wham”, the application will not accept it! (Sorry to all Wham fans ;-) )

public class ChristmasSong : IDataErrorInfo, INotifyPropertyChanged
{
    private string title;
    private string performedBy;
    private TimeSpan duration;
    private DateTime published;
 
    private string error;
 
    public event PropertyChangedEventHandler PropertyChanged;
 
    protected void NotifyPropertyChanged(string propertyName)
    {
        if (PropertyChanged != null)
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
    }
 
    public string Title
    {
        get
        {
            return title;
        }
        set
        {
            title = value;
            NotifyPropertyChanged("Title");
        }
    }
 
    public string PerformedBy
    {
        get
        {
            return performedBy;
        }
        set
        {
            performedBy = value;
            NotifyPropertyChanged("PerformedBy");
        }
    }
 
    public TimeSpan Duration
    {
        get
        {
            return duration;
        }
        set
        {
            duration = value;
            NotifyPropertyChanged("Duration");
        }
    }
 
    public DateTime Published
    {
        get
        {
            return published;
        }
        set
        {
            published = value;
            NotifyPropertyChanged("Published");
        }
    }
 
    public string Error
    {
        get { return error; }
    }
 
    public string this[string columnName]
    {
        get 
        {
            string error = string.Empty;
            switch (columnName)
            {
                case "Title":
                    if(string.IsNullOrEmpty(title))
                        error = "Title can not be blank";
                    break;
                case "PerformedBy":
                    if(string.IsNullOrEmpty(performedBy))
                        error = "The artist should be filled in";
                    else if(performedBy.Equals("Wham") && title.Equals("Last Christmas"))
                        error = "You should NOT be entering that song to your collection!";
                    break;
                case "Duration":
                    if(duration.TotalSeconds > 600)
                    error = "A song can not be longer than 10 minutes";
                    break;
                case "Published":
                    if (Published > new DateTime(2010, 1, 1))
                        error = "You found a song from the future?";
                    else if (published < new DateTime(1900, 1, 1))
                        error = "That's a bit too old, isn't it?";
                    break;
            }
            return error;
        }
    }
}

We can now bind our UI to an instance of this type. Below is the XAML code for the Grid containing the fields. Note especially the data binding expression, where I’m stating the fields should be reporting the error back. For spacing reasons, I only pasted the Title field, the others are similar.

<TextBlock x:Name="TitleTextBlock" Text="Title" Grid.Row="1"  
  Foreground="Red" HorizontalAlignment="Left" Margin="2" 
  VerticalAlignment="Center"></TextBlock>
<TextBox x:Name="TitleTextBox" Grid.Row="1" Grid.Column="1" Width="200" 
  VerticalAlignment="Center" HorizontalAlignment="Left" Margin="2" 
  Text="{Binding Path=Title, Mode=TwoWay, ValidatesOnDataErrors=True, 
    NotifyOnValidationError=True}" />

When we are entering data now, the validation will kick in whenever a validation error is encountered.

We can take it one step further though, using the BindingValidationError event, defined on FrameworkElement. This event will trigger whenever a binding error is reported by the binding source, being here the instance of the ChristmasSong class. In this event, we can through the e.Action property check if an error was added or resolved. In this particular code, I have specified that the Submit button will only become enabled when there are no more errors.

private void ChristmasSongGrid_BindingValidationError(object sender, 
  ValidationErrorEventArgs e)
{
  switch (e.Action)
  {
    case ValidationErrorEventAction.Added:
         errorsOnPage++;
         break;
    case ValidationErrorEventAction.Removed:
         errorsOnPage--;
         break;
  }
  if (errorsOnPage == 0)
    SubmitButton.IsEnabled = true;
  else
    SubmitButton.IsEnabled = false;
}

All this can be seen in action, when we are entering “Last Christmas” by “Wham”!

image

The sample code can be downloaded here: SLDataErrorInfo.zip (308.96 KB)

  Posted on: Friday, December 18, 2009 12:06:56 PM (Romance Standard Time, UTC+01:00)   |   Comments [0]
         
Gill Cleeren     .net | Silverlight | Silverlight Advent Calendar | sl4     December 17, 2009    

In today’s article, we’re looking at cross-domain access to a service in Silverlight 4’s trusted applications.

As you may know, when accessing any service located in a domain different from the one in which the Silverlight application itself is hosted, Silverlight will check for a cross domain file being in place. Cross-domain policies prevent Silverlight applications to connect with services that are not in the same domain. However, a service can opt-in to be connected to if at the root of the domain, a cross-domain policy file has been deployed. Silverlight will check for its existence (the file should be named clientaccesspolicy.xml or crossdomain.xml, which is the cross-domain file of Flash) and if found, Silverlight will connect in a cross-domain manner.

Services such as Twitter and Facebook do not deploy this file anymore. Flickr does, meaning that we can connect to Flickr from a client-side Silverlight application. If we want to connect with Twitter from Silverlight, we have to create a service within the same domain as where the Silverlight application is hosted. This service can then communicate with Twitter and Silverlight can communicate with our service.

In Silverlight 4, the notion of trusted applications was added (we looked in this blog series already to some other concepts available when running a trusted application such as COM interop and local file access). If a Silverlight application is running as a trusted application, it can perform cross-domain calls, without there needing to be a cross domain policy file in place. That means that we can build a Twitter client as a trusted Silverlight application without having to build an extra service layer in between.

In the sample I have built for this post, I’m doing exactly that. Below is a screenshot. The UI of the application contains a templated ListBox. Note that the application is running OOB as a trusted application.

image

The code for this application is quite easy. It’s the same code we would write to access a “local” service, meaning a service in the same domain. Below is the code to access Twitter, an asynchronous web service call using the WebClient class.

public void GetPublicTimeLine()
{
    string publicTimeLine = "http://twitter.com/statuses/public_timeline.xml";
    WebClient client = new WebClient();
    client.DownloadStringCompleted += new DownloadStringCompletedEventHandler
      (client_DownloadStringCompleted);
    client.DownloadStringAsync(new Uri(publicTimeLine, UriKind.Absolute));
}
 
void client_DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e)
{
    XDocument document = XDocument.Parse(e.Result);
    twitterData = (from status in document.Descendants("status")
                   select new Tweet
                   {
                     Message = status.Element("text").Value.Trim(),
                     User = status.Element("user").Element("name").Value.Trim()
                     }).ToList();
 
    PublicTimeLineListBox.ItemsSource = twitterData;
}

This code is executed when a user clicks on the “Load Twitter Messages” button at the top. We do perform a check to see if the application is running OOB and is running with elevated permissions.

private void LoadTwitterButton_Click(object sender, RoutedEventArgs e)
{
    if (Application.Current.IsRunningOutOfBrowser && 
            Application.Current.HasElevatedPermissions)
    {
        //it's OK to access Twitter services cross domain now
        GetPublicTimeLine();
    }
}

Tomorrow, we will look at extending this sample by allowing to send credentials to Twitter to access personalized streams of tweets.

The complete sample can be downloaded here.

SLTrustedTwitter.zip (269 KB)
  Posted on: Thursday, December 17, 2009 4:52:54 PM (Romance Standard Time, UTC+01:00)   |   Comments [3]
         
Gill Cleeren     .net | Silverlight | Silverlight Advent Calendar | sl4     December 16, 2009    

The DataGrid is probably the most popular control in many business applications. Added in Silverlight 2, this control allows for easy displaying and editing of data in a tabular format. While already a very decent control, the DataGrid in Silverlight 2 and 3 still had some things that were missing or not working like you’d hope them to. In today’s post, we’ll look at the newly added changes to the control.

The first and probably biggest change is the auto-sizing option for columns. In previous versions, we could basically specify a column width or specify nothing at all, leaving it to Silverlight. What we could not do, was saying to a specific column: Take all the remaining space, similar to what we can do with the Grid control through the use of *.

Basically, there are now 5 options we have to specify how a column should behave (copied from the MSDN docs):

Member name
Description
Auto
The unit of measure is based on the size of the cells and the column header.
Pixel The unit of measure is expressed in pixels.
SizeToCells The unit of measure is based on the size of the cells.
SizeToHeader The unit of measure is based on the size of the column header.
Star The unit of measure is a weighted proportion of the available space.

Let’s take a look at using these in an example. I created a random data generator (see sample code download), which generates Employee instances. These are stored in a generic list which is then set as the ItemsSource for a DataGrid. The result can be see below.

image

As you can see in the screenshot, the Address column is wider than the others. I actually specified it to be twice as wide as the FirstName and the LastName columns. The City column has a specific width set.

<data:DataGrid AutoGenerateColumns="False" x:Name="EmployeeDataGrid" 
    HeadersVisibility="All" Grid.Row="1">
    <data:DataGrid.Columns>
        <data:DataGridTextColumn Header="FirstName" Binding="{Binding FirstName}" Width="*"/>
        <data:DataGridTextColumn Header="LastName" Binding="{Binding LastName}" Width="*"/>
        <data:DataGridTextColumn Header="Address" Binding="{Binding Address}" Width="2*"/>
        <data:DataGridTextColumn Header="City" Binding="{Binding City}" Width="100" />
    </data:DataGrid.Columns>
</data:DataGrid>

We can also use the other options. By clicking on the Change button at the top, we switch the DataGrid to use another sizing option.

private void ChangeColumnsButton_Click(object sender, RoutedEventArgs e)
{
    EmployeeDataGrid.Columns[0].Width = new DataGridLength(1, 
        DataGridLengthUnitType.SizeToCells);
    EmployeeDataGrid.Columns[1].Width = new DataGridLength(1, 
        DataGridLengthUnitType.SizeToCells);
    EmployeeDataGrid.Columns[2].Width = new DataGridLength(1, 
        DataGridLengthUnitType.SizeToCells);
    EmployeeDataGrid.Columns[3].Width = new DataGridLength(1, 
        DataGridLengthUnitType.Star);
}

This results in the following:

image

The first three columns are sizing to their contents. The City column takes all the remaining space.

The second new feature concerning the DataGrid, is the ability to copy data from the DataGrid to Excel. This feature is really handy in business applications. Very often, we need to be able to export data from an application towards Excel. This can make this very easy to do.

When we start a copy by selecting one or more rows, we are shown a prompt, asking us if we want to allow access to the clipboard. If confirmed, the data is copied as textual information and can be pasted in Excel.

There’s an event fired per row that is copied to the clipboard, namely the CopyingRowClipboardContent event. In this event, we can see what data is being copied and if needed apply transformations on it.

private void EmployeeDataGrid_CopyingRowClipboardContent(object sender, 
    DataGridRowClipboardEventArgs e)
{
    // do something
}
The complete sample can be downloaded here. SLNewDataGrid.zip (1.04 MB)
  Posted on: Wednesday, December 16, 2009 10:10:05 PM (Romance Standard Time, UTC+01:00)   |   Comments [2]
         
Gill Cleeren     .net | Silverlight | Silverlight Advent Calendar | sl4     December 15, 2009    

In today’s article, we’ll be looking at another new feature that was added with Silverlight 4, namely the ability to react to right-clicks in a Silverlight application. This means that we can override the default behavior of Silverlight where it’s showing the context menu allowing us to go to the configuration screen.

Because Silverlight now supports right-clicking, we can create a context menu that appears on the right-click event. We can code this context menu as we want. Right-clicking is supported on any UIElement: on the UIElement class, there are two new events added, namely the MouseRightButtonDown and the MouseRightButtonUp. Both of these new events use MouseButtonEventArgs as their event arguments, which are also used for the normal left click events.

As said, by default, the Silverlight context menu is shown when a user right-clicks on a Silverlight application. To block this, in the MouseRightButtonDown, we have to set the e.Handled to true. This way, the default context menu is not shown and we can go ahead and create our own context menu. This will appear on the MouseLeftButtonUp event.

Let’s take a look at a simple application. In this application, we have attached a custom context menu on an Image control. Using this context menu, I can choose to delete the image I clicked on.

image

The image itself has both the MouseRightButtonDown and the MouseRightButtonUp events attached to it using XAML code:

<Image Height="195" x:Name="imgTree" Stretch="Fill" Width="249" 
   MouseRightButtonDown="imgTree_MouseRightButtonDown" 
   MouseRightButtonUp="imgTree_MouseRightButtonUp" 
   Source="tree.png" Canvas.Left="181" Canvas.Top="79">
</Image>

To prevent the default context menu from appearing, we can set the e.Handled to true in the mouse down event:

private void imgTree_MouseRightButtonDown(object sender, MouseButtonEventArgs e)
{
    e.Handled = true;
}

In the mouse up event, we can then specify what context menu we want to have appear:

private void imgTree_MouseRightButtonUp(object sender, MouseButtonEventArgs e)
{
    Image image = sender as Image;
    if (image != null)
    {
        ContextMenu contextMenu = new ContextMenu(PrintCanvas, image);
        contextMenu.Show(e.GetPosition(LayoutRoot));
    }
}

The ContextMenu used here is a custom control.

The complete source code can be downloaded here: SLRightClick.zip (473.2 KB)

  Posted on: Tuesday, December 15, 2009 11:21:21 PM (Romance Standard Time, UTC+01:00)   |   Comments [0]
         
Gill Cleeren     .net | Silverlight | Silverlight Advent Calendar | sl4     December 14, 2009    

Only 10 days ‘till Christmas and thus only 10 more posts after this one to finish my series. In today’s post, we’ll be taking a look at the Local Connection API, a feature introduced with Silverlight 3.

Most of the time, we build full-screen Silverlight applications, meaning that the only function that the HTML still fulfills, is hosting a DIV that will contain the HTML OBJECT tag in which Silverlight will be hosted. However, sometimes Silverlight could be used as an “island” on an HTML page. For example, we could have an HTML page (perhaps rendered by ASP.NET) in which a table of data is shown. The end-user may want a richer experience with this data grid such as reordering rows, efficient paging and so on. We could decide to just convert that specific part to functionality to Silverlight, resulting in a Silverlight island.

To go even further, in some scenarios, it may be needed that two or more of these Silverlight islands live on a single page. Silverlight 2 introduced the HTML Bridge to allow these two Silverlight plug-ins to talk to one another: using Javascript as an intermediate, we could send messages from control 1 to control 2 and vice-versa. While possible, it’s not the easiest way of developing an application.

Silverlight 3 introduced something easier for this particular problem, namely the local connection API. It basically allows two or more Silverlight instances to communicate with each other. These two instances can live on the same page, but can even live on two different browser tabs or even two different browser windows. The API uses a concept of a named pipe: a named sender sends a message into a pipe and a receiver can intercept this message. The message needs to be a string.

The messaging system introduced is based on two types, LocalMessageSender and LocalMessageReceiver, both types that live in the System.Windows.Messaging namespace.

Let’s take a look at an example. The application I built for this post contains two Silverlight controls. The first control contains a few buttons. When clicking on a button, I want to change the background of control n° 2. (Note that these are actually 2 different Silverlight applications hosted on one single page).

image

The first thing we need to do is creating an instance of the LocalMessageSender class. As parameter, we pass in the name that will be used as the identification (ColorSender), like so:

private LocalMessageSender localMessageSender;
private LocalMessageReceiver localMessageReceiver;
 
public MainPage()
{
    InitializeComponent();
    localMessageSender = new LocalMessageSender("ColorSender");
 
}

When we click on a button, we’ll send a message using this instance. In this case, we pass in the name of the color we want:

private void RedButton_Click(object sender, RoutedEventArgs e)
{
  localMessageSender.SendAsync("Red");
}
 
private void GreenButton_Click(object sender, RoutedEventArgs e)
{
  localMessageSender.SendAsync("Green");
}
 
private void BlueButton_Click(object sender, RoutedEventArgs e)
{
  localMessageSender.SendAsync("Blue");
}

In the second application, we create a LocalMessageReceiver, passing in the same string, ColorSender, as the name of the sender it should listen to. We then specify the handler for the MessageReceived event, which will trigger when a string is received. The receiver starts listening for values by calling its Listen() method:

private LocalMessageReceiver localMessageReceiver;
 
public MainPage()
{
  InitializeComponent();
  localMessageReceiver = new LocalMessageReceiver("ColorSender");
  localMessageReceiver.MessageReceived += 
  new EventHandler<MessageReceivedEventArgs>(localMessageReceiver_MessageReceived);
  localMessageReceiver.Listen();
}

Based on the value of the received message, we change the color:

void localMessageReceiver_MessageReceived(object sender, MessageReceivedEventArgs e)
{
  switch (e.Message)
  {
    case "Red": LayoutRoot.Background = new SolidColorBrush(Colors.Red);
                ColorTextBlock.Text = "RED";
                break;
    case "Green": LayoutRoot.Background = new SolidColorBrush(Colors.Green);
                  ColorTextBlock.Text = "GREEN";
                  break;
    case "Blue": LayoutRoot.Background = new SolidColorBrush(Colors.Blue);
                 ColorTextBlock.Text = "BLUE";
                 break;
    default: LayoutRoot.Background = new SolidColorBrush(Colors.White);
             ColorTextBlock.Text = "WHITE";
             break;
  }
}

The code can be downloaded here: SLLocalConnection.zip (97.1 KB)

  Posted on: Monday, December 14, 2009 9:31:33 PM (Romance Standard Time, UTC+01:00)   |   Comments [0]
         
Gill Cleeren     Silverlight | Silverlight Advent Calendar | sl4     December 14, 2009    

I hope you are all enjoying my post series Silverlight Advent Calendar. I still have a list of upcoming articles, but I was wondering, are there any specific things you would like to see appear?

I have one request already for an article on the local connection API, that will come in the coming days.

Let me know your ideas!

  Posted on: Monday, December 14, 2009 8:57:06 AM (Romance Standard Time, UTC+01:00)   |   Comments [4]
         
Gill Cleeren     .net | Silverlight | Silverlight Advent Calendar | sl4     December 13, 2009    

In today’s post, we will be working with services. As some of you know, I’m currently finishing my first book titled Silverlight 4 Data Access Cookbook. If all goes well, it will be available in early access preview somewhere in the coming week. Therefore, I decided I needed to write a post that’s in the same area as my book. More specifically, I’ll be creating a application that works together with the services API exposed by Bing.com, Microsoft’s new and successful search engine.

Bing has a very rich API that we can use to incorporate search functionalities in our applications, including Silverlight applications. It allows us to use WCF (SOAP) and REST communication, because it exposes endpoints for both these technologies. In this particular example, I’m using the SOAP interface for communication using a WCF service. To get a complete overview of the functionalities, you can download the PDF describing the entire API at http://www.bing.com/developers/s/API%20Basics.pdf . It also contains some sample code.

The very first thing we should before we can actually start building applications with Bing, is obtaining an API key. This key is required so Bing can check that your request is valid (this key will be part of every request you’ll send to Bing). Obtaining a key is free and can be done at http://www.bing.com/developers/createapp.aspx .

The provided sample is a Silverlight 4/VS2010 solution, all the code included however also works with Visual Studio 2008/Silverlight 3.

First, we need to generate a proxy using Visual Studio 2010. For this, we need the address of the WSDL (Web Service Description Language) file. In the documentation, we can find that this address is: http://api.search.live.net/search.wsdl?AppID=12345 where 12345 needs to be replaced with your obtained API key. In Visual Studio, we add the service reference using the dialog and I have set the namespace to BingSearchService.

image

Visual Studio will at this point create the proxy for the service. A proxy contains a client-side copy of the types exposed by the service and its methods. The contents of the methods is replaced with a call to the real service method. In the client-side code, we can now work with these types and methods.

In the screenshot a bit further, you can see the interface of the application. It contains a TextBox and a Button to search and a templated ListBox to display the results. Let’s first create the code for the event handler of the click event. Bing accepts quite a lot of parameters for the search it needs to perform. These are encapsulated in an object of type SearchRequest. Since we are conducting a normal web search, we specify this in the Sources property of the SearchRequest instance. A search with Bing is a service request, so all communication needs to be done asynchronously. We can see this at the way we are calling the service using the SearchCompleted event and SearchAsync() method. Below is the code.

private void SearchButton_Click(object sender, RoutedEventArgs e)
{
  LiveSearchPortTypeClient soapClient = new LiveSearchPortTypeClient();
  SearchRequest request = new SearchRequest();
  request.AppId = "81691C6A195FBC3FB469594BAA30B50A99CF3D22";
  request.Sources = new SourceType[] { SourceType.Web };
 
  if (SearchTextBox.Text != string.Empty)
  {
    request.Query = SearchTextBox.Text;
    soapClient.SearchCompleted += 
      new EventHandler<SearchCompletedEventArgs>(soapClient_SearchCompleted);
    soapClient.SearchAsync(request);
  }
}

When the service completes asynchronously, meaning, when Bing’s results are ready and available, the *Completed event is called and executed. In this event handler, we have access to the results of the service call. The result can be found in the e.Result parameter and is always of the same type as returned by the service. In our specific case, the used type is a SearchResponse, again a type exposed by the Bing services. We can now loop through these results and using a simple LINQ query, create an IEnumberable<SearchResult> . SearchResult is a type created by us to allow us to data bind to the results. This type is very simple:

public class SearchResult
{
    public string SearchResultTitle { get; set; }
    public string SearchResultDescription { get; set; }
    public string SearchResultUri { get; set; }
}

The code for the event handler is shown next:

void soapClient_SearchCompleted(object sender, SearchCompletedEventArgs e)
{
    SearchResponse response = e.Result;
    if (response.Web.Results.Count() > 0)
    {
        var results = from result in response.Web.Results
                      select new SearchResult
                      {
                          SearchResultTitle = result.Title,
                          SearchResultUri = result.Url,
                          SearchResultDescription = result.Description
                      };
        ResultListBox.ItemsSource = results.ToList();
    }
}

The ListBox is as said templated; the ItemsTemplate has been replaced with a custom data template, as shown below:

<ListBox x:Name="ResultListBox" Grid.Row="1" Margin="3" Background="Black">
  <ListBox.ItemTemplate>
    <DataTemplate>
      <Grid>
        <Grid.RowDefinitions>
          <RowDefinition></RowDefinition>
            <RowDefinition></RowDefinition>
            <RowDefinition></RowDefinition>
        </Grid.RowDefinitions>
        <TextBlock Text="{Binding SearchResultTitle}" FontWeight="Bold" 
          FontSize="15" TextDecorations="Underline" Foreground="White"></TextBlock>
        <TextBlock Text="{Binding SearchResultDescription}" Grid.Row="1" 
          TextWrapping="Wrap" Width="400" HorizontalAlignment="Left" 
          Foreground="White"></TextBlock>
        <TextBlock Text="{Binding SearchResultUri}" Grid.Row="2" Foreground="#568e71">
        </TextBlock>
      </Grid>
    </DataTemplate>
  </ListBox.ItemTemplate>
</ListBox>

When we now execute a search using our application, we see the following:

image

The complete sample can be downloaded here:

SLBing.zip (790.49 KB)
  Posted on: Sunday, December 13, 2009 12:48:53 PM (Romance Standard Time, UTC+01:00)   |   Comments [0]
         
Gill Cleeren     .net | Silverlight | Silverlight Advent Calendar | sl4     December 11, 2009    

I made it through the first half of my Silverlight Advent Calendar! You’re currently reading the 12th installment of my article series. In this article, we are looking at the RichTextArea control, which was added to the platform with Silverlight 4.

Up until Silverlight 4, we had no real way to perform rich text editing. Some (open source) implementations have been built of which the best one is in my opinion VectorLight.com’s RichTextBox/RichTextBlock (check it out at www.vectorlight.com, they have quite some nice controls). Silverlight 4 introduces the RichTextArea control. Its name already implies what it is capable of: allowing us to do rich text editing in our Silverlight applications. Things like making text bold, italic or underlined are of course supported by this control. On top of that, more advanced options are available, such as adding hyperlinks in text, adding images and adding XAML content. It can also out-of-the-box work with the clipboard and offers BiDi support. The latter is important for some languages such as Hebrew, since these are right-to-left languages.

Behind the visual of the control, text is being added as XAML, so in fact, the control is rendering the result of that XAML. This XAML is XAML like we know it for displaying rich text using TextBlock controls, containing Paragraphs, Runs and LineBreaks. At the time of writing, there is no real Text property on the control to get the entered text without the markup. To get the contents, we can use the Blocks collection to loop through the content. This is a bit of a setback and I can only hope it will in some way become possible to get the text from the control as well (without the markup).

Using this new control, we can thus perform most of the actions we’ll want to do with text. The control does not have buttons by default, so we need to add them ourselves. Below is an implementation of the control in combination with a ribbon (found in the official Silverlight 4 whitepaper/documentation).

image

Let’s take a look a simple implementation now. My implementation will make it possible to add text (quite logically), make it bold, underlined or italic. We’ll also add the possibility to change the color of the selected text and print it using the new printing functions in Silverlight 4.

image

Below is the XAML for the RichTextArea (nothing special as you can see):

<RichTextArea x:Name="MainRichTextArea" Height="300"></RichTextArea>

To implement the different functions, we need to add some code in the event handlers of the button click events. For example, if we want to make the selected text bold, we start by checking if text is selected. If there is, we check if the selected text is already bold or not. We set it to bold if it isn’t and vice-versa of course. The code for this is shown below.

private void BoldButton_Click(object sender, RoutedEventArgs e)
{
  if (MainRichTextArea.Selection.Text.Length > 0)
  {
    if (MainRichTextArea.Selection.GetPropertyValue(Run.FontWeightProperty) 
        is FontWeight &&
        ((FontWeight)MainRichTextArea.Selection.GetPropertyValue
        (Run.FontWeightProperty)) == FontWeights.Normal)
        {
          MainRichTextArea.Selection.SetPropertyValue(Run.FontWeightProperty, 
            FontWeights.Bold);  
        }
    else
        MainRichTextArea.Selection.SetPropertyValue(Run.FontWeightProperty, 
          FontWeights.Normal);
        MainRichTextArea.Focus();
    }
}

The code is similar for the other actions, it can be found in the sample code download. When we want to print our added text, we have 2 options. The most simple one is pointing the PageVisual to the RichTextArea:

private void PrintButton_Click(object sender, RoutedEventArgs e)
{
  PrintDocument printDocument = new PrintDocument();
  printDocument.DocumentName = "My rich text";
 
  printDocument.PrintPage += (s, args) =>
  {
    args.PageVisual = MainRichTextArea;
  };
  printDocument.Print();
}

The second option exists out of looping through the Blocks collection of the RichTextArea, parsing each of the retrieved items and adding them to the Inlines collection of a TextBlock control.

The sample code can be downloaded here: SLRichTextArea.zip (84.68 KB)

  Posted on: Saturday, December 12, 2009 12:49:28 AM (Romance Standard Time, UTC+01:00)   |   Comments [0]
         
Gill Cleeren     .net | Silverlight | Silverlight Advent Calendar | sl4     December 11, 2009    

One day away from finishing the first 12 (half of the total of 24 ;-) )of my Silverlight articles. In this post, we are looking at local file access in Silverlight 4. This new feature is another one that is only enabled with OOB apps with elevated permissions.

In Silverlight 2 and 3, we already had some basic file access. We could perform an OpenFileDialog starting with Silverlight 2. It gave us a read-only stream to the selected file, meaning that the only thing we could do with the file, was reading it and using its contents in the application. Silverlight 3 added the SaveFileDialog, which, surprisingly, allows us to save files to the local disk.

Silverlight 4 ads more or less real local data access. By more or less, I mean that Silverlight applications can only access some folders. More particularly, we can access the My Documents, My Pictures, My Videos and My Music folders (on Mac, an equivalent exists). Within this directory, we can read, create and delete files. One important side-note though: the application needs to be a trusted application, meaning that it needs to run with elevated permissions.

Silverlight has a specific enumeration that we can use to get the physical location of the files: Environment.SpecialFolder. In it, we can find MyPictures etc. Note that it contains more than what we can access at this point, for example ProgramFiles. Even if we try to do something with this, we can’t since Silverlight won’t allow access to this directory.

Let’s take a look at a basic application as shown below.

image

Let’s first take a look at the left listbox, which is filled with the files of my My Pictures folder. Reading out the files, as well as other operations on the files, are done using the traditional classes contained in the System.IO namespace, such as Directory, File, Path etc. To read out the contents, we use the following code:

if (canWork)
{
  var files = Directory.EnumerateFiles(Environment.GetFolderPath
   (Environment.SpecialFolder.MyPictures));
  foreach (var file in files)
    {
      string fileName = System.IO.Path.GetFileName(file);
      imageNames.Add(fileName);
    }
    ImageListBox.ItemsSource = imageNames;
}

Note the canWork bool value. Since the application can only work if running OOB and with elevated permissions, we perform a check initially to see if the operations are permitted. The value is set through the following code:

void MainPage_Loaded(object sender, RoutedEventArgs e)
{
  //check to see if application is running OOB and with elevated permissions
  if (App.Current.IsRunningOutOfBrowser && App.Current.HasElevatedPermissions)
    canWork = true;
  else
    MessageBox.Show("The application needs to run out-of-browser with 
      elevated permissions");
}

The second listbox is filled with all *.txt files, located in my My Documents folder: I’m simply applying a filter.

private void FetchTextFiles()
{
    var files = Directory.EnumerateFiles(Environment.GetFolderPath
     (Environment.SpecialFolder.MyDocuments))
     .Where(filename => filename.EndsWith("txt"));
    
    TextFilesListBox.ItemsSource = files;
}

As said, we can do more than just reading out the contents of a directory. We can for example delete a file. When we select a file in the second listbox, we can delete it using the following code:

private void DeleteButton_Click(object sender, RoutedEventArgs e)
{
  if (canWork)
  {
    if (TextFilesListBox.SelectedItem != null)
    {
      string path = TextFilesListBox.SelectedItem.ToString();
      File.Delete(path);
      FetchTextFiles();
    }
  }
}

As you can see, working with the files is no different than working with files in plain .NET.

The code can be downloaded here: SLLocalFileAccess.zip (68.26 KB)

  Posted on: Friday, December 11, 2009 10:34:42 PM (Romance Standard Time, UTC+01:00)   |   Comments [0]
         
Gill Cleeren     .net | Silverlight | Silverlight Advent Calendar | sl4     December 10, 2009    

Today, we are looking again at another new feature in Silverlight 4 which is the support for webcams and microphones.

Silverlight 4 gets support for microphones and webcams. It has access to the raw streams for both. The added support opens a lot of opportunities for new types of applications. For example, it becomes possible to write an application that reads barcodes of products you hold in front of the webcam, scans them and searches for product information.

The classes that expose this functionality live in the System.Windows.Media namespace. We’ll be using those to create a simple application that connects with the webcam and takes snapshots. Below is the interface of the application (a colleague of mine was just eating a banana :) )

image

The XAML for the application is shown below. The ComboBox displays the available webcams (only one on my machine), we are binding to the FriendlyName property. The ListBox is templated: both the ItemsTemplate and the ItemsPanel are overriden, to have the control display a horizontal list of images.

<Grid x:Name="LayoutRoot" Background="White">
  <Grid.RowDefinitions>
    <RowDefinition Height="330"></RowDefinition>
      <RowDefinition Height="50"></RowDefinition>
        <RowDefinition Height="140"></RowDefinition>
  </Grid.RowDefinitions>
  <Border BorderBrush="Black" BorderThickness="3" HorizontalAlignment="Center" 
    VerticalAlignment="Center">
    <Rectangle x:Name="WebcamRectangle" Fill="LightGray" Height="300" 
      Width="400"></Rectangle>
  </Border>
  <StackPanel Orientation="Horizontal" Grid.Row="1" VerticalAlignment="Center" 
    HorizontalAlignment="Center">
    <ComboBox x:Name="WebcamComboBox" Width="200" Margin="3" 
      SelectionChanged="WebcamComboBox_SelectionChanged">
      <ComboBox.ItemTemplate>
        <DataTemplate>
          <TextBlock Text="{Binding FriendlyName}"></TextBlock>
        </DataTemplate>
      </ComboBox.ItemTemplate>
    </ComboBox>
    <Button x:Name="StartWebcamButton" Click="StartWebcamButton_Click" 
      Content="Start webcam" HorizontalAlignment="Center" Margin="3"></Button>
    <Button x:Name="TakePictureButton" Click="TakePictureButton_Click" 
      Content="Take picture" HorizontalAlignment="Center" Margin="3"></Button>
  </StackPanel>
  <ListBox x:Name="PicListBox"  Width="500" Height="120" 
    ScrollViewer.HorizontalScrollBarVisibility="Visible"  Grid.Row="2">
    <ListBox.ItemTemplate>
      <DataTemplate>
        <Image Margin="5" Source="{Binding}" Stretch="UniformToFill" 
          Height="80" VerticalAlignment="Center"/>
      </DataTemplate>
    </ListBox.ItemTemplate>
    <ListBox.ItemsPanel>
      <ItemsPanelTemplate>
        <StackPanel Orientation="Horizontal" VerticalAlignment="Top" 
          HorizontalAlignment="Left"/>
      </ItemsPanelTemplate>
    </ListBox.ItemsPanel>
  </ListBox>
</Grid>

To fill the ComboBox with the available webcams, we can use the CaptureDeviceConfiguration.GetAvailableVideoCaptureDevices(). This returns a Collection of VideoCaptureDevice instances. The VideoCaptureDevice class exposes the FriendlyName property we used in the data binding expression.

//fills the dropdown with all available webcams
WebcamComboBox.ItemsSource = 
  CaptureDeviceConfiguration.GetAvailableVideoCaptureDevices();

We can instruct Silverlight to use the default capturing device, if nothing is selected by the user in the ComboBox:

//use the default
VideoCaptureDevice selectedVideoCaptureDevice = 
CaptureDeviceConfiguration.GetDefaultVideoCaptureDevice();

A little side note: we can change the default device in the Silverlight Configuration window as shown below.

image

Starting the webcam needs to be a user-initiated action. It’s impossible to start the webcam on load of the application for example. Therefore, we put this code in the event handler of the click on the Button. Whenever the webcam is started, a prompt is shown, asking the user if he wants to allow the application to access the device. This prompt can not be changed by the developer.

image

The following lines of code, located in the click event handler, check if the user has already granted permission in this particular session to access the webcam. If not, the prompt is shown following the RequestDeviceAccess() method call.

if (CaptureDeviceConfiguration.AllowedDeviceAccess 
    || CaptureDeviceConfiguration.RequestDeviceAccess())
{
...
}

To access the video capture of the webcam, we use the CaptureSource class.

VideoCaptureDevice videoCaptureDevice = selectedVideoCaptureDevice;
CaptureSource captureSource = new CaptureSource();
captureSource.VideoCaptureDevice = videoCaptureDevice;

This CaptureSource can then be used in combination with a VideoBrush to paint the video on any shape. Here, we used a Rectangle. To start the capturing of the device associated with the capture device (note that more than one device can be associated with one CaptureSource), we call its Start() method.

VideoBrush videoBrush = new VideoBrush();
videoBrush.SetSource(captureSource);
WebcamRectangle.Fill = videoBrush;
captureSource.Start();

If we want to capture a snapshot, we can use the AsyncCaptureImage method. It provides a WriteableBitmap to the Action(T) which is passed in as parameter. We add the image to the collection. Because of the applied binding, it is displayed automatically.

private void TakePictureButton_Click(object sender, RoutedEventArgs e)
{
    captureSource.AsyncCaptureImage((snapshot) =>
    {
        snapshots.Add(snapshot);
    });
}

The complete code for today’s article can be found here: SLWebcam.zip (60.67 KB)

  Posted on: Thursday, December 10, 2009 11:33:15 AM (Romance Standard Time, UTC+01:00)   |   Comments [1]
         
Gill Cleeren     .net | Silverlight | Silverlight Advent Calendar | sl4     December 9, 2009    
Article number 9 is here, a bit later on the day since I was home late yesterday from the MVP dinner we had and left at 6am this morning to do the Silverlight Road Show. But here we are with the article of today, December 9th!

Today we are looking at a data binding feature that was introduced with Silverlight 3. The reason I’m dedicating an article to it, is that not that many people seem to know about this little gem, namely element-to-element binding or simply element binding. Regular data binding happens between a source object (for example a Person or a Customer) and a control such as a TextBlock. Element binding happens between 2 controls: a property of element A is bound to a property of element B.

A good example to understand element bindings is the following: a Slider control of which the Value property is bound to the Text property of a TextBlock.

image

To make the link between the 2 values, we create a binding as usual, but we specify the “source” as being another element, in this case the Slider control using the ElementName property on the binding.

<StackPanel x:Name="SliderStackPanel" Orientation="Horizontal" Grid.Row="0">
    <Slider x:Name="YearsToPensionSlider" Width="300" Minimum="0" Maximum="40" 
      SmallChange="1" LargeChange="1" VerticalAlignment="Center"></Slider>
    <TextBlock x:Name="ValueTextBlock" 
      Text="{Binding ElementName=YearsToPensionSlider, Path=Value}" 
      VerticalAlignment="Center"></TextBlock>
</StackPanel>

We can also make this binding a bi-directional binding. In the image above, in the second row, it’s possible to specify a value manually in the TextBox control. Due to the binding, the slider will also change its value if we specify the binding to be a two way binding, as so:

<StackPanel x:Name="SliderStackPanel2" Orientation="Horizontal" Grid.Row="1">
    <Slider x:Name="YearsToPensionSlider2" Width="300" Minimum="0" Maximum="40" 
      SmallChange="1" LargeChange="1" VerticalAlignment="Center"></Slider>
    <TextBox x:Name="ValueTextBox" 
      Text="{Binding ElementName=YearsToPensionSlider2, Path=Value, Mode=TwoWay}" 
      VerticalAlignment="Center" Width="100"></TextBox>
</StackPanel>

You can download the code for this sample here. This is a Visual Studio 2008 project and works both with Silverlight 3 and Silverlight 4.SLElementBinding.zip (59.38 KB)
  Posted on: Wednesday, December 09, 2009 11:35:28 PM (Romance Standard Time, UTC+01:00)   |   Comments [3]
         
Gill Cleeren     .net | Silverlight | Silverlight Advent Calendar | sl4     December 7, 2009    

Today, I picked another new feature of Silverlight 4, namely the programmatic access to and from the clipboard in Silverlight 4.

Silverlight 4 has the ability to copy text to the clipboard, check if there is text available on the clipboard and paste the text from the clipboard back to the Silverlight application. To support this, it has 3 static methods available on the newly added ClipBoard class:

  • Clipboard.SetText()
  • Clipboard.ContainsText()
  • Clipboard.GetText()

Let’s look at using these in a sample. Below we see the interface of the basic text editing application, containing 2 RichTextArea controls.

image

When clicking on the left button, we check if there’s some text selected in the left RichTextArea control. As said, this is done through the use of the static SetText() method on the ClipBoard class. Note that any interaction with the Clipboard class can only happen after a user-initiated action, such as clicking a Button.

private void CopyButton_Click(object sender, RoutedEventArgs e)
{
    if (CopyTextArea.Selection.Text != string.Empty)
    {
        WarningTextBlock.Text = string.Empty;
        Clipboard.SetText(CopyTextArea.Selection.Text);
    }
    else
    {
        WarningTextBlock.Text = "Select some text first";
    }
}

This call is supported both in-browser and out-of-browser. There’s a difference though. When accessing the clipboard from an in-browser application, a warning will be shown to the user, asking if the action to the clipboard is trusted.

image

If we however make our application a trusted application by requiring elevated permissions when running out-of-browser, it will no longer display this prompt (we looked at elevating permissions yesterday).

When we want to paste in the text using the paste button, we can use the 2 other before mentioned methods, like so:

private void PasteButton_Click(object sender, RoutedEventArgs e)
{
    if (Clipboard.ContainsText())
    {
        PasteTexArea.Selection.Text = Clipboard.GetText();
    }
    else
    {
        WarningTextBlock.Text = "No text exists on the clipboard";
    }
}

In the current beta, we can only copy text to and from the clipboard. Objects such as images are not supported at this point!

The complete code for this sample can be found here: SilverlightClipboard.zip (61.65 KB)

  Posted on: Tuesday, December 08, 2009 12:04:05 AM (Romance Standard Time, UTC+01:00)   |   Comments [0]
         
Gill Cleeren     .net | Silverlight | Silverlight Advent Calendar | sl4     December 6, 2009    

December 7th already, this post marks the first week of my Silverlight Advent Calendar!

In today’s post, I’ll be looking at how to create a simple kiosk application. A kiosk application is typically something you see at a booth or a registration desk, filling the entire screen and only allowing the user to perform a specific action. In this specific example, I’m looking at how to create a user registration application which could typically be used for a kiosk.

As said, the most specific requirement for this type of applications, is being full screen. Silverlight 2 and 3 already supported full screen applications: making an application full screen could easily be done using the following line of code:

App.Current.Host.Content.IsFullScreen = true;

This code will only execute following a user-initiated action, such as a click on a Button. It’s thus not possible to load the application full screen by putting this line in some loaded event of the control.

Seems all quite well, doesn’t it? Indeed,  but there was one disappointment in all this: when in full screen, the user could not enter text, the text input was limited to some keys such as the arrow keys. Every character key was ignored. This behavior changed in Silverlight 4. In fact, I should say “can be changed” in Silverlight 4. By default, it will still not accept character keys in full screen, but when creating the application as OOB (Out Of Browser) application and giving it elevated permissions, it will accept them perfectly.

Elevated permissions

Before we look at the application itself, let’s first look at elevated permissions. In Silverlight 1, 2 and 3, code executed  always ran in the sandbox of the browser. There was no way to get out of this sandbox. Silverlight 4 adds a new type of application, namely a trusted Silverlight application, running with elevated permissions and allowing actions that were not possible in the previous versions. This means that we now have 3 possible modes in Silverlight 4: in-browser, out-of-browser (but still in the sandbox) and trusted (out-of-browser with elevated permissions).

Each application can be created to require elevated permissions to run. We can enable this by in the Project Properties window, clicking on the Out-of-Browser settings and then checking the following checkbox:

clip_image002

When the application is now installed, another dialog is displayed, warning the user that this application will have more permissions than “normal” installed apps. This dialog looks like the following screenshot.

image

We’ll come across more features in the following days which require elevated permissions such as COM interop and cross-domain service access.

Back to the kiosk

OK, now that we know that for our application, we’ll need elevated permissions, let’s look at some code and screenshots. The application is built as a navigation application. The application starts (by setting the root page) to an admin screen. In this screen, the admin can set the application to full screen. From then on, it is full screen and cannot be changed by the user.

image

When clicking on the button, the following code is executed:

private void FullScreenButton_Click(object sender, RoutedEventArgs e)
{
    App.Current.Host.Content.IsFullScreen = true;
}

The admin can then click the Start Wizard hyperlink, which will set the kiosk-registration application to its waiting screen.

image

In the following screen, the user can enter his/her details. In full-screen mode, this would NOT have been possible if:

  • we had used Silverlight 2 or Silverlight 3
  • we would not have set the application to require elevated permissions

Try for yourself disabling the elevated permissions requirement. If you run the application in full-screen then (in OOB mode), all text input in the text fields is ignored.

image

How to quit the application

When putting a trusted application in full-screen, by default hitting the ESCAPE key will not resize it back to its original position. Therefore, we can handle the keydown event in the MainWindow.xaml for example, as follows:

private void UserControl_KeyDown(object sender, KeyEventArgs e)
{
    if(e.Key == Key.Escape)
        App.Current.Host.Content.IsFullScreen = false;
}
The full code can be downloaded here: KioskApplication.zip (166.63 KB)
  Posted on: Monday, December 07, 2009 12:08:01 AM (Romance Standard Time, UTC+01:00)   |   Comments [0]
         
Gill Cleeren     .net | Silverlight | Silverlight Advent Calendar | sl4     December 3, 2009    

Here we are with the 4th article already in my Silverlight Advent Calendar series!

When using data binding in Silverlight, we often have to include one or more converters. A converter is a class that implements the IValueConverter interface. This class defines 2 methods, Convert and ConvertBack. Convert is applied when the data in the data binding action flows from source object to target control. A common use for converters is formatting a date value that comes from the database into a specific format or adding a currency symbol to a double value. Below is a sample converter class:

public class CurrencyConverter : IValueConverter
    {
 
        #region IValueConverter Members
 
        public object Convert(object value, Type targetType, object parameter, 
            System.Globalization.CultureInfo culture)
        {
            double amount = double.Parse(value.ToString());
            if (amount < 0)
                return "- " + amount.ToString("c", culture);
            else
                return amount.ToString("c", culture);
        }
 
        public object ConvertBack(object value, Type targetType, object parameter, 
            System.Globalization.CultureInfo culture)
        {
            throw new NotImplementedException();
        }
 
        #endregion
    }

Silverlight 4’s data binding engine has been extended with a few options that in some cases can avoid forcing us to create a converter. More specifically, three new options have been added: TargetNullValue, StringFormat and FallbackValue.

For these samples, we’ll use a class called Customer:

public class Customer
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public DateTime BirthDate { get; set; }
    public string Email { get; set; }
}

I have instantiated this class and set it as DataContext for the entire UserControl:

 

customer = new Customer()
           {
               FirstName = "Gill",
               LastName = "Cleeren",
               BirthDay = new DateTime(1979, 1, 1),
               Email = "gillcleeren@somewhere.com",
               CustomerAddress = null
           };
 
           this.DataContext = customer;

Let’s take a look at what these new features offer us. TargetNullValue can be used in a data binding expression to specify what value should be used in case the binding returns null. In the previous sample, there is no value specified for the CustomerAddress property (it is null). If we want some replacement text, in this case “Unknown”, to be placed instead, we can use TargetNullValue like so:

<TextBlock x:Name="AddressTextBlock" Grid.Row="4" Grid.Column="0" 
    Text="Address:" FontWeight="Bold"></TextBlock>
<TextBlock x:Name="AddressValueTextBlock" Grid.Row="4" Grid.Column="1" 
    Text="{Binding CustomerAddress, TargetNullValue=Unknown}" ></TextBlock>

StringFormat can be used to format the value, just as was shown with the Converter earlier. For example, if we want the BirthDay to be formatted as a date in MM/dd/yyyy format, we can do so using the following code:

<TextBlock x:Name="BirthDateTextBlock" Grid.Row="2" 
    Grid.Column="0" Text="Birthday:" FontWeight="Bold"></TextBlock>
<TextBlock x:Name="BirthDateValueTextBlock" Grid.Row="2" Grid.Column="1" 
    Text="{Binding BirthDay, StringFormat=MM/dd/yyyy}"></TextBlock>

Finally, the FallbackValue can be used to display a value when the data-bound property on the source can not be located. In other words, this value will be displayed when we make an error in that we are trying to bind to a non-existent property. This is shown using the following code:

 

<TextBlock x:Name="InfoTextBlock" Grid.Row="5" 
    Grid.Column="0" Text="Info:" FontWeight="Bold"></TextBlock>
<TextBlock x:Name="InfoValueTextBlock" Grid.Row="5" Grid.Column="1" 
    Text="{Binding Info, FallbackValue=Nothing}" ></TextBlock>

The final result is shown below:

image

The complete code sample can be downloaded here: DatabindingInSL4.zip (64.44 KB)

Note: these new data binding features are a Silverlight 4 feature only!

  Posted on: Friday, December 04, 2009 12:02:03 AM (Romance Standard Time, UTC+01:00)   |   Comments [0]
         
Gill Cleeren     .net | Silverlight | Silverlight Advent Calendar | sl4     December 2, 2009    

Silverlight is, as you probably know, a web technology. Basically, we have two choices to add Silverlight to a page (be this page ASP.NET, HTML, PHP or what have you):

  • Screen-filling Silverlight application
  • Silverlight “islands”: small piece(s) of Silverlight embedded on a webpage

Silverlight 4 adds a new option for bringing together Silverlight and HTML by means of the WebBrowser control. If you have done WinForms development, you may remember a similar control in that area as well. It basically allows us to display HTML content inside a Silverlight application.

For some scenarios, this is a very interesting addition to the platform. Imagine you are building a Silverlight application that’s actually a migration of an ASP.NET application. To cut costs, you must perform this migration in several parts. Instead of requiring that all the functionality is converted to Silverlight in one go, you can opt to leave some part in ASP.NET for the time being and still integrate it in the Silverlight environment.

As said, the control available for this, is the WebBrowser control.  The following properties are the most important ones when working with the WebBrowser control: 

· Source: gets or sets the URI that should be rendered in the WebBrowser control

· Navigate: specifies the URI that should be loaded in the control (works identical to the Source property)

· NavigateToString: you can also display an on-the-fly generated string of HTML. This can be done using this method.

The WebBrowser control only works when the Silverlight application is running out-of-browser. If we try running it in-browser, we’ll see a rectangle saying that the HTML is disabled.

image

If we’re running this sample out-of-browser and navigate to this very site (www.snowball.be), we’ll see the following:

image

Let’s now take a look at some code to create this.

The following is the declaration of the WebBrowser control itself:

<WebBrowser x:Name="MainWebBrowser" 
            Width="800" Height="600"></WebBrowser>

In the code-behind, in the click event of the Go-Button at the top, we can send the WebBrowser control to the requested page:

private void AddressButton_Click(object sender, RoutedEventArgs e)
{
    if (Application.Current.HasElevatedPermissions)
        MainWebBrowser.Navigate(new Uri(AddressTextBox.Text));
    else
    {
        string localIFrame = 
                "<HTML><HEAD></HEAD><BODY><IFRAME width='100%'      
                height='100%' src='" + AddressTextBox.Text + "' /></BODY></HTML>";
                MainWebBrowser.NavigateToString(localIFrame);
    }
}

As you can see, there are 2 implementations of the navigation. Why is that? When running out-of-browser, the WebBrowser control works, BUT it can only navigate to pages within the same domain as where the Silverlight application is hosted. To navigate to an external page (that is on another domain), the Silverlight application needs elevated permissions: it must be a Trusted Application. ( We’ll look at Trusted applications in a future post). This is reflected by the first check.

Now there’s a little workaround this limitation and that’s adding an IFRAME control manually and hosting the external page in there. This is done in the else-block.

Note: this is a Silverlight 4 feature which only works in Visual Studio 2010!

The sample can be found here.

  Posted on: Thursday, December 03, 2009 12:46:46 AM (Romance Standard Time, UTC+01:00)   |   Comments [1]
         
Gill Cleeren     .net | Silverlight | Silverlight Advent Calendar | sl4     December 1, 2009    

Question: how many times a day do you move things around in your PC environment? Placing a file on your desktop, picking it up and moving or dragging it into a folder is something we all know and do constantly. Also, personally, I often drag a file into a window to trigger the application to do something with it. Often when I’m doing some quick editing, I drag an image onto Photoshop’s desktop icon. Or when I want to send a mail with an attachment, I drag the file into the window. The application gets a handle to the file and knows what to do with it.

Silverlight applications could not be the target of a drag and drop operation. It was possible to drag stuff around inside the Silverlight window, but it was not possible to drag things into the window running Silverlight. Things changed in this area with the arrival of Silverlight 4. At the time of writing, it’s only possible to drag in one file or a list of files, however dragging in a directory is not supported. This feature is available on Windows and Mac.

Any UIElement within Silverlight can now be the target of a drag operation. IThe UIElement class now defines several events to support dragging, namely DragEnter, DragLeave, DragOver and Drop. DragEnter, DragLeave and DragOver can be used for example to highlight a control when content is being dragged over it. The Drop event fires when an object is dropped onto the UIElement. Let’s take a look at a sample.

Imagine we are building an application in which the user can drag and drop images on a canvas. The way the Canvas needs to be declared is as follows:

<Canvas x:Name="DropCanvas" AllowDrop="True" Drop="DropCanvas_Drop" >

In code-behind, we should handle the Drop event to react to a file being dropped on the UIElement.

private void DropCanvas_Drop(object sender, DragEventArgs e)
{
    IDataObject dataObject = e.Data;
    Point dropPoint = e.GetPosition(DropCanvas);
    if (dataObject.GetDataPresent(DataFormats.FileDrop))
    {
        FileInfo[] files = (FileInfo[])dataObject.GetData(DataFormats.FileDrop);
        
        if (files.Length > 0)
        {
            if (files[0].Extension == ".jpg")
            {
                //only take file 0
                System.Windows.Media.Imaging.BitmapImage bitmapImage = 
                    new System.Windows.Media.Imaging.BitmapImage();
                bitmapImage.SetSource(files[0].OpenRead());
                Image newImage = new Image();
                newImage.Source = bitmapImage;
                newImage.Width = 200;
                newImage.Height = 200;
 
                newImage.SetValue(Canvas.TopProperty, dropPoint.Y);
                newImage.SetValue(Canvas.LeftProperty, dropPoint.X);
 
                newImage.Stretch = Stretch.Uniform;
                DropCanvas.Children.Add(newImage);
            }
        }
    }
}

This event defines a DragEventArgs parameter, which defines a Data property, of type IDataObject. We can access the file or files using the GetData method. This method returns an array of FileInfo objects and these files can then be read out. The code for this is shown below.

We check if the file is an image (check for the file extension) and if it is, we create a new BitmapImage, passing in the dropped file. After that, we create a new Image control and set its source to the BitmapImage instance.

Note: this is a Silverlight 4 feature and the sample only works with Visual Studio 2010!

The complete code can be downloaded here: DragAndDrop.zip (29.78 KB)

  Posted on: Wednesday, December 02, 2009 12:42:41 AM (Romance Standard Time, UTC+01:00)   |   Comments [2]
         
Gill Cleeren     .net | Silverlight | Silverlight Advent Calendar | sl4     November 30, 2009    
Here’s the first article of the Silverlight Advent Calendar! We are starting quite easy…
 
While browsing through the new properties and types available in Silverlight, I came across this nice little new property on the TextBlock called TextTrimming. As the word says, it helps you with showing an ellipsis when the text is too wide for the TextBlock you want it to appear in.

Let’s take a look at this property in action. Suppose we have a TextBlock that has a specific width set to it. On this TextBlock, we specify the TextTrimming property to have the value WordEllipsis.

   1: <TextBlock Text="Hello, my name is Gill Cleeren" 
   2: Width="150" TextTrimming="WordEllipsis"></TextBlock>

Without the TextTrimming property, the result is the following. As you can see, the text is cut of where the TextBlock ends.

image

However, with TextTrimming enabled, Silverlight calculates where it should be placing the ellipsis, making the result look much more polished.

image

Note that is a Silverlight 4 only feature, which only works with Visual Studio 2010!

Tomorrow, we’ll be looking at… Oh can’t say, that has to be a surprise until tomorrow! You can’t go peeking in that advent calendar!

  Posted on: Tuesday, December 01, 2009 12:02:45 AM (Romance Standard Time, UTC+01:00)   |   Comments [0]
         
Gill Cleeren     Silverlight | sl4 | Silverlight Advent Calendar     November 30, 2009    

December is a month of festivities, presents and great food. With Christmas just around the corner, the year has been flying by even faster than the previous one.

To go out with a bang (or should I have said bing…), I have decided to come up with something special called “the Silverlight Advent Calendar”. As probably, many of you don’t know what an advent calendar is as it is something only known in some cultures, let’s start with a little explanation I found on Wikipedia:

An Advent calendar is a special calendar which is used to count or celebrate the days of Advent in anticipation of Christmas. Some calendars are strictly religious, whereas others are secular in content.

Today, most advent calendars are made for children. Many take the form of a large rectangular card with many "windows", one of which is opened every day during Advent. In less elaborate calendars, each window opens to reveal an image, a poem, or part of a story such as the Nativity story itself. More elaborate Advent calendars have a small gift concealed in each window, such as a toy or a chocolate item.

Here are some examples:

advent-calendar

2007-11-26-advent

However, in my calendar, there’s no chocolate for every day you open it! I have something much better… Each day, starting December 1st, I will be posting a Silverlight article here on www.snowball.be. That’s right, a free article on Silverlight for every day you stop by! You don’t even have to open a little door or something to get a free gift!

Christmas must be early this year!

Disclaimer: I HOPE to be able to post each day, as December is a very busy month for me…

  Posted on: Monday, November 30, 2009 11:18:21 PM (Romance Standard Time, UTC+01:00)   |   Comments [0]
         
11/28/2014   5:37:29 PM
 Welcome to Snowball.be
Hello and welcome to snowball.be!

My name is Gill Cleeren, I'm a Microsoft Regional Director and an MVP ASP.NET.
On Snowball.be, you'll find all kind news and articles on .net, ASP.NET, WPF, Silverlight and Microsoft in general.
More on me can be found on my about page.

Should you have any questions, don't hesitate to contact me by Send mail to the author(s) .

 Partner sites
 Most popular tags
.net (124) .net 3.0 (6) .net 3.5 (18) .NET 4 (18) .NET Show (1) ADO.net (4) ASP.net (53) ASP.net AJAX (4) ASP.NET MVC (3) Atlas (12) Azure (2) Blend (2) Book (6) Book review (4) C# (43) Case studies (1) Chopsticks (3) Community (10) Community Day (16) Consoles (1) Database (1) DevDays09 (4) DotNetNuke (4) Efficiency (57) Enterprise Library (5) Events (62) Expression (7) Games (3) Hardware (9) Internet (18) IT (1) jQuery (1) LightSwitch (3) Links (11) LINQ (4) Mac (2) Metro (1) Microsoft (75) Mix 07 (6) Mix 08 (4) Mix 09 (1) Mix 11 (1) Movies (4) MVP (5) MVP Summit 2008 (3) mvvm (1) NDCOslo (1) Office 2007 (10) Other (8) PDC (22) PDC2008 (10) Personal (36) ppt (9) Programming (52) Programming tools (22) Regional Director (2) Silverlight (143) Silverlight Advent Calendar (24) sl4 (44) SL5 Data and Services Cookbook (2) Slide decks (13) Snowball (13) Software (20) Microsoft (25) Speaking (15) SQL Server (10) TechDays (13) TechEd (14) telerik (6) Telerik (6) TFS (1) Twitter (1) Vista (73) Vista Tricks (9) Visual Studio.net (38) Visug (33) VS2010 (8) Wallpaper (2) WCF (2) Webcasts (9) Webinars (5) Windows (41) Windows 7 (5) Windows 8 (3) Windows Azure (2) Windows Mobile (3) Windows Phone 7 (2) WinFX (17) WinRT (2) WP7 (2) WPF (40) XAML (24)

 On this page
Silverlight Advent Calendar: December 24th: Browsing Christmas pictures on Flickr
Silverlight Advent Calendar: December 23rd: Merged resource dictionaries
Silverlight Advent Calendar: December 22nd: INotifyDataErrorInfo in the spotlight
Silverlight Advent Calendar: December 21st: Duplex WCF bindings in Silverlight
Silverlight Advent Calendar: December 20th: Learning about the IsolatedStorage
Silverlight Advent Calendar: December 19th: Entering credentials via ClientHttp
Silverlight Advent Calendar: December 18th: IDataErrorInfo in an example to stop Last Christmas by Wham!
Silverlight Advent Calendar: December 17th: Cross-domain network access
Silverlight Advent Calendar: December 16th: The new DataGrid has landed!
Silverlight Advent Calendar: December 15th: Right-clicking in Silverlight 4 to create a context menu
Silverlight Advent Calendar: December 14th: The Local Connection API explained
Silverlight Advent Calendar: now taking requests
Silverlight Advent Calendar: December 13th: Working with Bing from Silverlight
Silverlight Advent Calendar: December 12th: The RichTextArea exposed
Silverlight Advent Calendar: December 11th: Local file access in Silverlight 4
Silverlight Advent Calendar: December 10th: Working with the webcam from Silverlight 4
Silverlight Advent Calendar: December 9th: Element-to-element bindings in Silverlight 3
Silverlight Advent Calendar: December 8th: Working with the clipboard in Silverlight 4
Silverlight Advent Calendar: December 7th: Creating a kiosk application
Silverlight Advent Calendar: December 4th: Replacing converters with new data binding features
Silverlight Advent Calendar: December 3rd: The new Silverlight 4 WebBrowser control
Silverlight Advent Calendar: December 2nd: Drag and drop in Silverlight 4
Silverlight Advent Calendar: December 1st: Text trimming made easy in Silverlight 4
Silverlight Advent Calendar coming up!
 This site
 Archives
Navigation
 Sitemap
 Blogroll OPML
 Disclaimer

All content is property of www.snowball.be. Nothing on this site can be copied or published elsewhere, unless otherwise stated.

This site is made by Gill Cleeren.

Questions? Opinions? Send mail to the author(s) E-mail