IValueConverters: A great tool in developing WPF/Silverlight applications

IValueConverters in WPF/Silverlight are a very powerful tool in UI presentation, especially, when following the MVVM pattern. In MVVM, where everything in the View is bounded to some property from ViewModel, IValueConverters come handy because often the properties in the ViewModel do not necessarily return the same data type that the View needs. For instance, we can have a boolean IsVisible property in the ViewModel but the UI controls need a Visibility enumeration (with values Visibility.Collapsed and Visibility.Visible) to properly consume it. The great thing about IValueConverters is that they take an object and return an object, so its upto the innovation of developer to make the most out of them.

In this post, I will be demonstrating the powers of IValueConverters by representing a simple boolean variable, that has just two possible values, in several ways. Here’s a screenshot from the application. The bindings in the left column are two way, thus the change in any one of the controls is immediately reflected in the whole view. Have a look:

ivalueconvertersdemo

The ViewModel class exposes only one boolean property to the View. Here’s the code:

public class SampleViewModel : System.ComponentModel.INotifyPropertyChanged
{
    bool boolProperty;

    public bool BoolProperty
    {
        get { return boolProperty; }
        set { boolProperty = value; RaisePropertyChanged("BoolProperty"); }
    }

    public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged;
    private void RaisePropertyChanged(string propertyName)
    {
        if (PropertyChanged != null)
            PropertyChanged(this, new System.ComponentModel.PropertyChangedEventArgs(propertyName));
    }

}

The View consumes the boolean property using:

  • Default Boolean To String Converter (with Values = True, False)
  • Boolean to String Converter (with values = Yes, No)
  • Boolean to String Converter (with values = Start, Stop)
  • Boolean to Brush Converter (with colors Green, Red)
  • Boolean to Opacity Converter (with opacity 100%, 10%)
  • Boolean to FontStyle Converter (with styles Normal, Italic)
  • Boolean to Visibility Converter

Here’s the code for the View:

<UserControl x:Class="IValueConvertersDemo.View.SampleView"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:vm="clr-namespace:IValueConvertersDemo.ViewModels"
    xmlns:converter="clr-namespace:IValueConvertersDemo.Converters"
    >
    <UserControl.Resources>
        <vm:SampleViewModel x:Key="ViewModel" BoolProperty="True" />
        <converter:BoolToColorConverter x:Key="BoolToColorConverter" />
        <converter:BoolToVisibilityConverter x:Key="BoolToVisibilityConverter" />
        <converter:BoolToStringConverter x:Key="BoolToStringConverter" />
        <converter:BoolToDoubleConverter x:Key="BoolToDoubleConverter" />
        <converter:BoolToFontStyleConverter x:Key="BoolToFontStyleConverter" />
    </UserControl.Resources>
    <Grid x:Name="LayoutRoot" DataContext="{StaticResource ViewModel}" Background="White">
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="250" />
            <ColumnDefinition />
        </Grid.ColumnDefinitions>

        <StackPanel Grid.Column="0" Margin="10" HorizontalAlignment="Left">
            <TextBlock Margin="10" FontSize="18" Text="Two-way" />
            <CheckBox Margin="10" Content="BoolProperty" IsChecked="{Binding BoolProperty, Mode=TwoWay}" />
            <TextBox Margin="10" Width="100" Text="{Binding BoolProperty, Mode=TwoWay}" />
            <TextBox Margin="10" Width="100" Text="{Binding BoolProperty, Mode=TwoWay, Converter={StaticResource BoolToStringConverter}, ConverterParameter='Yes,No'}" />
            <TextBox Margin="10" Width="100" Text="{Binding BoolProperty, Mode=TwoWay, Converter={StaticResource BoolToStringConverter}, ConverterParameter='Start,Stop'}" />
        </StackPanel>

        <StackPanel Grid.Column="1"  Margin="10">
            <TextBlock Margin="10" FontSize="18" Text="One-way" />
            <StackPanel Margin="10" Orientation="Horizontal">
                <Ellipse Height="50" Width="50" Fill="{Binding BoolProperty, Converter={StaticResource BoolToColorConverter}}" HorizontalAlignment="Left" />
                <TextBlock Margin="5" VerticalAlignment="Center" Text="Color(Green/Red) depending on bool property" />
            </StackPanel>
            <StackPanel Margin="10" Orientation="Horizontal">
                <Rectangle Fill="Blue" Height="50" Width="50" Opacity="{Binding BoolProperty, Converter={StaticResource BoolToDoubleConverter}}" HorizontalAlignment="Left" />
                <TextBlock Margin="5" VerticalAlignment="Center" Text="Opacity(100%/10%) depending on bool property" />
            </StackPanel>
            <TextBlock Margin="10" Text="Fontstyle(Normal/Italic) depending on bool property" FontStyle="{Binding BoolProperty, Converter={StaticResource BoolToFontStyleConverter}}" />
            <TextBlock Margin="10" Text="This text is visible only if bool value is true" Visibility="{Binding BoolProperty, Converter={StaticResource BoolToVisibilityConverter}}" />
        </StackPanel>

    </Grid>
