Cascading ComboBoxes Selection in RadGridView for Silverlight and WPF

by XAML Team | Comments 18

 

A common scenario when entering data is a cascade of comboboxes, where the list in the next combo is populated after selecting a value in the previous.

A typical example is selecting a country from a combo then selecting from a filtered list of cities in a second combo.

We have had tons of questions on how this can be done within a row in RadGridVIew so I decided to make a small demo. I have used a nice List of countries by continent.

 

 

 

cascadingComboboxes

 

 

There are lots of ways to achieve this with RadGirdView. Bellow I have tried to demonstrate the most-descent-in-my-opinion way  :).

As always I should mention that despite the demo uses RadGridVIew for Silverlight the same technique is applicable for RadGridView for WPF as they both share a common API.

 

This time I will dive a bit deeper into the steps required to achieve such behavior. If you just need the sample project please scroll over to the end.

 

1. Bind correctly to data  - the RadGridView and both GridViewComboBoxColumns .

To keep the demo simple I have dumped this  List of countries by continent into the ugly Locations.cs data holder class. (you may download all the files from the link provided at the end)

 

We bind RadGridView to a List of Location objects.

 

public class Location
    {
 public string ContinentCode { get; set; }
 public int? CountryID { get; set; }
 public List<Country> AvailableCountries { get; set; }
    }

*Please note this is a simplified version of the Location class. In the downloadable sample bellow the class implements the INotifyPropertyChanged interface. We need this to ensure immediate updates to the UI when the user makes selection in the combo.

 

Each Location object is associated with a Continent and a Country.

 public class Continent
    {
 public string Code { get; set; }
 public string Name { get; set; }
    }
 public class Country
    {
 public int ID { get; set; }
 public string Name { get; set; }
 public string ContinentCode { get; set; }
    }

 

We bind the first combo column to a list of continents, and the RadGridView to a list of locations.

((GridViewComboBoxColumn)this.radGridView.Columns[0]).ItemsSource = Locations.Continents;
this.radGridView.ItemsSource = locations;

 

We bind the second column a bit differently:

(XAML)

<telerik:GridViewComboBoxColumn ItemsSourceBinding="{Binding AvailableCountries}"

Since the items source will be different for each row – different sets of countries for different continents – we use the ItemsSourceBinding property. The AvailableCountries property of the Location object will hold the filtered by continent countries.

 

 

2. Provide some filtering logic, so that we can have a list of countries for a selected continent.

 

 public IEnumerable<Country> AvailableCountries
        {
 get 
            {
 return from c in Locations.Countries
                       where c.ContinentCode == this.ContinentCode
                       select c;
            }
        }

3. Ensure the second column reacts immediately on selection changes in the first combo

The default behavior of RadGridView is to commit the newly selected value after the cell loses focus. In our case we need to update the second column immediately after the selected continent has changed.

 

We subscribe to the RadComboBox.SelectionChangedEvent.

this.AddHandler(RadComboBox.SelectionChangedEvent, new Telerik.Windows.Controls.SelectionChangedEventHandler(comboSelectionChanged));

Within the event handler we update the  underlying business object :

 void comboSelectionChanged(object sender, RadRoutedEventArgs args)
        {
            RadComboBox comboBox = (RadComboBox)args.OriginalSource;
 
 if (comboBox.SelectedValue==null 
                || comboBox.SelectedValuePath != "Code") // we take action only if the continent combo is changed 
 return;
 
            Location location = comboBox.DataContext as Location;
            location.ContinentCode = (string) comboBox.SelectedValue;//we submit the value immediately rather than waiting the cell to lose focus. 
        }

 

 

Please get all the code as well as a working demo project

18 Comments

Sunggoo Im

Pavel,

This was a great help to me.

I just started playing with Telerik's Silverlight RadControls and was trying to implement cascading dropdown columns in a Gridview. This demo showed clearly how I should approach the problem.

Can I, further, ask you for the RIA Service version of this demo?

I can't get it to integrated with an actual DB.

Thank you in advance for your consideration,

Sunggoo.
Sunggoo Im

Pavel,

This was a great help to me.

I just started playing with Telerik's Silverlight RadControls and was trying to implement cascading dropdown columns in a Gridview. This demo showed clearly how I should approach the problem.

Can I, further, ask you for the RIA Service version of this demo?

I can't get it to integrated with an actual DB.

Thank you in advance for your consideration,

Sunggoo.
VB

Hi,

Can something like this be achieved using MVVM?

VB

Hi,

Can something like this be achieved using MVVM?

Jan Heiko Houtrouw
Hi,

The solution works, but the class Location must contain the member AvailableCountries which is just necessary for the user interface. So there is no clean seperation between the user interface and the data objects.

Not the best solution ...
Jan Heiko Houtrouw
Hi,

The solution works, but the class Location must contain the member AvailableCountries which is just necessary for the user interface. So there is no clean seperation between the user interface and the data objects.

Not the best solution ...
Jan Heiko Houtrouw
Hi,

