Introduction
This article shows how to create a set of self-contained ComboBox controls that display international country, region, and city selections from pre-defined, non-database lists. The controls fit in small size DLLs that do not require Compact SQL Server to execute.
Background
I was working on a WM6 application that asked the user for demographic data such as Country, State/Province/Region, and City. The data would eventually be transmitted to a Web Service so I wanted standardized responses. Therefore, I had to provide the list of locations.
Users do not like to scroll through long lists of thousands of states and cities. I wanted the controls to be smart enough to rebuild Region and City lists when a new Country or Region was selected. If you selected "United States > Michigan", I only wanted Michigan cities on the CityComboBox.
I did not, however, want to rely on a database that required the Microsoft SQL Server Compact Framework. It did not make sense that users had to load a large framework on their mobile device just to implement a small feature. Nor did I want to integrate with an external Web data source, as internet connections are not always possible in a mobile environment.
I found a great source of countries, regions, and cities at GeoBytes.com. I did a little massaging of the data to trim it to the smallest size possible. Instead of adding it to a database, I embedded the CSV files directly into the controls so that only the DLL need be distributed.
Using the code
The solution is split into two projects. The CountryRegionComboBox project provides two ComboBox
controls, Country and Region. It does not provide the City ComboBox
, as that adds a considerable amount of data. You can use this assembly when you do not need cities and wish to conserve precious mobile disk space.
The CountryRegionCityComboBox includes a 408 KB list of cities. I was able to trim it down a little by combining identically named cities, listing their Region IDs together. If you need the cities and don't mind the extra size, use this assembly.
Embedding the lists
The controls read the embedded data files using System.Reflection.Assembly
and the GetManifestResourceStream()
method. The CSV files are set as Embedded Resources so they are included in the DLL at compile time.
System.Reflection.Assembly asm = System.Reflection.Assembly.GetExecutingAssembly();
Stream CountryFileStream =
asm.GetManifestResourceStream("CountryRegionCityControls.Countries.csv");
Cross-control events
When you select a new country, the Country control needs to fire an event that is intercepted by the Region control. The Region control will then rebuild its list to display the regions, states, provinces, etc., of the selected country.
To do this, I used static classes that fired events for each control. Since the classes are static, there is only one implementation of each that is accessable by all controls. If two controls are on the same form, one will trigger events in the other without either of them being aware of the other.
public static class DetectCountryChange
{
public static event CountryChangedDelegate CountryChanged;
public static void FireCountryChanged(Country Country)
{
if (CountryChanged != null)
CountryChanged(new CountryChangedEventArgs(Country));
}
}
To subscribe to the CountryChanged
event, you add the event subscription to the Region control's constructor:
public RegionsComboBox()
{
InitializeComponent();
DetectCountryChange.CountryChanged +=
new CountryChangedDelegate(DetectCountryChange_CountryChanged);
}
When the CountryComboBox
changes, it calls the FireCountryChanged()
method, which triggers the CountryChanged
event that is intercepted by the RegionsComboBox
.
History
- 2009.05.29 - Initial submission.