Synch a ComboBox in WPF with Foreign Keys
In most database applications you will have foreign keys to express a relationship between one table and another. Many times this takes the form of a lookup table where you want the user to pick from a selection of items such as an Employee Type for an Employee record. You don’t want to have the user remember a number such as 1, 2, or 3, but instead they will pick from a list of the description that corresponds to these numbers such as “Manager”, “Project Manager”, or Employee. While you want to display these descriptions in a ComboBox, like that shown in Figure 1, you want to store the Employee Type ID number into your Employee table to make the data smaller and make it more efficient to join between the two tables.
In Figure 1 you see a DataGrid of Employee object and each employee has been assigned an EmployeeTypeId. As you click on each Employee object in the DataGrid you display the data using data binding in the text boxes at the bottom of the screen. You also have a ComboBox where each row is an EmployeeType object. Since the Employee object only has the EmployeeTypeId you need to position the ComboBox to that appropriate value so it can update the description as you move through the Employee objects. This blog post will discuss how to accomplish this using data binding in WPF.
Figure 1: A ComboBox is used to display related values based on a Foreign Key
To keep things simple for this sample, I will not be using a database, but instead will just be hard coding some values for the Employee and EmployeeType objects. Let’s take a look at the classes that make up this sample.
The EmployeeType Class
The EmployeeType class is very simple with just an EmployeeTypeId property and and EmpType property. The constructor will pass in an id and a description to assign to the EmpType property. Within this same file there is an EmployeeTypes collection class. This collection class uses a Generic List.
public class EmployeeType
{
public EmployeeType(int id, string empType)
{
EmployeeTypeId = id;
EmpType = empType;
}
public int EmployeeTypeId { get; set; }
public string EmpType { get; set; }
}
public class EmployeeTypes : List<EmployeeType>
{
}
The Employee Class
The Employee class has three properties; EmployeeId, Name, and EmployeeTypeId. The constructor allows you to pass in all three of these values to set the appropriate properties. A Generic List class is used for a collection class of Employee objects.
public class Employee
{
public Employee(int id, string name, int empTypeId)
{
EmployeeId = id;
Name = name;
EmployeeTypeId = empTypeId;
}
public int EmployeeId { get; set; }
public string Name { get; set; }
public int EmployeeTypeId { get; set; }
}
public class Employees : List<Employee>
{
}
The EmployeeViewModel Class
For this sample you will also need a ViewModel class. In this class is where you will create a list of Employee objects and EmployeeType objects. This class will be used for the binding to the XAML in the Employee window.
In this class you will use two Dependency Properties for the collection of Employee objects and the collection of EmployeeType objects. A Dependency Property is simply a property that stores its data into an internal structure and not into a private variable like a simple property you might create. In addition, a Dependency Property also implements the INotifyPropertyChanged event so if the value changes a PropertyChanged event is raised to inform any object bound to it that it needs to update its value. To use Dependency Properties your class must inherit from DependencyObject.
The EmployeeViewModel class has one method to load a collection of Employee objects into the EmployeeCollection property. It also has a method to load a collection of EmployeeType objects into the EmployeeTypesCollection property. The constructor of this EmployeeViewModel class calls both of these methods so the properties will be loaded when an instance of this class is created.
public class EmployeeViewModel : DependencyObject
{
public EmployeeViewModel()
{
LoadEmployees();
LoadEmployeeTypes();
}
public EmployeeTypes EmployeeTypesCollection
{
get { return (EmployeeTypes)GetValue(
EmployeeTypesCollectionProperty); }
set { SetValue(EmployeeTypesCollectionProperty, value); }
}
public static readonly DependencyProperty
EmployeeTypesCollectionProperty =
DependencyProperty.Register("EmployeeTypesCollection",
typeof(EmployeeTypes), typeof(EmployeeViewModel), null);
public Employees EmployeeCollection
{
get { return (Employees)GetValue(EmployeeCollectionProperty); }
set { SetValue(EmployeeCollectionProperty, value); }
}
public static readonly DependencyProperty
EmployeeCollectionProperty =
DependencyProperty.Register("EmployeeCollection",
typeof(Employees), typeof(EmployeeViewModel), null);
public void LoadEmployeeTypes()
{
EmployeeTypesCollection = new EmployeeTypes();
EmployeeTypesCollection.Add(new EmployeeType(1, "Manager"));
EmployeeTypesCollection.Add(new EmployeeType(2,
"Project Manager"));
EmployeeTypesCollection.Add(new EmployeeType(3, "Employee"));
}
public void LoadEmployees()
{
EmployeeCollection = new Employees();
EmployeeCollection.Add(new Employee(1,
"Michael Krasowski", 1));
EmployeeCollection.Add(new Employee(2, "John Kuhn", 2));
EmployeeCollection.Add(new Employee(3, "Jim Ruhl", 3));
EmployeeCollection.Add(new Employee(4, "Russ Marzolf", 3));
EmployeeCollection.Add(new Employee(4, "John Brongo", 3));
}
}
The XAML
Now that you have all of your classes created you are now ready to create the user interface shown in Figure 1. You will add a DataGrid, some text block and text box controls, and a combo box. You will assign the DataGrid the name “grdData”.
You will need to have your window create an instance of the ViewModel class. You do this by adding an XML namespace that points at your project and assign an alias of “vm”.
<Window x:Class="WPFComboBox.MainWindow"
...
xmlns:vm="clr-namespace:WPFComboBox"
...>
You can then create an instance of the EmployeeViewModel class in the Window.Resources element of your Window using the following syntax. You will assign this class a key (a variable name) of “viewModel”.
<Window.Resources>
<vm:EmployeeViewModel x:Key="viewModel" />
</Window.Resources>
Now it is time for you to bind the DataGrid to the EmployeeCollection property in your ViewModel class. To your DataGrid element you can add the following ItemsSource property. This syntax uses a typical Binding where the source of the data is the StaticResource “viewModel”. The path is the EmployeeCollection dependency property.
<DataGrid AutoGenerateColumns="True"
Name="grdData"
ItemsSource="{Binding Source={StaticResource viewModel},
Path=EmployeeCollection}" />
The TextBox controls on this form are contained within a Grid control. This Grid’s DataContext property is bound to the SelectedItem property of the DataGrid named “grdData” on this window. Since all of TextBox controls are contained within this Grid control, you only need to specify the {Binding Path=[PROPERTY]} within the Text property of each text box. Check the sample (instructions for download are at the end of this blog post) for the complete syntax.
<Grid Grid.Row="1"
DataContext="{Binding ElementName=grdData,
Path=SelectedItem}">
Bind the ComboBox
Finally it is time to create the bindings for the ComboBox. The data to load into the ComboBox will come from the EmployeeViewModel class and the property called EmployeeTypesCollection within that object. So the ItemsSource property will be bound to that object and that property. You will set the DisplayMemberPath to “EmpType” which is the property that has the employee type description. In order to position the ComboBox when you change to a different selected item in the DataGrid you need to also bind the SelectedValuePath and SelectedValue properties.
The SelectedValuePath is set to the EmployeeTypeId property in the EmployeeType object. The SelectedValue property if set with a value like 1, 2 or 3 will force the ComboBox to set the selected index to the value that matches what is put into this property. You will need to bind this property to the EmployeeTypeId property in the Employee object that comes from the DataGrid. You use an ElementName binding to the “grdData” DataGrid and set the Path to “SelectedItem.EmployeeTypeId”. Remember that when you click on the DataGrid, the SelectedItem property is set to the current Employee object. The Employee object contains a EmployeeTypeId property that is set to a value like 1, 2, or 3. This is what sets the SelectedValue each time you move through the DataGrid. This then causes the ComboBox to reposition to the appropriate employee type description.
<ComboBox
DisplayMemberPath="EmpType"
SelectedValuePath="EmployeeTypeId"
SelectedValue="{Binding
ElementName=grdData,
Path=SelectedItem.EmployeeTypeId}"
ItemsSource="{Binding Source={StaticResource viewModel},
Path=EmployeeTypesCollection}" />
Summary
In this blog post you learned how to keep a ComboBox in synch with a DataGrid as you move through related data. As with most things in XAML, it is all about creating classes with properties and methods to hold data, then simply binding to the appropriate properties to display that data. In the case of a ComboBox you just need to set the SelectedValuePath and the SelectedValue attributes to have it automatically update when the data in a related object changes.
NOTE: You can download this article and many samples like the one shown in this blog entry at my website. http://www.pdsa.com/downloads. Select “Tips and Tricks”, then “Synch a ComboBox in WPF with Foreign Keys” from the drop down list.
Good Luck with your Coding,
Paul Sheriff
** SPECIAL OFFER FOR MY BLOG READERS **
We frequently offer a FREE gift for readers of my blog. Visit http://www.pdsa.com/Event/Blog for your FREE gift!
Past Blog Content
Blog Archive
-
2015
-
2014 (18)
-
2013 (11)
-
2012 (19)
-
2011 (29)
-
2010 (19)
-
2009 (28)
-
2008 (0)
-
2007 (14)
-
2006 (6)