Archives

Archives / 2009 / August
  • Cleaning Up the Disabled State of a Silverlight Control using Expression Blend

    Silverlight provides the ultimate in flexibility when it comes to styling controls.  If you don’t like how something looks you can tweak the template in a tool like Expression Blend quickly and easily and get something completely different.  This post is geared towards those of you who may just be getting into Silverlight.  My goal is to demonstrate how easy it is to change controls using a simple example that I just went through on a client application.

    Read more...

  • Creating a Silverlight DataContext Proxy to Simplify Data Binding in Nested Controls

    Data binding is one of my favorite features and something that I use a lot while building Silverlight applications.  Silverlight makes it really easy to bind data to different controls and move data from controls back into the original bound object without having to write any C# or VB code in many cases.  Here’s an example of binding a ComboBox control to an object that defines a property named Branches:

    Read more...

  • Building Line of Business Applications with Silverlight 3 – Pros and Cons

    imageI’ve been hearing a lot of people talk about how Silverlight 3 can or can’t do various things lately throughout the blogosphere and Twitter.  The sad part is that some of the people giving their two cents about what Silverlight can’t do haven’t built a “real world” application so I’m not sure how their opinion carries any weight (I know because I’ve asked a few of them). Their list of cons are typically based on what they heard on Twitter or various rumors floating around.  As with any technology there are pros and cons and Silverlight is no exception.  The goal of this post is to talk through some of the pros and cons I’ve personally encountered while building a “real world”, enterprise-scale timesheet and job management application for a large electrical contracting company.  I’ll point out what has worked really well and what hasn’t so that people looking to move applications to Silverlight 3 can get an honest assessment on what can be done and what can’t be done.

    The application my company is building has a lot of different screens (30+), integrates with the backend database using WCF, uses reporting services for reports and leverages many of the key features Silverlight 3 has to offer.  The existing application used by the client was written using Access Forms and is being ported to Silverlight 3 along with a bunch of new functionality.

    Let’s start by going through what I personally feel are the pros and cons that Silverlight brings to the table for Line of Business (LOB) applications.

    Silverlight 3 Pros

    Here are some of the pros I’ve found as I’ve worked through the application:

    1. Excellent data binding support – my favorite feature.  This changes how you look at building applications and allows you to write data-centric code instead of control-centric code as with normal Web applications.  There’s a lot I could cover here but I wrote up a post awhile back that gives additional details about what I like.  Read it here.
    2. Server code and client code can written in the same language.  Business and validation rules can be shared between the client and server code base which definitely makes applications more maintainable into the future.
    3. A rich set of controls – I really like the controls available with the Silverlight SDK as well as those in the Silverlight toolkit.  There are a few (mentioned below) that can test your patience when you’re building a more involved application and not a simple demo.
    4. Support for just about any type of animation or effect you want.  Don’t need that in a LOB app?  I beg to differ.  You can get quite creative with how data is displayed with Silverlight and not be limited to simple slide, sizing or opacity animations.  I’ll show an example of what I call the “card flow” interface later in this post and show how it allows managers to get to timecards faster and easier than before.
    5. Excellent designer support through Expression Blend 3 – Visual Studio 2008 is great for coding but doesn’t provide any design-time features for Silverlight 3.  Not a big deal….you can use Expression Blend 3 which provides a great interface for designing your applications.  The new SketchFlow application can even be used to prototype applications upfront and get customer feedback more quickly and easily than in the past.
    6. Easy to integrate distributed data using WCF, ASMX, REST, ADO.NET Data Services, .NET RIA Services, etc.  The application that I’m working on leverages WCF as well as some dynamic code on the client-side to make calls to the service.  It works great and FaultExceptions can even be handled now with Silverlight 3.
    7. Desktop-style application with a web deployment model – Get the responsiveness of a desktop application that can be deployed right through the browser and even run on Macs.
    8. Support for styling of controls.  Silverlight 3 now allows styles to be changed on the fly and styles to be based on other styles.  If you don’t like how a control currently looks you can completely change it by using styles and control templates. 
    9. Out of browser support – This was a key reason why my client choose Silverlight 3.  They have a lot of remote workers that don’t always have access to the Internet and needed to be able to enter timesheets even when disconnected and then sync them later once a given employee has connectivity.
    10. Support for behaviorsBehaviors are very, very useful in Silverlight applications.  I needed to add mouse wheel scroll capabilities to a ScrollViewer control to simplify scrolling in a screen and did it within minutes by using a behavior (thanks to Andrea Boschin who created the behavior).
    11. Manipulate pixels using WriteableBitmap.  This can be used to create some very cool effects (some which may not be applicable to LOB applications…but they’re still cool).  I’m using it mainly to fill the printing hole mentioned below.
    12. Support for storing local data through isolated storage – By using isolated storage you can cache data, store data when no network connectivity is available plus more.
    13. Support for navigation – Silverlight 3 has a new navigation application template in Visual Studio that makes creating an application with multiple pages quite easy.  Each “page” can be linked to directly through deep linking and has built-in support for history.  If the client hits the back or forward button in the browser they’re taken to the appropriate Silverlight application page.  The navigation framework allows code to be placed into separate pages (or user controls) and then loaded as a particular link or menu item is clicked.  It’s a great feature that can really reduce the amount of code you have to write especially compared to Silverlight 2.

    Silverlight 3 Cons

    So everything’s all peachy right? Both the client and I are very happy with how things are going with the application but there have been some challenges along the way.  At the beginning of this post I said I’d mention the cons as well and I’m going to be brutally honest here with some of the areas where Silverlight 3 is missing the mark when it comes to Line of Business applications.

    1. No printing support.  Microsoft is working hard on this for future versions of Silverlight but if you can’t wait until then it’s definitely something to be aware of.  We knew about this going into the project so it was supposed to be a non issue since we could pop-up HTML overlays or open new browser windows that could be printed.  But, for one particular screen the lack of printing support is proving to be a challenge since it turns out the end user always prints the screen as they’re doing payroll work and the screen is quite complex.  I’ve come up with a work around using Silverlight 3’s WriteableBitmap but am currently sending bytes up to the server, converting them into a JPEG and then displaying the image in the browser.  Definitely a hack solution.  We’re now looking at generating dynamic Excel files on the fly at the client with a snapshot image of the Silverlight data since Excel will print the image properly and the browser doesn’t (still researching this option). If that doesn’t work we’ll just use reporting services to generate the same screen…not my preference though.
    2. Binding data to controls such as a ComboBox that is nested in other controls is harder than it should be.  I use a lot of ComboBox controls that are nested in DataGrid or ListBox rows in the application.  While I have it working fine now (after beating my head against the monitor a few times), the ComboBox control itself really needs a SelectedValue and ValueMember property for some situations and a way to more easily bind its ItemsSource to collections that may be defined outside of the current DataContext. All of this can be done via code but if you want to handle the majority of it declaratively in XAML it can be challenging in some cases especially once you start breaking parts of a page into user controls.  I ended up enhancing some code Rocky Lhotka blogged about and created my own ComboBox control that simplifies things a lot.
    3. Lack of built-in support for commanding.  What’s “commanding”?  In a nutshell it’s the ability to call a method within an object directly from XAML as an event fires instead of going through a code-behind file.  For example, when a button is clicked the click event can call directly into a ViewModel object that acts on the event.  By using commanding you can make your applications more testable. Frameworks such as Prism (and many others that have popped up) support simple button commanding and provide a code framework that can be used to build other types of commands.  The application I’m working on handles just about every event in the book (click, mouseenter, mouseleave, rowloaded, lostfocus, gotfocus, plus others) though and writing custom code just to handle events got old (so we simplified things and handle the event in the code-behind which notifies the ViewModel via events).  If you want to handle events directly in the code-behind file this is a non-issue for you, but if you want to handle them in another class it can be more challenging than it should be.
    4. Learning curve – There is certainly a learning curve associated with building Silverlight applications especially if you’re coming from building standard Web applications.  However, I think that’s the case with any technology.  I had already built several demo applications with Silverlight, co-authored a book and several articles on it and still ran into a few challenges with architecture choices along the way (we’re following the MVVM pattern).  I think it boils down to experience though so I list this one simply so that people know they will spend some time learning.  If you’re one of those people who doesn’t like to learn new things then Silverlight probably isn’t for you.  If you enjoy learning new things then I think you’ll love Silverlight once you get the hang of it.
    5. No built-in support for displaying Reporting Services reports within Silverlight – This isn’t a huge issue in my opinion since a report can be brought up in a separate browser window or displayed using an HTML overlay.  However, if you need to display the report directly in Silverlight there isn’t any built-in way to do that in Silverlight 3.  3rd party vendors to the rescue though.  Perpetuum Software now has a product that can integrate reports directly into Silverlight.
    6. The client has to have the Silverlight plugin installed.  I personally don’t view this as much of a con for internal enterprise LOB applications since it’s fairly easy for a PC manager to push out Silverlight to client PCs in many environments.  If the application will be run externally then installation of the plugin on each client has to be considered though.
    7. Sharing service types between proxy objects needs to be enhanced - When the project was initially started I wanted to use ADO.NET Data Services for some things and WCF for others.  The problem is that if both types of services reference the same type (such as Customer) then both generated proxy objects will contain a copy of that type which ultimately bloats the XAP size and leads to having to reference types by namespace down in the code versus a using statement at the top.  Although proxies can share code libraries from other projects (that's an option in the proxy generation wizard), my Model entities use .NET language features not available in Silverlight class libraries so type sharing won't work there.  It's something to keep in mind as you decide how you'll get data into your application.

    There are definitely more pros than I’ve listed and probably a few more cons although I honestly can’t think of many more that I’ve had to deal with.  The bottom line is that all of the pieces are there (aside from printing) to build powerful Line of Business applications that are built using existing .NET languages.  You can make the applications look however you’d like and not have to worry if they’ll look good across different browsers.  Here’s a picture of a payroll screen in the early stages of development:


    Makeover


    Here’s what the screen looks like after applying a few styles to the controls and adding support for things like inline editing of data:


    image


    Some of the more “fun” features in Silverlight 3 can also be put to good use in Line of Business applications.  I needed to create a way for warehouse managers to easily manage multiple time cards for employees.  I ended up going with what I call a “card flow” interface (similar to cover flow in iTunes) to display the time cards.  The end user can use the mouse wheel to quickly navigate through different cards.  The selected card will slide to the middle with a cool animation and the others are angled using perspective 3D.  Here’s what the “card flow” interface looks like (I need to give credit to Jose Fajardo for blogging about the concept):

    image

     

    To Wrap It Up

    So is Silverlight 3 ready for prime time, enterprise level Line of Business applications?  Having worked with the framework nearly every day for the past 3 months in a “real world” scenario my short answer is a big “YES”!  I don’t say this because I’ve been drinking the Kool-Aid though.  Anyone who knows me understands that I use what I feel works best for a given application (unless the client wants something specific of course).  I truly enjoy working with the framework and think it can do a lot of powerful things.

    Every company is unique though so the answer really depends on what features your application needs.  Our previous client’s application was built using ASP.NET MVC and jQuery and it did everything they wanted it to do (I really like ASP.NET MVC and jQuery by the way).  However, Silverlight provides a more consistent way to develop enterprise applications that doesn’t require learning JavaScript, additional libraries like jQuery, CSS, HTML and other Web technologies.  With Silverlight you can write code using your favorite .NET language on both the client and server, debug applications like any normal .NET application, bind data in flexible ways, retrieve data from remote services, animate objects as needed, round corners and work with gradients without ever creating a .gif, .jpg or .png and have a ton of controls right at your finger tips.  I’m a big fan.

    Logo

    For more information about onsite, online and video training, mentoring and consulting solutions for .NET, SharePoint or Silverlight please visit http://www.thewahlingroup.com.

    Read more...

  • So What’s a Silverlight Value Converter Anyway?

    Silverlight has an excellent data binding engine that allows data to be bound through XAML or programmatically with code.  My number one reason for using Silverlight on client projects is the data binding support.  Once you get into it and understand how it works it can save a lot of time when building applications.

    When you’re binding data to controls there will be times when the data needs to be modified or tweaked some on the way into a control or as the data leaves a control and goes back to the source property (during a TwoWay binding for example).  Sure, you can always write code to change a given value, but in many cases it’s much easier to write a simple value converter instead that can be re-used.  In this post I’ll walk through creating a value converter and then show the code for a few of the value converters I find myself using fairly frequently.

    Creating a Value Converter

    Let’s look at a simple example of creating a value converter to get started.  Many applications work with DateTime objects but don’t want the time displayed or want the date formatted a specific way.  If you bind a DateTime property to something like a TextBlock in Silverlight you’ll get the standard output based upon the current culture:

    clip_image002[11]

    In the case of a birthday you want to show the date but don’t need the time of course.  You’d want something like the following:

    clip_image002[13]

    Although a separate property could be created in the source object being bound that handles formatting the date, a value converter can be created and re-used over and over anytime a specific date format needs to be created.  To create a value converter you’ll need to add a Silverlight class into your project and implement an interface named IValueConverter (located in the System.Windows.Data namespace).  This interface defines two members including Convert and ConvertBack. Convert is used to modify data as its bound from the source object to the control. ConvertBack works the other way. As a user changes data in a control such as a TextBox the data can be "converted back" to the original data type in the source object by using ConvertBack.

    Both Convert and ConvertBack accept the same parameters including the data being bound, the type of the data being bound, any parameter data passed in that can be used in the data conversion process as well as the target culture that the entire process is running under (English, Spanish, French, etc.). Here's what the method signatures look like:

    object Convert(object value, Type targetType, object parameter, CultureInfo culture);
    object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture);

    Once IValueConverter is added to your class you can right-click it in Visual Studio and select Implement Interface from the menu to automatically fill in the Convert and ConvertBack methods. If you're using Visual Basic you can simply hit enter after the interface name to accomplish the same thing.  Here’s an example of a date value converter that allows dates to be formatted:

    using System;
    using System.Windows;
    using System.Windows.Data;
    using System.Globalization;
    
    namespace View.Converters
    {
       public class DateConverter : IValueConverter
        {
    
            #region IValueConverter Members
    
            //Called when binding from an object property to a control property
            public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
            {
                if (value == null || (DateTime)value == DateTime.MinValue) return null;
                DateTime dt = (DateTime)value;
                return dt.ToString((string)parameter, culture);
            }
    
            //Called with two-way data binding as value is pulled out of control and put back into the property
            public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
            {
                string val = (string)value;
                DateTime outDate;
                if (DateTime.TryParse(val, culture, DateTimeStyles.None, out outDate))
                {
                    return outDate;
                }
                return DependencyProperty.UnsetValue;
    
            }
    
            #endregion
        }
    }


    To use a value converter you’ll need to first reference the class’s namespace (and assembly if the class is in a separate project).  This can be done in the XAML file where the value converter will be used, in App.xaml or in a merged resource dictionary file.  An example of defining a namespace in a resource dictionary file named Styles.xaml is shown next:

    <ResourceDictionary
      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
      xmlns:converters="clr-namespace:View.Converters"
    >
    
    
    
    </ResourceDictionary> 


    Once the namespace is defined you need to define the converter using XAML and give it a key that can be used to reference the value converter (similar to an ID in ASP.NET):

    <converters:DateConverter x:Key="DateConverter" />
    


    Now that the converter is defined you can use it in any data binding that handles a DateTime object using the Binding object’s Converter and ConverterParameter properties.  Looking at the code below you can see that the converter is referenced using the StaticResource keyword (since it’s defined as a resource within your Silverlight project) which locates the appropriate key for the converter.

    <TextBox x:Name="txtBirthday" 
             Text="{Binding Birthday, Mode=TwoWay, Converter={StaticResource DateConverter},ConverterParameter=d}"
             FontFamily="Arial" 
             Width="200" 
             Height="20" 
             Margin="5"  />


    This automatically routes all in and out data binding operations (since it’s a TwoWay binding) through the value converter.  If data is being bound to a control such as a TextBox the parameter passed (“d” in this example) is used to format the DateTime object data.  If data is updated in the control it’ll automatically be converted from a string back into a DateTime object.

    Value Converter Examples


    I have quite a few value converters that I use from time to time in client applications and have listed some of them below.  If you have a favorite one you’re willing to share with others please add a comment to this post with your name, a description of the value converter as well as the code and I’ll update the blog (and give you credit of course) so that a nice library of value converters built-up over time. Update: Shawn Wildermuth let me know that the Silverlight Contrib project has some similar value converters as well as others so check that out as well if you get a chance.

    BoolToVisibilityConverter

    Handles converting boolean values into Visibility enumeration values.  This is useful when controls need to be shown and hidden based upon the value of a boolean property.  The Inverse parameter value can be passed when you want to hide a control when the property being bound is true or show the control when the value is false.

    using System;
    using System.Windows;
    using System.Windows.Data;
    
    namespace JobPlan.View.Converters
    {
        public class BoolToVisibilityConverter : IValueConverter
        {
            #region IValueConverter Members
    
            public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
            {
                if (parameter == null)
                {
                    return ((bool)value == true) ? Visibility.Visible : Visibility.Collapsed;
                }
                else if (parameter.ToString() == "Inverse")
                {
                    return ((bool)value == true) ? Visibility.Collapsed : Visibility.Visible;
                }
                return false;
            }
    
            public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
            {
                throw new NotImplementedException();
            }
    
            #endregion
        }
    }


    DecimalFormatterConverter
     

    Converts decimal values into different formats.

    using System;
    using System.Windows.Data;
    
    namespace JobPlan.View.Converters
    {
        public class DecimalFormatterConverter : IValueConverter
        {
    
            #region IValueConverter Members
    
            public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
            {
                string format = (parameter == null) ? "#.##" : parameter.ToString();
                return (value == null) ? String.Empty : decimal.Parse(value.ToString()).ToString(format);
            }
    
            public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
            {
                try
                {
                    if (value != null && value.ToString() != String.Empty)
                    {
                        return decimal.Parse(value.ToString());
                    }
                }
                catch { }
                return null;
            }
    
            #endregion
        }
    }


    ListCountVisibilityConverter

    Handles showing and hiding controls such as ListBox based upon the number of items in the collection the control is bound to.  For example if there is only one item in a collection you may not want to show a ListBox control due to the data being shown with other controls in the UI.  You could pass a value of 1 for the ConverterParameter and the ListBox would automatically be hidden.

    using System;
    using System.Windows;
    using System.Windows.Data;
    using System.Collections;
    
    namespace JobPlan.View.Converters
    {
        public class ListCountVisibilityConverter : IValueConverter
        {
    
            #region IValueConverter Members
    
            public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
            {
                if (value != null)
                {
                    IList list = value as IList;
                    if (list != null)
                    {
                        int minCount = int.Parse(parameter.ToString());
                        return (list.Count > minCount) ? Visibility.Visible : Visibility.Collapsed;
                    }
                    
                }
                return Visibility.Collapsed;
            }
    
            public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
            {
                throw new NotImplementedException();
            }
    
            #endregion
        }
    }


    NullToVisibilityConverter

    Handles showing and hiding a control based upon a value being null or not.

    using System;
    using System.Windows;
    using System.Windows.Data;
    
    namespace JobPlan.View.Converters
    {
        public class NullToVisibilityConverter : IValueConverter
        {
    
            #region IValueConverter Members
    
            public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
            {
                if (parameter == null) //Not invert parameter passed
                {
                    return (value == null) ? Visibility.Collapsed : Visibility.Visible;
                }
                else if (parameter.ToString() == "Inverse")
                {
                    return (value == null) ? Visibility.Visible : Visibility.Collapsed;
                }
                return Visibility.Collapsed;
            }
    
            public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
            {
                throw new NotImplementedException();
            }
    
            #endregion
        }
    }


    StringTruncateConverter

    Trims a string down to a specific length for display in the user interface.

    using System;
    using System.Windows.Data;
    
    namespace JobPlan.View.Converters
    {
        public class StringTruncateConverter : IValueConverter
        {
    
            #region IValueConverter Members
    
            public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
            {
                int maxLength;
                if (int.TryParse(parameter.ToString(), out maxLength))
                {
                    string val = (value == null) ? null : value.ToString();
                    if (val != null && val.Length > maxLength)
                    {
                        return val.Substring(0, maxLength) + "..";
                    }
                }
                return value;
            }
    
            public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
            {
                throw new NotImplementedException();
            }
    
            #endregion
        }
    }

    Read more...

comments powered by Disqus