SetBinding ComboBox Silverlight (网摘)
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 = newAssignmentTypes( );
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
AssignmentTypes
combo.ItemsSource = assgnTypes;
combo.DisplayMemberPath = "AssignmentType"
//problem nothing to set the value for selected item automatically
combo.DataContext = myDBTable;
System.Windows.Data.
comboBinding.Mode = System.Windows.Data.
combo.SetBinding(
{
//get types from db...
//add items...
{
{
}
{
set
}
{
}
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:
using
using
using
using
using
using
using
using
using
using
using
{
:
{
base
}
{
{
{
SetValue(
}
{
//Add exception handling
}
}
}
{
}
{
}
{
{
}
{
{
SelectedItem = q;
}
{ }
}
}
Now with the extended ComboBox control I can do what I needed:
combo.ItemsSource = assgnTypes;
combo.DisplayMemberPath = "AssignmentType"
combo.SelectedValuePath = "AssignmentTypeID"
combo.DataContext = myDBTable;
System.Windows.Data.
comboBinding.Mode = System.Windows.Data.
combo.SetBinding(
Hope it helps,
Jones