</UserControl>

Notice that a boolean variable can only be assigned two values: True and False. Yet, still, we are able to use that in several different ways. Thus IValueConverters provide endless possibilities in WPF data presentation. Its only upto the brain of developer how to exploit them.

The sourcecode for the various converters is included with the sample application that can be downloaded here. Make sure you rename the file to .zip for extraction.

Beginning MVVM: The Basics

WPF and Silverlight experts talk a lot about the new Model-View-ViewModel pattern these days. The powerful databinding support in WPF framework provides the basis for MVVM pattern. In this post, I will be highlighting some points that beginners may need to know to start learning MVVM.

To start with MVVM, you must know the basics of WPF/Silverlight databinding and how to data-bind controls using XAML constructs. The thing to note is that if you data-bind a UI control to an object (using the DataContext dependency property), then all of the public properties of that data-object are available to the child controls. In MVVM, the view (a UI control/page) is data-bound to a ViewModel (a simple C# class). The ViewModel exposes all the data and commands the View needs via properties. The View, then declaratively binds its UI controls to corresponding properties and commands of the ViewModel via XAML constructs.

As concepts are more clarified through examples, I am presenting a very simple example. Lets build a Silverlight MVVM version for this UI:
mvvm-picture1

The Listbox presents a list of persons and the right section presents the details of a selected person. To start with MVVM, we will make a ViewModel class containing two properties: a PersonList property that will be providing list of persons for the Listbox, and a SelectedPerson property that will contain the reference to the selected person. Here’s a crud implementation for the class:

public class PersonViewModel : System.ComponentModel.INotifyPropertyChanged
{
    ObservableCollection<Person> personList;
    Person selectedPerson;

    public PersonViewModel()
    {
        //populate some sample data
        personList = new ObservableCollection<Person>()
        {
            new Person(){Name="Syed Mehroz Alam", Age=10, City="Karachi", Country="Pakistan"},
            new Person(){Name="Zinedine Zidane", Age=20, City="Marseille", Country="France"},
            new Person(){Name="Ronaldinho", Age=30, City="Porto Alegre", Country="Brazil"},
            new Person(){Name="John Smith", Age=40, City="Washington", Country="USA"}
        };
    }

    #region Properties

    public ObservableCollection<Person> PersonList
    {
        get { return personList; }
    }

    public Person SelectedPerson
    {
        get { return selectedPerson; }
        set
        {
            selectedPerson = value;
            RaisePropertyChanged("SelectedPerson");
        }
    }

    #endregion

    #region INotifyPropertyChanged Members

    public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged;
    private void RaisePropertyChanged(string propertyName)
    {
        if (PropertyChanged != null)
            PropertyChanged(this, new System.ComponentModel.PropertyChangedEventArgs(propertyName));
    }

    #endregion
}

Now we have this View-Model ready to be consumed by a View. So in a new UserControl, we will instantiate the ViewModel class through XAML in the resources section and will data-bind the View to it using DataContext property of the main control of the View, in our case, a Grid named LayoutRoot. Since the LayoutRoot is data-bound to the ViewModel class, we can use its public properties as the DataSources of our other controls in the View. For our example, we will assign Listbox’s ItemSource to ViewModel.PersonList property and Listbox’s SelectedItem to ViewModel.SelectedPerson property. The right section will be bound to the SelectedPerson and will contain TextBoxes for displaying details of that person. Here’s the XAML code:

<UserControl x:Class="MVVMExample1.View.PersonView"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:viewmodel="clr-namespace:MVVMExample1.ViewModel"
    >
    <UserControl.Resources>
        <viewmodel:PersonViewModel x:Key="ViewModel" />
    </UserControl.Resources>

    <!-- Databind the root control to the ViewModel class -->
    <!-- This way, all the public properties of ViewModel class become available to us -->
    <Grid x:Name="LayoutRoot" Background="White" DataContext="{StaticResource ViewModel}">
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="250" />
            <ColumnDefinition Width="*" />
        </Grid.ColumnDefinitions>

        <!-- The listbox gets its ItemSource from ViewModel.PersonList property -->
        <!-- Also, the SelectedItem is bound to ViewModel.SelectedPerson property -->
        <ListBox Grid.Column="0" Margin="5"
                 ItemsSource="{Binding PersonList}"
                 DisplayMemberPath="Name"
                 SelectedItem="{Binding SelectedPerson, Mode=TwoWay}"
                 />

        <!-- Databind the right section to ViewModel.SelectedPerson property -->
        <Grid x:Name="PersonDetails" Grid.Column="1" DataContext="{Binding SelectedPerson}" Margin="5" >
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="100" />
            <ColumnDefinition Width="150" />
        </Grid.ColumnDefinitions>
            <Grid.RowDefinitions>
                <RowDefinition Height="30" />
                <RowDefinition Height="20" />
                <RowDefinition Height="20" />
                <RowDefinition Height="20" />
                <RowDefinition Height="20" />
            </Grid.RowDefinitions>
            <TextBlock Grid.Row="0" Grid.ColumnSpan="2" Text="Person Details" FontSize="15" />

            <TextBlock Grid.Row="1" Grid.Column="0" Text="Name" />
            <TextBox Grid.Row="1" Grid.Column="1" Text="{Binding Name, Mode=TwoWay}" />

            <TextBlock Grid.Row="2" Grid.Column="0" Text="Age" />
            <TextBox Grid.Row="2" Grid.Column="1" Text="{Binding Age, Mode=TwoWay}" />

            <TextBlock Grid.Row="3" Grid.Column="0" Text="City" />
            <TextBox Grid.Row="3" Grid.Column="1" Text="{Binding City, Mode=TwoWay}" />

            <TextBlock Grid.Row="4" Grid.Column="0" Text="Country" />
            <TextBox Grid.Row="4" Grid.Column="1" Text="{Binding Country, Mode=TwoWay}" />
        </Grid>

    </Grid>
</UserControl>

That’s all, our sample application is ready to be run. Observe how selecting a person updates the person details section. Also observe that the databindings are TwoWay and changing the name of a person in the details section is reflected in the ListBox panel as well.

Note that in Silverlight, we do not have built-in support for commands and triggers like WPF. So in order to execute some code upon a button click, we will either need manual hooking of the button’s Click event, a separate third party extension(e.g. SLExtensions, Prism or SilverlightFX) or make our own implementation. More on this in some later post.

Notice that MVVM seems difficult at first, but as soon as you create your first UI using this pattern, you will find yourself thinking your application in terms of Dependency Properties and IValueConverters. Beware, MVVM is quite addictive. Karl Shifflett presents some great MVVM resources on this page. Be sure to bookmark that page for new MVVM stuff.

The source code for the example UI we discussed can be downloaded here. The file needs to be renamed to .zip for extraction. This is “WordPress File Download Pattern” :)

