using System;
using System.Collections;
using System.Web.UI;
namespace Spring.DataBinding{
/// <summary>
/// This formatter acts as an adapter to a datasource. It parses a given datasource into
/// a table to allow converting back and forth between objects and their keys during formatting.
/// </summary>
/// <remarks>
/// <para>
/// The DataSourceItemFormatter expects the "source" to have 2 properties named "DataSourceField" and "DataValueField".
/// </para>
/// </remarks>
public class DataSourceItemFormatter : IBindingAwareFormatter
{
private string _dataSourceFieldName;
private string _dataValueFieldName;
[ThreadStatic]
private Hashtable _dataItemsByKey;
[ThreadStatic]
private string _dataItemKeyField;
/// <summary>
/// Initialize this instance.
/// </summary>
/// <param name="dataSourceField">The name of the "source"s property containing the dataSource</param>
/// <param name="dataValueField">The name of the "source"s property containing the name of the key property</param>
public DataSourceItemFormatter(string dataSourceField, string dataValueField)
{
this._dataSourceFieldName = dataSourceField;
if(dataValueField == null)
{
throw new ArgumentNullException("DataValueField must not be null.");
}
this._dataValueFieldName = dataValueField;
}
/// <summary>
/// Sets the bindingContext for the current thread.
/// </summary>
/// <param name="source">The source object</param>
/// <param name="target">The target object</param>
/// <param name="variables">Variables to be used during binding</param>
/// <param name="direction">The current binding's direction</param>
public void SetBindingContext(object source, object target, IDictionary variables, BindingDirection direction)
{
// Extract dataSource from source object
IEnumerable dataSource = DataBinder.GetPropertyValue(source, this._dataSourceFieldName) as IEnumerable;
// Extract dataItemKeyField from source object
string dataItemKeyField = DataBinder.GetPropertyValue(source, this._dataValueFieldName) as String;
Initialize(dataSource, dataItemKeyField, direction);
}
/// <summary>
/// Reset the current thread's binding context.
/// </summary>
public void ClearBindingContext()
{
_dataItemsByKey = null;
_dataItemKeyField = null;
}
/// <summary>
/// Initialize a new instance.
/// </summary>
/// <param name="dataSource">The datasource containing list items</param>
/// <param name="dataItemKeyField">The name of the listitem's property that evaluates to the item's key</param>
/// <param name="direction">The direction of the current binding</param>
private void Initialize(IEnumerable dataSource, string dataItemKeyField, BindingDirection direction)
{
_dataItemKeyField = dataItemKeyField;
// if we are binding from target to source only, we can save some performance
// here because only Format() will be called.
if (direction != (direction&BindingDirection.TargetToSource))
{
if(dataSource == null)
{
throw new ArgumentNullException(
string.Format("DataSource must not be null."));
}
_dataItemsByKey = new Hashtable();
foreach(object dataItem in dataSource)
{
string key = Format(dataItem);
this._dataItemsByKey.Add(key, dataItem);
}
}
}
/// <summary>
/// Returns the key of the specified value object.
/// </summary>
/// <param name="value">The value to extract the key from.</param>
/// <returns>Key extracted from <paramref name="value"/>.</returns>
public string Format(object value)
{
return DataBinder.GetPropertyValue(value, this._dataItemKeyField, null);
}
/// <summary>
/// Return the object with the specified key from the datasource.
/// </summary>
/// <param name="key">The key of the object.</param>
/// <returns>Parsed <paramref name="value"/>.</returns>
public object Parse(string key)
{
object item = this._dataItemsByKey[key];
if (item==null)
{
throw new ArgumentOutOfRangeException("key", key, string.Format("Item with key {0} does not exist", key));
}
return item;
}
}
}
|