in

Utah .NET User Group

Home of Utah's professional .NET developers.

Joe's Blog

August 2008 - Posts

  • Building a Silverlight ComboBox Using Attached Behaviors

    I recently needed to use a ComboBox in an application I was writing.  Because there is no built-in ComboBox in Silverlight I decided to explore building one using attached behaviors.  If you’re not familiar with this design pattern, check out Nikhil’s posts.  My ComboBox behavior is loosely based on his AutoComplete behavior.  I also make use of Julian’s ButtonCommands class, which he describes in this post.

    Silverlight ComboBox Project Test Page - Mozilla Firefox

    I started with a simple behavior interface:

    /// <summary>
    /// Represents a contract for encapsulation of logic that can be added
    /// to a dependency object through a pattern of attachment.
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public interface IBehavior<T> where T : DependencyObject
    {
        /// <summary>
        /// Gets the associated object.
        /// </summary>
        /// <value>The associated object.</value>
        T AssociatedObject { get; }
     
        /// <summary>
        /// Attaches to the specified associated object.
        /// </summary>
        /// <param name="associatedObject">The associated object.</param>
        void Attach(T associatedObject);
     
        /// <summary>
        /// Detaches from the associated object.
        /// </summary>
        void Detach();
    }

    Which I used to implement a ComboBoxBehavior class.  I chose to use FrameworkElement because I wanted to be able to attach this ComboBox to different controls.  Possibly a TextBlock, a TextBox, a Border … any number of controls.  We’ll come back to this later …

    public class ComboBoxBehavior : IBehavior<FrameworkElement>
    {
        ...
    }

    After creating the behavior I created a simple static class which had a single attached property of type ComboBoxBehavior.

    public static class Behaviors
    {
        public static readonly DependencyProperty ComboBoxProperty =
            DependencyProperty.RegisterAttached(
                "ComboBox",
                typeof(ComboBoxBehavior),
                typeof(Behaviors),
                new PropertyMetadata(
                    new PropertyChangedCallback(OnComboBoxChanged)
                )
            );
        ...
    }

    Attach() and Detach() are called in the property changed callback.

    private static void OnComboBoxChanged(
        FrameworkElement element,
        ComboBoxBehavior oldValue,
        ComboBoxBehavior newValue)
    {
        if (oldValue != null)
        {
            oldValue.Detach();
        }
     
        if (newValue != null)
        {
            newValue.Attach(element);
        }
    }

    You can then wire up the behavior in XAML … I’m using a Border control in this example.

    <Border
        xmlns:local="clr-namespace:SilverlightComboBox">
        <local:Behaviors.ComboBox>
            <local:ComboBoxBehavior
                Opened="SimpleComboBoxBehavior_Opened"
                ItemSelected="SimpleComboBoxBehavior_ItemSelected"
                ShowComboBoxCommand="{StaticResource ShowComboBoxSimple}">
            </local:ComboBoxBehavior>
        </local:Behaviors.ComboBox>
        <TextBlock
            x:Name="uxSimpleComboBox"
            Margin="4,0,4,0">
        </TextBlock>
    </Border>

    So onto the workhorse … the behavior itself.  The way I built this behavior to work is it fires an event, Opened,  when the DropDown is opened, in which you can set the ItemsSource and the SelectedIndex of the ItemsSource, as demonstrated in the following code:

    private void SimpleComboBoxBehavior_Opened(
        object sender, ComboBoxOpenedEventArgs e)
    {
        List<string> strings = new List<string>();
        strings.Add("A");
        strings.Add("B");
        strings.Add("C");
        strings.Add("D");
     
        e.ItemsSource = strings;
     
        var index = strings.IndexOf(uxSimpleComboBox.Text);
        e.SelectedIndex = index;
    }

    Once the user selects a value another event is fired, ItemSelected, which can also be handled:

    private void SimpleComboBoxBehavior_ItemSelected(
        object sender, ComboBoxItemSelectedEventArgs e)
    {
        if (e.SelectedItem != null)
        {
            uxSimpleComboBox.Text = e.SelectedItem.ToString();
        }
    }

    You may be wondering why I took this approach.  I decided to go down the eventing route because as I mentioned previously, I wanted to be able to attach this behavior to lots of different controls.  As such I felt that it should be up to the implementer to display the data as needed.

    I also decided to use commands to drive the opening and closing of the DropDown.  This allows the user to declaratively specify when the DropDown is opened or closed.  If you look at the XAML for the behavior I’m wiring up a ShowComboBoxSimple command to the ShowComboBoxCommand.

    ShowComboBoxCommand="{StaticResource ShowComboBoxSimple}"

    This is an ICommand of type MultiDelegateCommand.  MultiDelegateCommand is a command which can register multiple delegates to call when it is executed.

    I registered a MultiDelegateCommand instance in the Application Resources to be able to use it in the markup:

    public Page()
    {
        Application.Current.Resources.Add(
            "ShowComboBoxSimple",
            new MultiDelegateCommand()
        );
     
        ...
     
        InitializeComponent();
     
        ... 
    }

    Lastly, I wanted to show the DropDown anytime any portion of the control I’m attaching to is clicked.  This is accomplished by subscribing to the MouseLeftButtonUp event when the associated object is being attached:

    /// <summary>
    /// Attaches to the specified associated object.
    /// </summary>
    /// <param name="associatedObject">The associated object.</param>
    public void Attach(FrameworkElement associatedObject)
    {
        AssociatedObject = associatedObject;
        AssociatedObject.MouseLeftButtonUp += AssociatedObject_MouseLeftButtonUp;
        AssociatedObject.LostFocus += AssociatedObject_LostFocus;
    }
    private void AssociatedObject_MouseLeftButtonUp(
        object sender, MouseButtonEventArgs e)
    {
        if (ShowComboBoxCommand != null && !IsDropDownOpen)
        {
            ShowComboBoxCommand.Execute();
        }
    }

    And that’s basically it!  You may be wondering ... does this control support complex types?  You betcha! The source show’s a sample of working with complex types.  Here is shown a complex City type with a Name property.

    <DataTemplate x:Key="CityTemplate">
        <TextBlock
            Text="{Binding Name}"
            ToolTipService.ToolTip="{Binding Name}"/>
    </DataTemplate>
     
    ...
     
    <local:Behaviors.ComboBox>
        <local:ComboBoxBehavior
            Opened="ComplexComboBoxBehavior_Opened"
            ItemSelected="ComplexComboBoxBehavior_ItemSelected"
            ShowComboBoxCommand="{StaticResource ShowComboBoxComplex}">
            <local:ComboBoxBehavior.DropDownTemplate>
                <DataTemplate>
                    <ListBox ItemTemplate="{StaticResource CityTemplate}" />
                </DataTemplate>
            </local:ComboBoxBehavior.DropDownTemplate>
        </local:ComboBoxBehavior>
    </local:Behaviors.ComboBox>
    ...

     

    Download: SilverlightComboBox.zip

    Hope that helps!

    Joe

  • Visual Studio 2008 Service Pack 1 (SP1) Released

    Today Microsoft made Visual Studio 2008 Service Pack 1 (SP1) available for download.  I was able to participate in a case study done on SP1 with Microsoft, Misys, and Veracity Solutions specifically using ADO.NET Data Services and the Entity Framework.  You can read the full case study here, though here’s a small snippet:

    “For more than a decade, Misys Healthcare Systems and Veracity Solutions have partnered to develop innovative applications that meet the needs of healthcare providers while improving the quality of patient care. To help medical staff reduce manual, paper-based processes, Misys Healthcare Systems and Veracity Solutions collaborated to create FreeNatal, a Web-based application that provides prenatal care providers with an easy-to-use, secure interface for managing patients’ records. Using Microsoft® Visual Studio® 2008 SP1 and the Microsoft .NET Framework 3.5 SP1, eight members from the Misys-Veracity team created the application. By taking advantage of these powerful technologies, the team increased development speed by 60 percent, enabling accelerated market delivery and further strengthening their respective positions in the healthcare informatics industry.”

     FreeNatal “Face Sheet” view.

    FreeNatal “Face Sheet” view.

     FreeNatal application architecture diagram.

    FreeNatal application architecture diagram.
     
  • Composite Silverlight 2.0 Beta 2 Application Library

    Lately I’ve been working with Composite WPF and Silverlight applications at Veracity Solutions.  I spent a few hours over the weekend converting the Composite WPF Application Library to Silverlight.  Tonight I finished porting one of the QuickStarts to Silverlight.  Of course, just today the P&P team posted a AGCompositeApplicationLibrary spike.  Just my luck!  Regardless, here’s the UIComposition QuickStart running in Silverlight using my ported libraries:

    Region Quickstart - Windows Internet Explorer

    Some things to note:

    • The TabItem style isn’t using binding to get the header value.
    • Location tab doesn’t include the map data.
    • I added a TabControlRegionAdapter class that works with the Silverlight TabControl.

    Special thanks to Michael Sync for posting his Silverlight Unity port, it got me started.

    Download: CompositeSilverlight.zip

Copyright © 2000-2007, Utah .NET User Group
Powered by Community Server (Commercial Edition), by Telligent Systems