Saving data-objects to Isolated Storage in Silverlight

If you are developing an enterprise Silverlight application you will typically need to cache your business entities in the isolated storage. There are two apparent choices for this:

1. Use the powerful Linq-To-XML API from System.Xml.Linq namespace. This method is to be used when you want to have a fine grain control over serialization (e.g. you want to store some properties of the data objects).

2. Use the built-in DataContractSerializer class. This method is very simple and the only thing it requires is that our business objects need to have the [DataContract] and [DataMember] attributes. In this post, I am going to discuss this second method.

In an enterprise LOB (line of business) application, our business objects will typically be retrieved via WCF service and hence will already have the [DataContract] and [DataMember] attributes marked on them. So we can directly use the built-in DataContractSerializer to store them to, and later retrieve them from, the isolated storage. Here’s a generic class to simplify the process:

public static class IsolatedStorageCacheManager<T>
{
    public static void Store(string filename, T obj)
    {
        IsolatedStorageFile appStore = IsolatedStorageFile.GetUserStoreForApplication();
        using (IsolatedStorageFileStream fileStream = appStore.OpenFile(filename, FileMode.Create))
        {
            DataContractSerializer serializer = new DataContractSerializer(typeof(T));
            serializer.WriteObject(fileStream, obj);
        }
    }
    public static T Retrieve(string filename)
    {
        T obj = default(T);
        IsolatedStorageFile appStore = IsolatedStorageFile.GetUserStoreForApplication();
        if (appStore.FileExists(filename))
        {
            using (IsolatedStorageFileStream fileStream = appStore.OpenFile(filename, FileMode.Open))
            {
                DataContractSerializer serializer = new DataContractSerializer(typeof(T));
                obj = (T)serializer.ReadObject(fileStream);
            }
        }
        return obj;
    }
}

