轉帖|其它|編輯:郝浩|2011-03-25 16:26:57.000|閱讀 1406 次
概述:本文介紹為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@fc6vip.cn
文章轉載自:網絡轉載