转帖|其它|编辑:郝浩|2011-03-25 16:26:57.000|阅读 1383 次
概述:本文介绍为silverlight DataGrid增加AutoCompleteBox列的两种方法,众所周知DataGrid有个模板列 (DataGridTempleteColumn)列,其功能非常强大,在模板列中基本上什么组件都可以放,因此我们不难想到用模板列很容易就可以实现. 下面展示模板列的方式的几个步骤。
# 慧都年终大促·界面/图表报表/文档/IDE等千款热门软控件火热促销中 >>
本文介绍为silverlight DataGrid增加AutoCompleteBox列的两种方法,众所周知DataGrid有个模板列(DataGridTempleteColumn)列,其功能非常强大,在模板列中基本上什么组件都可以放,因此我们不难想到用模板列很容易就可以实现.下面展示模板列的方式的几个步骤:
1.在Resource中定义AutoCompleteBox的ItemTemplate,定义下拉列表中的每一项的显示方式及其内容.
<data:DataGrid.Resources>
<DataTemplate x:Name="currencyDataTemplate">
<TextBlock Text="{Binding Path=Name}"/>
</DataTemplate>
</data:DataGrid.Resources>
2.定义AutoCompleteBox的数据源
<data:DataGrid.Resources>
<riaControls:DomainDataSource x:Key=
"currencyDomainDataSource" AutoLoad="True"
d:DesignData="{d:DesignInstance models:Currency, CreateList=true}" Height="0"
LoadedData= "currencyDomainDataSource_LoadedData"
Name="currencyDomainDataSource"
QueryName= "GetCurrenciesQuery" Width="0">
<riaControls:DomainDataSource.DomainContext>
<services:MtsDomainContext />
</riaControls:DomainDataSource.DomainContext>
</riaControls:DomainDataSource>
<DataTemplate x:Name="currencyDataTemplate">
<TextBlock Text="{Binding Path=Name}"/>
</DataTemplate>
</data:DataGrid.Resources>
2.定义DataGridTemplateColumn字段
<data:DataGridTemplateColumn x:Name="currencyColumn"
Header="Currency" Width="SizeToHeader">
<data:DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding Currency}" VerticalAlignment=
"Center" Margin="3"/>
</DataTemplate>
</data:DataGridTemplateColumn.CellTemplate>
<data:DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<input:AutoCompleteBox x:Name="currencyAutoComplete" Text="{Binding Path=CurrencyID,Mode=TwoWay}"
ItemsSource= "{Binding Path=Data,Source=
{StaticResource currencyDomainDataSource}}"
ValueMemberPath= "Name" ItemTemplate=
"{StaticResource currencyDataTemplate}" />
</DataTemplate>
</data:DataGridTemplateColumn.CellEditingTemplate>
</data:DataGridTemplateColumn>
3.如果需要,实现转换器类,实现IValueConverter接口,binding表达式中指定转换器
在上面例子中数据源的CurrencyID对应Currency的ID字段,用户输入时输入Currency的Name字段,因此需要ID和Name之间进行转换.
public class CurrencyValueConverter : IValueConverter
{
#region IValueConverter Members
public object Convert(object value, Type targetType,
object parameter, System.Globalization.CultureInfo culture)
{
//这里实现转换成Name
return value;
}
public object ConvertBack(object value, Type targetType,
object parameter, System.Globalization.CultureInfo culture)
{
//这里实现从Name转换成ID
return value;
}
#endregion
4.在Resouces中声明Converter
5.修改binding语句指定Converter及其Convert的参数等信息.
经过以上步骤基本完成了AutoCompleteBox列增加.
上述方法有以下问题:
1.步骤比较多
2.为每个使用到AutoCompleteBox列的地方都要以上处理,重复工作较多
3.为每个类似Key,Value的转换都要写个Converter类
那么我们能否写个类似DataGridTextColumn的DataGridAutoCompleteColumn类,通过简单的绑定就可以完成以上工作呢?答案肯定是可以的.
下面让我们来动手实现这个DataGridAutoCompleteColumn吧!
设计DataGridAutoCompleteColumn类面临几个问题:
1.为我们的DataGridAutoCompleteColumn选择一个合适的基类,并重写相应的方法
2.实现一个通用的转换器,完成Key,Value的映射
解决方法:
对于第一个问题我们选择DataGridBoundColumn类做为基类,重写GenerateElement,GenerateEditingElement,CancelCellEdit,PrepareCellForEdit等方法.
对于第二个问题我们指定ValueMemberPath,DisplayMemberPath,利用反射机制来实现这两个字段间的映射即可。下面是我实现的代码:
DataGridAutoCompleteColumn实现代码:
public class DataGridAutoCompleteColumn : DataGridBoundColumn
{
public DataGridAutoCompleteColumn()
{
//this.DefaultStyleKey = typeof(DataGridAutoCompleteColumn);
//this._converter = new DataGridAutoCompleteColumnConverter(this);
}
public override System.Windows.Data.Binding Binding
{
get
{
return base.Binding;
}
set
{
if (value != null)
{
if (value.Converter == null)//如果没指定转换器,指定通用转换器来转
{
value.Converter = new DataGridAutoCompleteColumnConverter();
}
}
base.Binding = value;
}
}
private IValueConverter Converter
{
get
{
if (this.Binding != null)
{
return this.Binding.Converter;
}
return null;
}
//set { this._converter = value; }
}
protected override FrameworkElement GenerateElement
(DataGridCell cell, object dataItem)
{
//throw new NotImplementedException();
TextBlock block = new TextBlock();
block.Margin = new Thickness(4.0);
block.VerticalAlignment = VerticalAlignment.Center;
if ((this.Binding != null) || !DesignerProperties.IsInDesignTool)
{
block.SetBinding(TextBlock.TextProperty, this.Binding);
}
return block;
}
protected override FrameworkElement GenerateEditingElement
(DataGridCell cell, object dataItem)
{
AutoCompleteBox box = new AutoCompleteBox();
box.VerticalAlignment = VerticalAlignment.Center;
box.Background = new SolidColorBrush(Colors.Transparent);
if ((this.Binding != null) || !DesignerProperties.IsInDesignTool)
{
box.ItemsSource = this.ItemsSource;
DataTemplate itemTemplate = this.ItemTemplate;
if (itemTemplate == null && !String.IsNullOrEmpty(this.DisplayMemberPath))
{
string xaml = "<DataTemplate xmlns=
\"//schemas.microsoft.com/winfx/2006/xaml/presentation\">
<TextBlock Text=\"{Binding Path=" +
this.DisplayMemberPath + "}\" /> </DataTemplate>";
itemTemplate = (DataTemplate)XamlReader.Load(xaml);
}
box.ItemTemplate = itemTemplate;
if (!String.IsNullOrEmpty(DisplayMemberPath))
{
Binding valueBinding = new Binding(this.DisplayMemberPath);
box.ValueMemberBinding = valueBinding;
}
else if (!String.IsNullOrEmpty(ValueMemberPath))
{
box.ValueMemberPath = this.ValueMemberPath;
}
box.SetBinding(AutoCompleteBox.TextProperty, this.Binding);
//box.SetBinding(base.BindingTarget, this.Binding);
}
return box;
}
protected override void CancelCellEdit
(FrameworkElement editingElement, object uneditedValue)
{
//base.CancelCellEdit(editingElement, uneditedValue);
AutoCompleteBox box = editingElement as AutoCompleteBox;
if (box != null)
{
if (this.RequiredConverter)
{
box.Text = (string)this.Converter.Convert(uneditedValue, typeof(string),
null, System.Globalization.CultureInfo.CurrentCulture);
}
else if(uneditedValue != null)
{
box.Text = uneditedValue.ToString();
}
}
}
protected override object PrepareCellForEdit
(FrameworkElement editingElement, RoutedEventArgs editingEventArgs)
{
AutoCompleteBox box = editingElement as AutoCompleteBox;
if (box != null)
{
return box.Text;
}
else
{
return string.Empty;
}
//return base.PrepareCellForEdit(editingElement, editingEventArgs);
}
private bool RequiredConverter
{
get
{
return !String.IsNullOrEmpty(this.ValueMemberPath)
&& !String.IsNullOrEmpty(this.DisplayMemberPath);
}
}
#region AutoComplete
public string ValueMemberPath
{
get { return GetValue(ValueMemberPathProperty) as string; }
set { SetValue(ValueMemberPathProperty, value); }
}
// Using a DependencyProperty as the backing store for ValueMemberPath.
This enables animation, styling, binding, etc...
public static readonly DependencyProperty ValueMemberPathProperty =
DependencyProperty.Register( "ValueMemberPath", typeof(string), typeof(DataGridAutoCompleteColumn),
new PropertyMetadata(null, OnMemberPathPropertyChanged));
public string DisplayMemberPath
{
get { return GetValue(DisplayMemberPathProperty) as string; }
set { SetValue(DisplayMemberPathProperty, value); }
}
// Using a DependencyProperty as the backing store for DisplayMemberPath.
This enables animation, styling, binding, etc...
public static readonly DependencyProperty DisplayMemberPathProperty =
DependencyProperty.Register( "DisplayMemberPath", typeof(string), typeof(DataGridAutoCompleteColumn),
new PropertyMetadata(null, OnMemberPathPropertyChanged));
public IEnumerable ItemsSource
{
get { return GetValue(ItemsSourceProperty) as IEnumerable; }
set { SetValue(ItemsSourceProperty, value); }
}
// Using a DependencyProperty as the backing store for ItemsSource.
This enables animation, styling, binding, etc...
public static readonly DependencyProperty ItemsSourceProperty =
DependencyProperty.Register( "ItemsSource", typeof(IEnumerable),
typeof(DataGridAutoCompleteColumn), new PropertyMetadata
(null, OnItemsSourcePropertyChanged));
public DataTemplate ItemTemplate
{
get { return GetValue(ItemTemplateProperty) as DataTemplate; }
set { SetValue(ItemTemplateProperty, value); }
}
// Using a DependencyProperty as the backing store for ItemTemplate.
This enables animation, styling, binding, etc...
public static readonly DependencyProperty ItemTemplateProperty =
DependencyProperty.Register( "ItemTemplate", typeof(DataTemplate), typeof(DataGridAutoCompleteColumn),
new PropertyMetadata(null));
private static void OnMemberPathPropertyChanged(DependencyObject d,
DependencyPropertyChangedEventArgs e)
{
DataGridAutoCompleteColumn control = d as DataGridAutoCompleteColumn;
if (control != null)
{
control.OnMemberPathChanged();
}
}
private void OnMemberPathChanged()
{
//set binding converter
DataGridAutoCompleteColumnConverter converter =
this.Converter as DataGridAutoCompleteColumnConverter;
if (converter != null)
{
converter.ValueMember = this.ValueMemberPath;
converter.DisplayMember = this.DisplayMemberPath;
}
}
private static void OnItemsSourcePropertyChanged
(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
DataGridAutoCompleteColumn control = d as DataGridAutoCompleteColumn;
if (control != null)
{
control.OnItemsSourceChanged();
}
}
private void OnItemsSourceChanged()
{
IValueConverter converter = this.Converter;//this._converter as DataGridAutoCompleteColumnConverter;
if (converter != null && converter is DataGridAutoCompleteColumnConverter)
{
((DataGridAutoCompleteColumnConverter)converter).ItemsSource =
this.ItemsSource;
}
}
#endregion
}
DataGridAutoCompleteColumnConverter类实现代码:
class DataGridAutoCompleteColumnConverter : IValueConverter
{
private IEnumerable _itemsSource;
private PropertyInfo _valuePropertyInfo;
private PropertyInfo _displayPropertyInfo;
private Type _elementType;
private bool _initialized = false;
public string DisplayMember { get; set; }
public string ValueMember { get; set; }
public IEnumerable ItemsSource
{
get { return this._itemsSource; }
set
{
this._itemsSource = value;
_initialized = false;
}
}
private void Init()
{
if (_initialized) return;
if (this.ItemsSource != null && !String.IsNullOrEmpty(this.DisplayMember)
&& !String.IsNullOrEmpty(this.ValueMember))
{
IEnumerator enumrator = this.ItemsSource.GetEnumerator();
enumrator.MoveNext();
object current = enumrator.Current;
Type type = current.GetType();
if (current != null)
if (!String.IsNullOrEmpty(this.ValueMember))
{
_valuePropertyInfo = type.GetProperty(this.ValueMember);
}
if (!String.IsNullOrEmpty(this.DisplayMember))
{
_displayPropertyInfo = type.GetProperty(this.DisplayMember);
}
_elementType = type;
_initialized = true;
}
}
else
{
this._valuePropertyInfo = null;
this._displayPropertyInfo = null;
}
}
#region IValueConverter Members
public object Convert(object value, Type targetType, object parameter,
System.Globalization.CultureInfo culture)
{
if (String.IsNullOrEmpty(this.DisplayMember)
&& String.IsNullOrEmpty(this.ValueMember))
return value;
this.Init();
if (this._displayPropertyInfo == null) return value;
if (targetType == this._displayPropertyInfo.PropertyType)
{
if (ItemsSource == null)
return value;
if (value.GetType() == this._elementType)
{
return this._displayPropertyInfo.GetValue(value, null);
}
object item = null;
foreach (object o in this.ItemsSource)
{
if (value.Equals(this._valuePropertyInfo.GetValue(o, null)))
{
item = o;
break;
}
}
if (item != null)
{
return this._displayPropertyInfo.GetValue(item, null);
}
}
return value;
}
public object ConvertBack(object value, Type targetType, object parameter,
System.Globalization.CultureInfo culture)
{
this.Init();
if(value == null)return value;
if (this._displayPropertyInfo != null)
{
object item = null;
foreach (object o in this.ItemsSource)
{
if (value.Equals(this._displayPropertyInfo.GetValue(o, null)))
{
item = o;
break;
}
}
if (item != null && this._valuePropertyInfo != null)
{
return this._valuePropertyInfo.GetValue(item, null);
}
return item;
}
else if(this._valuePropertyInfo != null && value.GetType() == this._elementType)
{
return this._valuePropertyInfo.GetValue(value, null);
}
else
{
if ((targetType != null) && targetType.IsClass)
{
string str = value as string;
if (str == string.Empty)
{
return null;
}
}
return value;
}
}
#endregion
}
到此我们的DataGridAutoCompleteColumn类基本开发完成.
使用时我们只需要以下声明就可以:
<local:DataGridAutoCompleteColumn x:Name="currencyColumn"
Binding="{Binding Path=CurrencyID}"
ItemsSource= "{Binding Source=
{StaticResource currencyDomainDataSource},Path=Data}"
ValueMemberPath= "ID" DisplayMemberPath="Name"
Header="Name" Width="SizeToHeader"/>
当然在Resource里面还是要定义ItemsSource的数据源的。
本站文章除注明转载外,均为本站原创或翻译。欢迎任何形式的转载,但请务必注明出处、不得修改原文相关链接,如果存在内容上的异议请邮件反馈至chenjj@pclwef.cn
文章转载自:网络转载