Using the above code is really simple. Just add the class to your project and use it like this:

//for storing and retrieving a single object
Person myPersonObj = GetPerson();
IsolatedStorageCacheManager<Person>.Store("myfile.xml", myPersonObj);
Person myPersonObj2 = IsolatedStorageCacheManager<Person>.Retrieve("myfile.xml");

//for storing and retrieving a collection of objects
List<Person> myPersonList = GetPersonList();
IsolatedStorageCacheManager<List<Person>>.Store("myfile.xml", myPersonList);
List<Person> myPersonList2 = IsolatedStorageCacheManager<List<Person>>.Retrieve("myfile.xml");

Hope you will find this implementation useful.

Deploying a Silverlight Application and WCF Service to IIS

While deploying a Silverlight application on IIS today, I learned several new things. Let me express my observations; This post is going to describe the security settings for WCF service in web.config. To start, I assume that you are using either Windows or Forms authentication and denying all the anonymous users as described in a previous post.

First, make sure to remove the mexHttpBinding endpoint as this requires you to enable anonymous access to the website in IIS. The mexHttpBinding endpoint will look something like:

<endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange"/>

 
BasicHttpBinding

Typically, in your application, you will also be using some custom basicHttpBinding configuration for your WCF service endpoint, like this one:

<endpoint address="" binding="basicHttpBinding"
 contract="SilverlightApplication.Web.WCFService"
 bindingConfiguration="myCustomBasicHttpBinding" />

Using a custom binding allows you to configure buffers and quotas as described in this post. The definition of this custom binding will look something like:

<bindings>
 <basicHttpBinding>
  <binding name="myCustomBasicHttpBinding">
  <security mode="TransportCredentialOnly">
   <transport clientCredentialType="None"/>
  </security>
  </binding>
 </basicHttpBinding>
</bindings>

The <transport clientCredentialType=”None”/> is the main point of interest here. If you are using Forms Authentication, you need to allow anonymous access through IIS and set the clientCredentialType to None. If you want to run your application under Windows Authentication, you should use clientCredentialType="xxxx" where xxxx is the corresponding IIS authentication type. So, to work with Integrated Windows/Basic/Digest/NTLM Authentication, the xxxx should be replaced by Windows/Basic/Digest/Ntlm respectively.

 

Update: Custom Binary Binding

Similar change is required for custom binary binding:

<customBinding>
 <binding name="myCustomBinaryBinding">
  <binaryMessageEncoding >
   <readerQuotas ... />
  </binaryMessageEncoding>
  <httpTransport authenticationScheme="Anonymous" ... />
 </binding>
</customBinding>

