Changing ComboBox list based on value of another control

Introduction

This sample demonstrates how to change the list of items in a ComboBox when the value of another control is changed. It performs a server query asynchronously and loads the combobox list when the query completes.

This sample also illustrates basic MVVM principles.

Description

This example works with Street Number of 1 or 2. For example, enter 1, then hit tab. Observe the values in the dropdown. Then try again with street number 2.

When the value of a control is changed, we call a WCF service. The returned value is populated in the AddressViewModel.StreetNames. The PropertyChanged event is thrown to trigger data binding.

View Model

We start the exercise by defining the basic View Model. The view has three elements: A street number, a street name, and a list of street names. The View Model implements these three as properties. We will use data binding to bind these values to the XAML.

C#
Edit|Remove
    public class AddressViewModel: INotifyPropertyChanged 
    { 
        private int mStreetNumber; 
        public int StreetNumber { 
            get 
            { 
                return mStreetNumber; 
            } 

            set 
            { 
                if (mStreetNumber != value) 
                {  
                    mStreetNumber = value; 

                    // Look up street name 
                    Web.AddressBookContext ctx = new Web.AddressBookContext(); 
                    InvokeOperation<IEnumerable<string>> op = ctx.GetStreetNames(mStreetNumber, this.GetStreetNamesCompleted, null); 
                } 
            } 
        } 

        public string StreetName { get; set; } 

        private IEnumerable<string> mStreetNames; 
        public IEnumerable<string> StreetNames 
        { 
            get 
            { 
                return mStreetNames; 
            } 
        } 

        void GetStreetNamesCompleted(InvokeOperation<IEnumerable<string>> op) 
        { 
            mStreetNames = op.Value; 
            if (PropertyChanged != null) 
                PropertyChanged(this, new PropertyChangedEventArgs("StreetNames")); 
        } 

        public event PropertyChangedEventHandler PropertyChanged;
When the street number is changed, the View Model calls the the AddressBook Service, to lookup  a list of street names that has the matching street number.)
In Silverlight, all operations are asynchronous, so that the UI remains responsive. When the webservice completes, we will get a call back. The callback is GetStreetNamesCompleted in our case. The return value is in the variable op.Value.

Binding the ViewModel to the XAML

In this example, we bind Street Number, Street Name and Street Names to the visual elements.
XAML
Edit|Remove
<UserControl x:Class="StreetLookup.MainPage" 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
    mc:Ignorable="d" 
    d:DesignHeight="300" d:DesignWidth="400"  
    xmlns:sdk="http://schemas.microsoft.com/winfx/2006/xaml/presentation/sdk" 
    Loaded="UserControl_Loaded" 
    > 

    <Grid x:Name="LayoutRoot" Background="White"> 
        <sdk:Label Height="27" HorizontalAlignment="Left" Margin="23,34,0,0" Name="label1" VerticalAlignment="Top" Width="76" Content="Number" /> 
        <sdk:Label Height="28" HorizontalAlignment="Left" Margin="178,34,0,0" Name="label2" VerticalAlignment="Top" Width="120" Content="Street" /> 
        <TextBox Height="23" HorizontalAlignment="Left" Margin="23,67,0,0" Name="textBox1" VerticalAlignment="Top" Width="120"  Text="{Binding StreetNumber,Mode=TwoWay}"/> 
        <ComboBox Height="23"  
                  SelectedValue="{Binding StreetName, Mode=TwoWay}"  
                  ItemsSource="{Binding StreetNames,Mode=OneWay}"  
                  HorizontalAlignment="Left" Margin="179,66,0,0" Name="comboBox1" VerticalAlignment="Top" Width="162"  
                  /> 
    </Grid> 
</UserControl>
When the MainPage loads, we set an instance of AddressViewModel as the MainPages’ DataContext. Note: all controls on a page inherits the DataContext from its parent. This is how TextBox binds to AddressViewModel.StreetNumber even though we haven’t set the DataContext on the TextBox.
C#
Edit|Remove
 private void UserControl_Loaded(object sender, RoutedEventArgs e) 
        { 
            this.DataContext = vm; 
        }

Web Service

In this example, because we are returning a list of Strings, we have to design this as an Invoke Operation on a WCF Domain Service, rather than a Query Operation.

C#
Edit|Remove
namespace StreetLookup.Web 
{ 
    using System.Collections.Generic; 
    using System.ServiceModel.DomainServices.Hosting; 
    using System.ServiceModel.DomainServices.Server; 

    // TODO: Create methods containing your application logic. 
    [EnableClientAccess()] 
    public class AddressBookService : DomainService 
    { 
        [Invoke] 
        public IEnumerable<string> GetStreetNames(int StreetNumber) 
        { 
            List<string> names = new List<string>(); 
            if (StreetNumber == 1) 
            { 
                names.Add("Parker St"); 
                names.Add("Zachary St"); 
            } 

            if (StreetNumber == 2) 
            { 
                names.Add("Market St"); 
                names.Add("Ocean Ave"); 
            } 

            return names; 
        } 
    } 
}

The Source Code is available here: http://code.msdn.microsoft.com/silverlight/Silverlight-Changing-64400600


About this entry