一、发现问题。
继续学习SL,这次用到了ComboBox,自带的ComboBox可以通过ItemSource和DisplayMemberPath来实现Items的数据绑定,DisplayMemberPath类似于Asp.net控件中的DataTextField,但是奇怪的是,ComboBox并没有提供类似与DataValueField这样的属性,也就是说,在使用SelectedItem的使用,我们无法像过去一样,直接赋一个值就可以让它选择对应的Item,现在的SelectedItem必须是ItemSource里面的一个对象,这样使用起来会非常的麻烦,比如说,我们让ComboBox绑定到Suppliers集合,DisplayMemberPath为CompanyName,这个时候,如果我们想通过SupplierID来指定ComboBox的SelectedItem时,就必须到ItemSource中,按照SupplierID来找到对应的Supplier对象,再把这个Supplier赋给SelectedItem才行。
所以这里又要骂微软了,真的不知道他们是怎么想的,学习SL这段时间来,感觉他们在做SL的时候,想的太理想化,像个科研产品,不适合商用。很多常用的功能都给他们改的面目全非,把简单问题复杂化。
二、解决问题。
要解决这个问题,我们需要自己动手来写一个新的ComboBox,并给它添加上SelectedValuePath和SelectedValue这两个属性。
这里参考了Jones的SilverLight ComboBox,这篇文章,原文在这里http://www.engineserver.com/silverlightcombobox/。
代码基本上和他的一样,但是修改了他代码中的部分问题,使其更加像Asp.net中的DropDownList。
我们新建了一个类,让它继承ComboBox,并且添加了SelectedValuePathProperty和SelectedValueProperty。这样,我们就可以通过Binding来和数据库进行绑定了,我在用Jones的代码时,界面出现之后,ComboBox并不会按照SelectedValuePath来显示指定的数据,但是在手工选择ComboBox里面的Item只有,它就能很好的工作了,这个很奇怪的问题花了我不少时间,结果发现,在SetBinding的时候,虽然我们让其Bind到SelectedValueProperty上,但是系统并没有通过SelectedValue的Set来赋值,所以SelectedValue的Set的代码并不会执行,这就导致初始化的时候,ComboBox并不按照Bind的显示数据。
后来,我在SelectedValuePropertyChanged中,对SelectedValue进行了赋值,使其可以正确工作。
ok,基本上就这么多了,其实整个功能很简单。下面看代码:
自定义ComboBox的代码:
ComboBoxClassic
public class ComboBoxClassic : ComboBox
{
#region SelectedValuePathProperty
public static readonly DependencyProperty SelectedValuePathProperty =
DependencyProperty.Register("SelectedValuePath", typeof(string),
typeof(ComboBoxClassic), null);
public string SelectedValuePath
{
get
{
return (string)GetValue(ComboBoxClassic.SelectedValuePathProperty);
}
set
{
SetValue(ComboBoxClassic.SelectedValuePathProperty, value);
}
}
#endregion
#region SelectedValueProperty
public static DependencyProperty SelectedValueProperty =
DependencyProperty.Register("SelectedValue", typeof(object), typeof(ComboBoxClassic),
new PropertyMetadata(new PropertyChangedCallback(SelectedValuePropertyChanged)));
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
{
throw new Exception();
}
}
}
private static void SelectedValuePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
(d as ComboBoxClassic).SelectedValue = e.NewValue;
}
#endregion
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));
var value = SelectedValue;
}
catch
{
throw new Exception();
}
}
}
}
调用的方法:
我们假设编写一个修改数据的窗体,传入ProductID后,在界面上显示多个TextBox和ComboBox,ComboBox中显示Suppliers的信息,并和Product中的SupplierID绑定。
先通WCF获取Suppliers数据,并绑定到ItemSource上。同时设置了DisplayMemberPath和SelectedValuePath属性。
void client_getAllSuppliersCompleted(object sender, getAllSuppliersCompletedEventArgs e)
{
cb.ItemsSource = e.Result;
cb.DisplayMemberPath = "CompanyName";
cb.SelectedValuePath = "SupplierID";
}
使用时,先获取Product信息,再绑定
void client_getProductCompleted(object sender, getProductCompletedEventArgs e)
{
products = e.Result;
//设置ComboBox所在的Grid的DataContext。
content.DataContext = products;
//设置Bind信息,让其和Product的SupplierID绑定。
//使用TwoWay,这样ComboBox在SelectedItem变动之后,会自动更新Product的SupplierID。
System.Windows.Data.Binding bind = new System.Windows.Data.Binding("SupplierID");
bind.Mode = System.Windows.Data.BindingMode.TwoWay;
cb.SetBinding(ComboBoxClassic.SelectedValueProperty, bind);
}