Again, the point of interest is the authenticationScheme attribute in the httpTransport element that needs to match the IIS authentication setting (e.g. Anonymous for Forms Authentication, Ntlm/Basic/Digest for Windows Authentication).

This way, the WCF service should work without any problems.

Retrieving huge amount of data from WCF service in Silverlight application

Today, I was trying to figure out why my WCF service call was always throwing the generic NotFound exception when trying to retrieve large datasets. I had all the buffer limits set to 2147483647 (int.MaxValue) at Silverlight service configuration file as well as WCF service configuration section under web.config. Some more analysis revealed that I was getting a System.Net.WebException, saying: The underlying connection was closed: The connection was closed unexpectedly. After some research, I found that I need to set the maxItemsInObjectGraph for dataContractSerializer to some higher value in my web.config.

So, if you are trapped in a similar situation, here are two steps to ensure that you can retrieve large amount of data from WCF service:

1. Enable Silverlight client to retrieve huge chunks of data by enabling large buffers. You need to increase buffer and message size limits for the binding inside ServiceReferences.ClientConfig with something like:

<system.serviceModel>
    <bindings>
        <basicHttpBinding>
            <binding name="BasicHttpBinding_SilverlightWCFService"
                maxBufferSize="2147483647"
                maxReceivedMessageSize="2147483647">
                <security mode="None" />
            </binding>
        </basicHttpBinding>
    </bindings>
    <client>
        <endpoint address="http://localhost:1252/SilverlightWCFService.svc"
            binding="basicHttpBinding" bindingConfiguration="BasicHttpBinding_SilverlightWCFService"
            contract="SilverlightWCFService.SilverlightWCFService" name="BasicHttpBinding_SilverlightWCFService" />
    </client>
</system.serviceModel>

2. Enable WCF service to send large amount of data. You need to set the binding buffer limits as well as tje DataContractSerializer’s maxItemsInObjectGraph value. Here’s an extract from web.config with all the limits set to maximum:

<system.serviceModel>
    <bindings>
        <basicHttpBinding>
            <!-- Create a custom binding for our service to enable sending large amount of data -->
            <binding name="MyBasicHttpBinding"
                maxBufferPoolSize="2147483647"
                maxReceivedMessageSize="2147483647"
                maxBufferSize="2147483647">
                <readerQuotas
                    maxArrayLength="2147483647"
                    maxBytesPerRead="2147483647"
                    maxDepth="2147483647"
                    maxNameTableCharCount="2147483647"
                    maxStringContentLength="2147483647" />
            </binding>
        </basicHttpBinding>
    </bindings>

    <behaviors>
        <serviceBehaviors>
            <!-- Enable the serializer to serialize greater number of records -->
            <behavior name="SilverlightWCFLargeDataApplication.Web.SilverlightWCFServiceBehavior">
                <serviceMetadata httpGetEnabled="true"/>
                <serviceDebug includeExceptionDetailInFaults="false"/>
                <dataContractSerializer maxItemsInObjectGraph="2147483647"/>
            </behavior>
        </serviceBehaviors>
    </behaviors>

    <serviceHostingEnvironment aspNetCompatibilityEnabled="false"/>
    <services>
        <!-- Bind the WCF service to our custom binding -->
        <service behaviorConfiguration="SilverlightWCFLargeDataApplication.Web.SilverlightWCFServiceBehavior"
                name="SilverlightWCFLargeDataApplication.Web.SilverlightWCFService">
            <endpoint address="" binding="basicHttpBinding"
                bindingConfiguration="MyBasicHttpBinding"
                contract="SilverlightWCFLargeDataApplication.Web.SilverlightWCFService"/>
            <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange"/>
        </service>
    </services>
</system.serviceModel>

That was all related to sending greater amount of data from WCF service to Silverlight application. However, if you want to send large amount of data from Silverlight to WCF service, you need one more step as you can reach the maximum permitted http request length limit (this is 4MB by default). This again can be solved by tweaking the web.config. E.g. to allow 10MB data, you need something like:

  <httpRuntime maxRequestLength="10240" />

That’s all. Let’s enjoy developing LOB applications with this great platform.

