KnownColors and Brushes

When transitioning from WinForms development to Silverlight, I found the lack of the KnownColors class to be a bit of a minor setback. After toughing it out for a while, looking up colours and coding new SolidColorBrush(Color.FromArgb(0xFF, 0xF5, 0xF5, 0xF5)) instead of Brushes.WhiteSmoke or new SolidColorBrush(Color.FromArgb(0xFF, 0×8B, 0×00, 0×00)) rather than Brushes.DarkRed, I decided to encapsulate it and make my life a little easier. Here then, is the Brushes class along with the KnownColor enumeration.

View the Demo

Download the source

KnownColor enumeration

The list of colours was obtained by reflecting (http://www.red-gate.com/products/reflector/) on the System.Drawing.KnownColor enumeration. Now this defines system colours as well, but that’s alright - we’ll take it all and just use the necessary subset in our Brushes class.

    public enum KnownColor
    {
        ActiveBorder            = 1,
        ActiveCaption           = 2,
        ActiveCaptionText       = 3,
        AliceBlue               = 0x1c,
        ...
        WindowFrame             = 0x19,
        WindowText              = 0x1a,
        Yellow                  = 0xa6,
        YellowGreen             = 0xa7
    }
 

Brushes class

To represent the Brushes, we use a static class with static properties and a static colour table. The table is a Dictionary that maps our KnownColor enumerations to SolidColorBrushes.

        private static readonly Dictionary<KnownColor, SolidColorBrush> _table = new Dictionary<KnownColor, SolidColorBrush>();
 

Each Brush is instantiated only as required. When requested, a lookup is first done in the dictionary to see if it’s already there. If it is, it’s simply returned. Otherwise, it’s created and stored in the dictionary, and then it’s returned.

        public static Brush AliceBlue
        {
            get
            {
                if (!_table.ContainsKey(KnownColor.AliceBlue))
                    _table[KnownColor.AliceBlue] = new SolidColorBrush(Color.FromArgb(0xFF, 0xF0, 0xF8, 0xFF));

                return _table[KnownColor.AliceBlue];
            }
        }

Seeing all the colours

Being a bit lazy, I didn’t want to hard-code the table of colour names to actual colours, so the code and markup in Page.xaml and Page.xaml.cs build the table by iterating the enumeration.

   foreach (PropertyInfo propertyInfo in (typeof(Brushes)).GetProperties())
      this.ColorPanel.Children.Add(MakeColorPanel(propertyInfo));
 

The GetProperties() method returns a list of PropertyInfo objects, one for each of the static SolidColorBrushes defined in the class. I created a horizontal StackPanel that contains a TextBlock with the Brush’s name (i.e.: its colour), and a Canvas to display the colour itself.

image

Putting all StackPanels into a WrapPanel, we can see a screen full of colour and their corresponding names. Some of the lighter colours can be a little difficult to see on the default White background, so there’s a Toggle background colour button that switches the WrapPanel’s Background property between Brushes.Black and Brushes.White. When the background is changed, each TextBlock’s colour is set to the ‘opposite’ colour so it’s still readable — that is, black on white or white on black. Check out the demo link above.

Note: In order to use the WrapPanel, you must get the Silverlight Toolkit. I have included a compiled version in the demo project, but you can find the most up-to-date version on Codeplex here.

Wrapup

To use:

  1. Add a reference to the Silverlight Toolkit dll (Microsoft.Windows.Controls.dll)
  2. Add KnownColor.cs and Brushes.cs to your project
  3. Add a using statement to reference the WorkSight.Silverlight.Controls namespace

    or

    Change the namespace in the 2 source files to match your own

The code is not fancy or complex — it’ll just save you a little time. I hope you find it useful.

Closable TabItem

Note: The source for this project has been updated to work with Silverlight 2.


TabItems can be added to a TabControl and, with code, can be removed as well. However, unlike many (most?) tabbed interfaces (FireFox, IE7+), tabs cannot be closed by a user - unless of course you add a mechanism to let them do so. The control described in this article provides a visual way of doing that.

View the Demo

Download the source

Main Layout

We’ll start by laying out our screen. It follows the standard format of list on the left, grid splitter, content on the right.

image

The list is a MainMenu user control (included) that uses a ListBox for its contents. For the purposes of this demo, the menu is hard-coded. It’s also possible to dynamically load it based on the contents of a Web.sitemap.

        <ListBox x:Name="mnu" Width="Auto" Height="Auto" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" >
            <ListBox.ItemTemplate>
                <DataTemplate>
                    <StackPanel Orientation="Horizontal" Width="Auto" Height="20" Margin="2" MouseLeftButtonDown="OnItemMouseLeftButtonDown">
                        <Image Source="{Binding Icon}" Margin="2" Width="16" Height="16" />
                        <TextBlock Text="{Binding Name}" VerticalAlignment="Center" HorizontalAlignment="Stretch" FontSize="11" Height="20" />
                    </StackPanel>
                </DataTemplate>
            </ListBox.ItemTemplate>
        </ListBox>

The DataTemplate has been modified so an icon can be displayed in an Image next to a TextBlock that contains the menu text itself. Rounding out the MenuEntry class is the handler.

        public class MenuEntry
        {
            public string               Name    { get; set; }
            public string               Icon    { get; set; }
            public RoutedEventHandler   Handler { get; set; }
        }

Loading a Panel

Note the MouseLeftButtonDown event of the MainMenu is wired up so when the ListBoxItem is clicked, the MenuEntry.Handler is called back. This calls a LoadPanel method on the main Page instance.

        public void             LoadPanel(string header, UserControl panel)
        {
            if (this.IsAlreadyLoaded(header))
                return;
            ClosableTabItem tabItem         = new ClosableTabItem();
            tabItem.Close                   += OnClosePanel;
            tabItem.Header                  = header;
            tabItem.Content                 = panel;
            this.pnlContent.Items.Add(tabItem);
            tabItem.IsSelected              = true;
        }

Because we don’t want to load the same panel more than once, the IsAlreadyLoaded method is called. It simply compares the text of all TabItem headers with the text of the header about to be displayed. If there’s a match, just select the existing tab and return.

If the tab isn’t loaded yet, we create a new ClosableTabItem, set its header and its content, and select it.

Subscribe to the Close event if you want to be called back when the user clicks on the close button.

image

ClosableTabItem

While it was possible to create a ClosableTabItem from scratch, I decided to leverage the existing TabItem. The only enhanced functionality I’m interested in adding is a visual element (check image for the user to click) and the callback handler.

        public class ClosableTabItem : TabItem

Constructing the ClosableTabItem

There are two overloaded constructors for the ClosableTabItem:

  1. No parameters - default to closable
  2. Specify if it can be closed or not

You might wonder why I would want to create a ClosableTabItem that cannot be closed - why not just use a TabItem? After all, one inherits from the other. It’s because of the styling. I wanted to change the look of TabItems so they matched ClosableTabItems. See the Styling the Controls section below to see how I was able to do that.

To build the new header, I wanted to keep the text portion and add a button. The button should be aligned to the right.

image

I created a two-column grid, adding a TextBlock in the first column and a Button in the second. All styling is done in generic.xaml.

        Grid                    grid        = new Grid();
        grid.ColumnDefinitions.Add(new ColumnDefinition());
        grid.ColumnDefinitions.Add(new ColumnDefinition());
        TextBlock               textBlock   = new TextBlock();
        textBlock.Text                      = (string) newHeader;
        textBlock.SetValue(Grid.ColumnProperty, 0);
        grid.Children.Add(textBlock);
        Button                  button      = new Button();
        button.Template                     = ResourceManager.GetControlTemplate("ClosableButtonTemplate");
        button.Click                        += OnClose;
        button.SetValue(Grid.ColumnProperty, 1);
        grid.Children.Add(button);
        base.OnHeaderChanged(oldHeader, grid);

image

When a TabItem’s header is changed, the method OnHeaderChanged(object oldHeader, object newHeader) that can be overridden is called. Because the ClosableTabItem may, in fact, not be Closable, the default action is to just called the base class without constructing the grid:

If it actually is Closable, the code above is called.

Failed attempt flashback: For my first attempt, I put the TextBlock and Button in a horizontal StackPanel, so the button was right-aligned by default. I found it didn’t look right though when too many tabs were opened. The tabs wrapped but got wider, so the close button ended up in the middle of the tab, not on the right. I exchanged the StackPanel for a Grid which provided the desired effect.

image

Handling the OnClose event

There are three things the ClosableTabItem needs to manage when the user closes it:

  1. Removes itself from the parent TabControl.To do this, it has to know who its parent is. The GetParentTabControl() method searches recursively to find a TabControl up the Parent chain. For safety’s sake, if a parent TabControl is never found (not very likely), there’s nothing for the ClosableTabItem to remove itself from, so nothing is done. Otherwise, it removes itself from the TabControl’s Items collection.
  2. Notify the programmer if they subscribed to the Close event.Simply check to see if the handler is null. If it isn’t null, call it.
  3. Nullify its Content.
        private void            OnClose(object sender, RoutedEventArgs e)
        {
            TabControl tabControl = GetParentTabControl(this);
            if (tabControl != null)
                tabControl.Items.Remove(this);
            if (this.Close != null)
                this.Close(this, e);
            this.Content = null;
        }
        private static TabControl GetParentTabControl(FrameworkElement tabItem)
        {
            FrameworkElement parent = (FrameworkElement) tabItem.Parent;
            if (parent == null)
                return null;
            if (parent is TabControl)
                return (TabControl) parent;
            return GetParentTabControl(parent);
        }

Styling the Controls

[Credit to Sandy Place for the styling of the TabControl and TabItem in generic.xaml].

Here is the basis from which we started:

  • We didn’t want to use the out-of-the-box styling of TabControl and TabItem
  • We wanted both ClosableTabItems and TabItems to look the same
  • Styles cannot inherit from other styles
  • We didn’t want to put the styling in Page.xaml - rather it should go in generic.xaml

Initially, we did put the styles in Page.xaml. However, not all controls were defined in Page.xaml. Those that were could reference them declaratively; for controls defined elsewhere (e.g.: the EmployeeMainPanel’s TabControl), I used the following code:

        this.Template = (ControlTemplate)((FrameworkElement)Application.Current.RootVisual).Resources["SlateTabItemStyle"];

I tend to like things to be consistent, so I moved the styles to generic.xaml so loading of the styles would all be done in code. After doing so however, this technique no longer worked. Apparently styles defined in generic.xaml are not accessible from RootVisual. However, a little research turned up a technique that proves very useful.

[Credit to Azret Botash (Developer Express) from the following article:] http://community.devexpress.com/blogs/theonewith/archive/2008/07/29/silverlight-more-then-one-default-template-in-generic-xaml.aspx

First, define the ControlTemplates (not Styles) in generic.xaml and provide a key.

        <ControlTemplate x:Key="SlateTabItemStyle" TargetType="ex:TabItem">
            <Grid x:Name="Root" Margin="3,2,0,1">
                <Grid.Resources>
                etc.

The ResourceManager class (included) is then used to load resources defined in any xaml file. This means you can define multiple resource dictionaries and load from any one of them. Because each ResourceDictionary can be defined in a separate xaml file, I suppose you could use this technique to define theme files. If you specify a null dictionaryName when loading a ResourceDictionary, it will default to generic.xaml. ResourceDictionaries are loaded into a static Dictionary cache so they’re only loaded once.

In order to use the template, I added the static GetControlTemplate() method. There are 2 overloads:

  1. GetControlTemplate(string key) - defaults to looking in the generic.xaml ResourceDictionary
  2. GetControlTemplate(string dictionaryName, string key) - looks in the dictionary of your choosing

In the constructor of the ClosableTabItem, the Template is loaded from generic.xaml:

        this.Template = ResourceManager.GetControlTemplate("SlateTabItemStyle");

If you wanted to use the 2nd overload:

        this.Template = ResourceManager.GetControlTemplate("generic.xaml", "SlateTabItemStyle");

Similarly, the ClosableTabItem’s close button’s Template is applied during the OnHeaderChanged event and the TabControl itself is styled whenever the Page or panel on which it is defined are loaded:

       // Close button 

       Button button = new Button();
        button.Template = ResourceManager.GetControlTemplate("ClosableButtonTemplate");
        button.Click    += OnClose;
       // Loading the Page 

       private Page()
        {
            InitializeComponent();
            this.pnlContent.Template = ResourceManager.GetControlTemplate("SlateTabControlStyle");
        }
       // Loading the EmployeeMainPanel 

       public EmployeeMainPanel()
        {
            InitializeComponent();
            this.InitializeControls();
            this.tabControl.Template = ResourceManager.GetControlTemplate("SlateTabControlStyle");
        }

Finally, you can see (from loading both the Page and the EmployeeMainPanel) that it’s possible to nest TabControls, mixing and matching Closable and non-ClosableTabItems.

image

Summary

The code is free - if you find a use for it, enjoy. Feel free to enhance it.

Regards,

Doug

A Dynamic WCF Client Proxy in Silverlight

Download the source

We were very excited to see WCF support in Silverlight but were a little disappointed with the tooling support, in particular the Silverlight client-side proxy generator. If, like us, you are writing a front end to your own services or are in control of both the service and client application read-on. If you are calling a 3rd party web services such as Amazon.com this code may not be for you (Unless you are interested in the some of the underlying gory implementation details we used to develop this beast, i.e. the reflection emit APIs), but wait, we are getting ahead of ourselves.

What is it?

After living with the existing Silverlight Proxy generated code for awhile it became frustrating to work around some of its shortcomings. In particular:

  • Each service had to be generated in its own namespace.
  • Couldn’t figure out how to share a [DataContract] easily between service definitions.
  • Bound at compile time. With our proxy generator types are generated dynamically using the reflection emit APIs therefore it should be possible to generate service calls more flexibly.
  • The generated code was not the most readable and simplest to use from the programmers perspective.

What we developed is a Silverlight WCF proxy generator which emits a client proxy at runtime. Minimal changes have to be done to your service to utilize this proxy. For example if you have a service definition such as:

    [ServiceContract(Namespace = "http://WorkSight.Net/")]
    public interface IGetDescriptionService
    {
        [OperationContract]
        DescriptionContract         GetDescription(int id);
    }

    [DataContract(Namespace = "")]
    public class DescriptionContract
    {
        [DataMember]
        public Guid                 Id              { get; set; }
        [DataMember]
        public string               Description     { get; set; }
        [DataMember]
        public string               Abbreviation    { get; set; }
    }

To call this service from Silverlight we first define the interface prototype for the service on the client as follows:

    public interface IGetDescriptionService
    {
        void                    GetDescription(int id, OnCompletion<DescriptionContract> result);
    }

Then to call this service you code:

   IGetDescriptionService descriptionService = WCFClientProxy<IGetDescriptionService>.Create();
   descriptionService.GetDescription(someId, (result, ex) =>
   {
      if(ex != null)
         throw ex;

      Debug.WriteLine(result.Description);
   });

How it Works Conceptually

When calling WCFClientProxy<T>.Create(); the interface specified as the template parameter is analyzed (this happens only once per service interface in the static constructor) and a client type that implements this interface is generated using the reflection emit APIs. Not only does it generate a class that implements this interface but also generates an asynchronous client side implementation of the server service which is called by this service and maps the results back to the callback OnCompletion<T>.

    public delegate void        OnCompletion<T>(T retValue, Exception error);

After the generation phase, WCFClientProxy<T>.Create() creates and returns a new instance of the generated proxy type as follows:

        public static T     Create()
        {
            BasicHttpBinding basicHttpBinding       = new BasicHttpBinding(BasicHttpSecurityMode.None);
            basicHttpBinding.MaxReceivedMessageSize = Int32.MaxValue;
            basicHttpBinding.MaxBufferSize          = Int32.MaxValue;
            return (T) Activator.CreateInstance(_ServiceType, basicHttpBinding, EndPointAddress);
        }

Usage Details and Features

Interface Mapping

  1. Service interfaces must be named the same on both the client and server and must be prefixed with a capital I.
    • As in IDepartmentsService, IImageService… etc….
  2. Service interfaces can be inherited.
    • For example the following service as defined on the server:
          [ServiceContract(Namespace = "http://WorkSight.Net/")]
          public interface IGetDescription
          {
              [OperationContract]
              DescriptionContract GetDescription(int value);
          }
      
          [ServiceContract(Namespace = "http://WorkSight.Net/")]
          public interface IDepartmentsService : IGetDescription
          {
              [OperationContract]
              string                      GetDepartmentData(int id);
          }
      
    • Maps to the following interface on the client:
          public interface IGetDescription
          {
              void                    GetDescription(int value, OnCompletion<DescriptionContract> result);
          }
      
          public interface IDepartmentsService : IGetDescription
          {
              void                    GetDepartmentData(int id, OnCompletion<string> result);
          }
  3. Operation contracts must have the same name on both the client and server.
  4. All client side mapped Operation Contracts return void and the return value is specified as the generic parameter to the OnCompletion<T> callback delegate which must be the last parameter after the function arguments.
    • To map the server side contract: int GetSomeInt(string a, string b); -> void GetSomeInt(string a, string b, OnCompletion<int> result);
  5. On the server you must implement the server as the interface name without the I prefix followed by .svc.
    • so IEmployeesService is implemented on the server in “EmployeesService.svc” and IGetDescription becomes “GetDescription.svc”.

Client Configuration File

Configuration of the client interface is controlled by a simple self explanatory ClientConfig.xml file which looks like:

<?xml version="1.0" encoding="utf-8" ?>
<settings>
  <setting key="serviceAddress" value="http://localhost:61979/{0}.svc" />
  <setting key="serviceNamespace" value="http://WorkSight.Net/" />
</settings>

The WCFClientProxy<T> uses serviceAddress value to resolve the service address by creating an EndpointAddress as follows:

   public static EndpointAddress EndPointAddress
   {
      get { return new EndpointAddress(string.Format(ApplicationConfig.GetValue("serviceAddress"), typeof(T).Name.Substring(1))); }
   }

Shared Data Contracts

Data Contracts are declared once and are shared between service definitions allowing you to write client side interfaces like:

    public interface IGetDescription
    {
        void                    GetDescription(int value, OnCompletion<DescriptionContract> result);
    }

    public interface IEmployeesService : IGetDescription
    {
//          Commented out deliberately to show you don't have declare all the methods of the service interface.
//        void GetEmployeeData(int id, OnCompletion<string> result);
    }

    public interface IDepartmentsService : IGetDescription
    {
        void                    GetDepartmentData(int id, OnCompletion<string> result);
    }

    // This DescriptionContract is shared by both interfaces.
    [DataContract(Namespace = "")]
    public class DescriptionContract
    {
        [DataMember]
        public Guid             Id { get; set; }
        [DataMember]
        public string           Description { get; set; }
        [DataMember]
        public string           Abbreviation { get; set; }
    }

Caveats and Warranties and Final Thoughts

The proxy is generated from the interface definition not WSDL.

The source code is provided as is and is free to modify and use. That being said I will try to provide bug fixes when I can but if it doesn’t work for you, hear are some options: change it/fix it/enhance it/delete it just don’t sell it as yours. This isn’t production quality, though it does seem to work reasonably well with our own internal Silverlight prototype provided you follow the above guidelines.

Most of the interesting code is in WCFClientProxy.cs

I probably won’t answer too many questions like why doesn’t my service work? and I am getting cross domain policy errors, why? Tech support on free stuff can be a little slow sometimes but I will pitch in when I can. Anyways, good luck and I hope some of you find it useful, if not interesting.

Regards,

Sandy Place

Silverlight ComboBox

Note: This project was written for and is compatible with Silverlight 2 Beta 2 and does not work with the RTM version of Silverlight 2. We have decided not to upgrade the project. On the other hand, all source is provided so if you wish, you are free to use it as a starting point for a project of your own.


Like many others, we have wanted a ComboBox since the release of Silverlight, but failed to find a usable one. Rather than waiting to see if and when the RTM includes one, we decided to address that need ourselves. It includes templating, data binding, and auto complete functionality.

View the Demo

Download the source

Getting Started

The initial code for the combo box was obtained by reflecting (http://www.red-gate.com/products/reflector/) on the Silverlight ListBox control. This provided the initial xaml and C# for the dropdown portion of the combo box, so we renamed the controls from ListBoxItem to ComboBoxItem and ListBox to ComboBox. To complete the control, we added a TextBox and a Button.

image001

Populating the Combo Box

The ComboBoxItem element has a Content property that can be used to populate the combo box in the XAML markup file.

<local:ComboBoxItem Content="British Columbia"/>
<local:ComboBoxItem Content="Alberta"/>
<local:ComboBoxItem Content="Saskatchewan"/>

In code, you can use the Add() method of the Items property on the Combo Box.

this.cboProvinces.Items.Add("British Columbia");
this.cboProvinces.Items.Add("Alberta");
this.cboProvinces.Items.Add("Saskatchewan");

You can also set the ItemsSource property of the combo box.

IList<Province> Provinces = new List<Province>();
Provinces.Add(new Province("British Columbia", "BC", new DateTime(1871, 7, 20)));
Provinces.Add(new Province("Alberta",          "AB", new DateTime(1905, 9, 1)));
Provinces.Add(new Province("Saskatchewan",     "SK", new DateTime(1905, 9, 1)));
this.cboProvinces.ItemsSource = Provinces;

Data Templates

For more complex dropdown combo box items, you can define a Data Template in your XAML.

<local:ComboBox.ItemTemplate>
    <DataTemplate>
        <StackPanel Margin="0,3,0,3">
            <StackPanel Orientation="Horizontal">
                <TextBlock Width="150"
                           Text="{Binding Path=FullName}"
                           VerticalAlignment="Center" FontSize="14" />
                <TextBlock Text="{Binding Path=Abbreviation}"
                           HorizontalAlignment="Right"
                           VerticalAlignment="Center"
                           FontSize="12" />
            </StackPanel>
            <TextBlock Text="{Binding Path=DateOfConfederation, Converter={StaticResource DateConverter}}"
                       Foreground="DarkRed"
                       FontSize="10" />
        </StackPanel>
    </DataTemplate>
</local:ComboBox.ItemTemplate>

Here’s the resulting dropdown:

image002

Data Binding

Of course, once a selection has been made from a rather complex dropdown, the entry in the text box portion of the combo box is the ToString() result of the selection.

image003

There are 2 ways to bind the selection to the text box:

DisplayMemberPath

Specify a property of the selection object.

<local:ComboBox x:Name="cboProvinces" Width="200" DisplayMemberPath="Description" />

DisplayMemberBinding

Specify the binding to use when displaying the selected item in the text box.

<local:ComboBox x:Name="cboProvinces"
                DisplayMemberBinding="{Binding DateOfConfederation}">

This becomes more useful when a type converter is specified.

<local:ComboBox x:Name="cboProvinces"
                DisplayMemberBinding="{Binding DateOfConfederation,
                Converter={StaticResource DateConverter}}">

If both DisplayMemberPath and DisplayMemberBinding are specified, only DisplayMemberPath is used.

Auto Complete

The auto-complete functionality is available by default. Each item in the drop down is compared with the text in the text box. If there’s a match, the item’s Visibility property is set to Visible. Otherwise, it’s set to Collapsed.

Initial dropdown

image004

Type ‘n’ in the text box

image005

Type ‘ne’ in the text box

image006

“Auto” DropDownWidth

The width of the dropdown defaults to the width of the combo box itself. You can specify a smaller or larger value by specifying a number, or you can auto-size the dropdown by specifying a DropDownWidth of “Auto”.

<local:ComboBox x:Name="cboProvinces" Width="250" />

image007
<local:ComboBox x:Name="cboProvinces" Width="250" DropDownWidth="200" />

image008
<local:ComboBox x:Name="cboProvinces" Width="250" DropDownWidth="300" />

image009
<local:ComboBox x:Name="cboProvinces" Width="250" DropDownWidth="Auto" />

image010

Processing the Selection

Subscribe to the SelectionChanged event to process the selection.

private void OnSelectionChanged(object sender, SelectionChangedEventArgs e)
{
    object selectedItem     = this.cboProvinces.SelectedItem;
    this.txtSelection.Text  = (selectedItem == null)
                                ? string.Empty
                                : ((Province)selectedItem).Description;
}

Welcome

Welcome to the WorkSight Blog. We hope to share some of our thoughts about software design and development based on our experience in building our product, WorkSight.Net. It is written in C# 3.5, ASP.NET and a sprinkling of JavaScript. We leverage Microsoft technologies such as Sql Server, ClickOnce, WCF, and are venturing into the Silverlight arena.

We’re not committing to a specific blogging schedule yet, but want to keep it interesting and relevant enough that you’ll give us a return visit.