The solution works, but the class Location must contain the member AvailableCountries which is just necessary for the user interface. So there is no clean seperation between the user interface and the data objects.

Not the best solution ...
f
also there are many availablecountries, not a single list that just filteres the countries list.
Danny G
Can this be done with regular RadGridView for WinForms? I'm using RadControls for Windows Forms Q2 2010 and I need the same functionality.
Houdini
My situation is almost like this but not quiet. I am binding my radGridView to a radDomainDataSource, which is pulling from my DomainService class and getting the records of all "Acquisition Status" that comes from the AcquisitionStatus table (via LINQtoSQL model). The AcquisitionStatus has a field called AcquistionTypeID (foreign Key), which points to the AcquisitionType table. All works well until I tried adding a GridViewComboBoxColumn to the radGridView to display the "Types of Acquisition" (a field in my AcquisitionType table) as opposed to displaying the AcquisitionTypeID field. All in all, I want to see the Types (Compulsory, private, etc...) in the box as oppose to seeing 1,2,3...etc.
Houdini
My situation is almost like this but not quiet. I am binding my radGridView to a radDomainDataSource, which is pulling from my DomainService class and getting the records of all "Acquisition Status" that comes from the AcquisitionStatus table (via LINQtoSQL model). The AcquisitionStatus has a field called AcquistionTypeID (foreign Key), which points to the AcquisitionType table. All works well until I tried adding a GridViewComboBoxColumn to the radGridView to display the "Types of Acquisition" (a field in my AcquisitionType table) as opposed to displaying the AcquisitionTypeID field. All in all, I want to see the Types (Compulsory, private, etc...) in the box as oppose to seeing 1,2,3...etc.
Johnny
Hi,
Thank you for the awesome example. I got a lot from it. I am currently designing a grid that is very similar to the example, however, I have one more column that displays value from the second cascaded combo box. For example, I have third column named population and I would like to use it to display the population from the country selected from the country combo box. I've tried many different approaches but still not sure how to tackle this problem. How can I able to do it? Thanks
Johnny
Hi,
Thank you for the awesome example. I got a lot from it. I am currently designing a grid that is very similar to the example, however, I have one more column that displays value from the second cascaded combo box. For example, I have third column named population and I would like to use it to display the population from the country selected from the country combo box. I've tried many different approaches but still not sure how to tackle this problem. How can I able to do it? Thanks
Johnny
Hi,
Thank you for the awesome example. I got a lot from it. I am currently designing a grid that is very similar to the example, however, I have one more column that displays value from the second cascaded combo box. For example, I have third column named population and I would like to use it to display the population from the country selected from the country combo box. I've tried many different approaches but still not sure how to tackle this problem. How can I able to do it? Thanks
rduclos
Do you have an example of this using RIA Services?
Maxim Balaganskiy
The better way to do this is writing an attached property for RadGridView, it will be fully MVVM-ish.
        #region GetUpdateComboBoxSourceOnPropertyChange         public static bool GetUpdateComboBoxSourceOnPropertyChange(DependencyObject obj)        {            return (bool)obj.GetValue(UpdateComboBoxSourceOnPropertyChangeProperty);        }         public static void SetUpdateComboBoxSourceOnPropertyChange(DependencyObject obj, bool value)        {            obj.SetValue(UpdateComboBoxSourceOnPropertyChangeProperty, value);        }         /// <summary>        /// Enables the behavior when selecting an item in any GridViewComboBoxColumn immediately changes the bound property        /// </summary>        public static readonly DependencyProperty UpdateComboBoxSourceOnPropertyChangeProperty =            DependencyProperty.RegisterAttached("UpdateComboBoxSourceOnPropertyChange"typeof(bool), typeof(RadGridViewHelper), new FrameworkPropertyMetadata(false, UpdateComboBoxSourceOnPropertyChangeChanged));         private static void UpdateComboBoxSourceOnPropertyChangeChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)        {            var grid = d as RadGridView;            if (grid == null)            {                return;            }             if ((bool)e.NewValue)            {                grid.AddHandler(RadComboBox.SelectionChangedEvent, new SelectionChangedEventHandler(ComboBoxSelectionChanged));            }        }         private static void ComboBoxSelectionChanged(object sender, SelectionChangedEventArgs e)        {            var cb = e.OriginalSource as RadComboBox;            var cell = (e.OriginalSource as RadComboBox).ParentOfType<GridViewCell>();            if (cell != null)            {                cell.Value = cb.SelectedItem;                cell.DataColumn.UpdateSourceWithEditorValue(cell);            }        }         #endregion
Maxim Balaganskiy
Sorry, meant to say "the better way to apply combo box changes immediately after selection"
Michel
I have converted this example to WPF because I need it there. It all works fine expect that the Telerik.Window.Controls does not have a SelectionchangedEventHandler. So the second dropdown is only updated when i leave the first.
I am using the latest version of the controls. What could be the wpf alternative for this?

Comments

  1.    
      
      
       
  2. (optional, emails won't be shown on public pages)
  3. (optional)
Read more articles by XAML Team - or - read latest articles in Developer Tools