IEnumerable.ToObservableCollection

In WPF/Silverlight, binding UI objects such as DataGrid or ListBox to collections is typically done using an ObservableCollection instead of the generic List object. This way, our UI is automatically synchronized since the observable collection provides event notification to WPF data binding engine whenever items are added, removed, or when the whole list is refreshed. The LINQ extension methods that return a collection actually return IEnumerable<T>. The .NET framework for Silverlight provides built-in extension methods to convert IEnumerable<T> to List<T> and Array<T> but there’s no method available to convert the collection to ObservableCollection<T>(WPF developers can simply use this constructor overload) . So here’s one you may find useful:

public static class CollectionExtensions
{
    public static ObservableCollection<T> ToObservableCollection<T>(this IEnumerable<T> enumerableList)
    {
        if (enumerableList != null)
        {
            //create an emtpy observable collection object
            var observableCollection = new ObservableCollection<T>();

            //loop through all the records and add to observable collection object
            foreach (var item in enumerableList)
                observableCollection.Add(item);

            //return the populated observable collection
            return observableCollection;
        }
        return null;
    }
}

Extension methods are very powerful and I am planning to post an example demonstrating their potential.

Securing Silverlight Application and WCF Service using ASP.NET Authentication Techniques

Security is an issue that experts are discussing since the birth of Silverlight. A typical enterprise Silverlight application will consist of one or more Silverlight pages communicating with one or more WCF services. There are three major security concerns here:

  1. Silverlight 2 supports communication with a WCF service in simple text (the basicHttpBinding), so anyone can use a packet sniffer (e.g. Fiddler) to read our data.
  2. Anyone can access our WCF service, call its methods and get our data.
  3. Anyone can download our Silverlight application (the .xap file), open it (since it is simple a zip file), extract the DLLs and use Reflector to read all our code.

The first problem can be solved by securing the transmission via transport security (using https protocol). More on this can be found at MSDN here. In this post, I will try to address the last two issues.

The good thing is that the Silverlight application and WCF service is hosted inside an ASP.NET website and luckily ASP.NET provides good tools around authentication so we can apply ASP.NET authentication techniques to secure those. The approach I like is to secure the entire ASP.NET web application using either Windows or Forms authentication and deny all anonymous users. This can be done by configuring the web.config as:

  <authentication mode="Windows"/> <!-- or Forms -->
  <authorization>
    <deny users="?"/>
  </authorization>

This way, if anyone tries to access the WCF service, or download the Silverlight .xap file, or view a page inside the ASP.NET website, the ASP.NET engine authenticates the request and only authorized users are able to view the application or use the service.

So now, if our application is configured to use Windows authentication, the ASP.NET engine authenticates the request via integrated windows authentication. If it succeeds, users are automatically redirected to the Silverlight application; otherwise they get a HTTP 401.1 – Unauthorized message.

And, if our application is configured to use Forms authentication, the ASP.NET engine takes the user to an aspx login page. Once the user is validated (we can use either ASP.NET built-in authentication or any custom implementation to authenticate the user), he/she is redirected to the Silverlight application.

To observe this, you can download this application (Be sure to first rename the file to zip; this is a WordPress requirement) and toggle its authentication technique between Windows and Forms using web.config.

Note that the application also demonstrates how to get the logged in user in Silverlight using a WCF service call. The key is that we can use System.Web.HttpContext.Current.User to get the current user principal if the WCF service is running in ASP.NET compatibility mode.


Screenshot of demo application

At this point, we have made sure that our application and WCF service is only accessible to authorized users. But the problem still exists to a small extent, although narrowed down to authorized users instead of general public. To further secure our application, we need to use some .NET obfuscater. This will ensure that no one, including authorized users, will be able to decompile our code using .NET reflector. And, to further enhance our WCF service security, we need to implement declarative or imperative security checks for our service methods. As with typical ASP.NET applications, we may need a custom implementation of IPrincipal and IIdentity for securing the WCF service using declarive security attributes.

That’s all. Let me know what you do think in the comments section below.

Follow

Get every new post delivered to your Inbox.

Join 55 other followers