If you are like me, you were surprised that Silverlight does not have a
SelectedValuePath and a SelectedValue property for Combo Boxes and List Boxes.
I became frustrated with working around the absence of those properties.
Some people feel that they are not needed since we can simply add a business
object to the control as an item. The problem with that is data binding.
I use business objects/classes as items in the list but whenever an item is
selected by the user or whenever a value is read in from a database, data
binding cannot be used to automatically determine or set the value the control
because there is no SelectedValue property.
Example of the problem:
AssignmentTypes
assgnTypes =
new
AssignmentTypes( );
combo.ItemsSource = assgnTypes;
combo.DisplayMemberPath = "AssignmentType";
//problem nothing to set the value for selected item automatically
combo.DataContext = myDBTable;
System.Windows.Data.
Binding
comboBinding = new
System.Windows.Data.Binding(
"AssignmentTypeID"
);
comboBinding.Mode = System.Windows.Data.BindingMode.TwoWay;
combo.SetBinding(ComboBox.DisplayMemberPathProperty,
comboBinding ); //THIS IS NO GOOD TO ME
BECAUSE THE DISPLAY VALUE IS A STRING AND THE DB HAS AN INTEGER VALUE
public
class
AssignmentTypes :
List<AssignmentTypes.AssignmentTypeItem>
{
//get types
from db...
//add
items...
public
class
AssignmentTypeItem
{
private
int _AssignmentTypeID;
public
int AssignmentTypeID
{
get
{ return _AssignmentTypeID;
}
set
{ _AssignmentTypeID = value;
}
}
private
string _AssignmentType;
public
string AssignmentType
{
get
{ return _AssignmentType;
}
set { _AssignmentType =
value; }
}
public
override
string ToString()
{
return
AssignmentType;
}
}
}
As you can see from the code above, the display value can be databound to my
data object but that is no good. I need the AssignmentTypeID to be the
value passed to my data object, not the display item. So here's what I did
to solve the problem:
Solution:
I created an extended ComboBox that gives me the SelectedValuePath and
SelectedValue properties.
using
System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Linq;
namespace
EnhancedControls
{
public
class
ComboBoxClassic :
ComboBox
{
public
static
readonly
DependencyProperty
SelectedValuePathProperty =
DependencyProperty.Register(
"SelectedValuePath",
typeof(
string ),
typeof(
ComboBoxClassic ),
new
PropertyMetadata(
new
PropertyChangedCallback(
SelectedValuePathPropertyChanged ) ) );
public
static
readonly
DependencyProperty
SelectedValueProperty =
DependencyProperty.Register(
"SelectedValue",
typeof(
object ),
typeof(
ComboBoxClassic ),
null);
public
ComboBoxClassic()
: base()
{
base.SelectionChanged
+= new
SelectionChangedEventHandler(
ComboBoxClassic_SelectionChanged );
}
void
ComboBoxClassic_SelectionChanged(
object sender,
SelectionChangedEventArgs e )
{
if ( SelectedItem !=
null && !string.IsNullOrEmpty(
SelectedValuePath ) )
{
try
{
SetValue( ComboBoxClassic.SelectedValueProperty,
SelectedItem.GetType().GetProperty( SelectedValuePath ).GetValue(
SelectedItem, null
) );
}
catch
{
//Add exception handling
}
}
}
static
void
SelectedValuePathPropertyChanged(DependencyObject
d,DependencyPropertyChangedEventArgs
e)
{
}
public
string
SelectedValuePath
{
get {
return (
string )GetValue(
ComboBoxClassic.SelectedValuePathProperty
); }
set {
SetValue( ComboBoxClassic.SelectedValuePathProperty,
value ); }
}
public
object SelectedValue
{
get
{
return
GetValue( SelectedValueProperty );
}
set
{
try
{
var q = (
from item
in Items
where
item.GetType().GetProperty( SelectedValuePath ).GetValue(item,null).Equals(
value )
select item
).Single();
SelectedItem = q;
}
catch
{ }
}
}
}
}
Now with the extended ComboBox control I can do what I
needed:
AssignmentTypes assgnTypes =
new
AssignmentTypes( );
combo.ItemsSource = assgnTypes;
combo.DisplayMemberPath = "AssignmentType";
combo.SelectedValuePath = "AssignmentTypeID";
combo.DataContext = myDBTable;
System.Windows.Data.Binding
comboBinding = new
System.Windows.Data.Binding(
"AssignmentTypeID"
);
comboBinding.Mode = System.Windows.Data.BindingMode.TwoWay;
combo.SetBinding(ComboBoxClassic.SelectedValuePathProperty,
comboBinding ); //THIS IS GOOD TO ME
Everything now works great using the extended ComboBox.
The database gets the value it needs and the user only sees the friendly
display string automatically. I can now use this in my normal
processing of databinding without have to manually do things in
SelectionChanged events. Why this was left out of Silverlight is
beyond me unless I was missing some other way around this.
Hope it helps,
Jones