//------------------------------------------------------------------------------ // // Copyright (c) Microsoft Corporation. All rights reserved. // //------------------------------------------------------------------------------ namespace System.Web.UI.WebControls { using System; using System.Collections.Specialized; using System.ComponentModel; using System.Globalization; using System.Web; using System.Web.Util; /// /// Creates a field bounded to a data field in a . /// public class BoundField : DataControlField { /// /// Specifies a string that represents "this". This field is read-only. /// public static readonly string ThisExpression = "!"; private static readonly string _expressionPartSeparator = "."; private PropertyDescriptor _boundFieldDesc; private bool _boundFieldDescInitialized; string _dataField; string _dataFormatString; bool _htmlEncode; bool _htmlEncodeSet = false; bool _suppressHeaderTextFieldChange; private bool _htmlEncodeFormatString; private bool _htmlEncodeFormatStringSet; /// /// Initializes a new instance of a class. /// public BoundField() { } /// /// Determines whether the control validates client input or not, defaults to inherit from parent. /// [ WebCategory("Behavior"), WebSysDescription(SR.Control_ValidateRequestMode), DefaultValue(ValidateRequestMode.Inherit) ] public new ValidateRequestMode ValidateRequestMode { get { return base.ValidateRequestMode; } set { base.ValidateRequestMode = value; } } /// /// Indicates whether to apply the DataFormatString in edit mode /// [ WebCategory("Behavior"), DefaultValue(false), WebSysDescription(SR.BoundField_ApplyFormatInEditMode) ] public virtual bool ApplyFormatInEditMode { get { object o = ViewState["ApplyFormatInEditMode"]; if (o != null) { return (bool)o; } return false; } set { ViewState["ApplyFormatInEditMode"] = value; } } /// /// Gets or sets the property that determines whether the BoundField treats empty string as /// null when the field values are extracted. /// [ WebCategory("Behavior"), DefaultValue(true), WebSysDescription(SR.BoundField_ConvertEmptyStringToNull) ] public virtual bool ConvertEmptyStringToNull { get { object o = ViewState["ConvertEmptyStringToNull"]; if (o != null) { return (bool)o; } return true; } set { ViewState["ConvertEmptyStringToNull"] = value; } } /// /// Gets or sets the field name from the data model bound to this field. /// [ WebCategory("Data"), DefaultValue(""), TypeConverter("System.Web.UI.Design.DataSourceViewSchemaConverter, " + AssemblyRef.SystemDesign), WebSysDescription(SR.BoundField_DataField) ] public virtual string DataField { get { if (_dataField == null) { object o = ViewState["DataField"]; if (o != null) _dataField = (string)o; else _dataField = String.Empty; } return _dataField; } set { if (!String.Equals(value, ViewState["DataField"])) { ViewState["DataField"] = value; _dataField = value; OnFieldChanged(); } } } /// /// Gets or sets the display format of data in this /// field. /// [ WebCategory("Data"), DefaultValue(""), WebSysDescription(SR.BoundField_DataFormatString) ] public virtual string DataFormatString { get { if (_dataFormatString == null) { object o = ViewState["DataFormatString"]; if (o != null) _dataFormatString = (string)o; else _dataFormatString = String.Empty; } return _dataFormatString; } set { if (!String.Equals(value, ViewState["DataFormatString"])) { ViewState["DataFormatString"] = value; _dataFormatString = value; OnFieldChanged(); } } } /// /// Gets or sets the text displayed in the header of the /// System.Web.UI.WebControls.Field. /// public override string HeaderText { get { return base.HeaderText; } set { if (!String.Equals(value, ViewState["HeaderText"])) { ViewState["HeaderText"] = value; if (!_suppressHeaderTextFieldChange) { OnFieldChanged(); } } } } /// /// Gets or sets a property indicating whether data should be HtmlEncoded when it is displayed to the user. /// [ WebCategory("Behavior"), DefaultValue(true), WebSysDescription(SR.BoundField_HtmlEncode) ] public virtual bool HtmlEncode { get { if (!_htmlEncodeSet) { object o = ViewState["HtmlEncode"]; if (o != null) { _htmlEncode = (bool)o; } else { _htmlEncode = true; } _htmlEncodeSet = true; } return _htmlEncode; } set { object oldValue = ViewState["HtmlEncode"]; if (oldValue == null || (bool)oldValue != value) { ViewState["HtmlEncode"] = value; _htmlEncode = value; _htmlEncodeSet = true; OnFieldChanged(); } } } /// /// Gets or sets a property indicating whether the format string should be HtmlEncoded /// when it is displayed to the user. /// [ WebCategory("Behavior"), DefaultValue(true), ] public virtual bool HtmlEncodeFormatString { get { if (!_htmlEncodeFormatStringSet) { object o = ViewState["HtmlEncodeFormatString"]; if (o != null) { _htmlEncodeFormatString = (bool)o; } else { _htmlEncodeFormatString = true; } _htmlEncodeFormatStringSet = true; } return _htmlEncodeFormatString; } set { object oldValue = ViewState["HtmlEncodeFormatString"]; if (oldValue == null || (bool)oldValue != value) { ViewState["HtmlEncodeFormatString"] = value; _htmlEncodeFormatString = value; _htmlEncodeFormatStringSet = true; OnFieldChanged(); } } } /// /// Gets or sets the property that determines what text is displayed if the value /// of the field is null. /// [ WebCategory("Behavior"), DefaultValue(""), WebSysDescription(SR.BoundField_NullDisplayText) ] public virtual string NullDisplayText { get { object o = ViewState["NullDisplayText"]; if (o != null) { return (string)o; } return String.Empty; } set { if (!String.Equals(value, ViewState["NullDisplayText"])) { ViewState["NullDisplayText"] = value; OnFieldChanged(); } } } /// /// Gets or sets the property that prevents modification to data /// in this field. /// [ WebCategory("Behavior"), DefaultValue(false), WebSysDescription(SR.BoundField_ReadOnly) ] public virtual bool ReadOnly { get { object o = ViewState["ReadOnly"]; if (o != null) return (bool)o; return false; } set { object oldValue = ViewState["ReadOnly"]; if (oldValue == null || (bool)oldValue != value) { ViewState["ReadOnly"] = value; OnFieldChanged(); } } } protected virtual bool SupportsHtmlEncode { get { return true; } } protected override void CopyProperties(DataControlField newField) { ((BoundField)newField).ApplyFormatInEditMode = ApplyFormatInEditMode; ((BoundField)newField).ConvertEmptyStringToNull = ConvertEmptyStringToNull; ((BoundField)newField).DataField = DataField; ((BoundField)newField).DataFormatString = DataFormatString; ((BoundField)newField).HtmlEncode = HtmlEncode; ((BoundField)newField).HtmlEncodeFormatString = HtmlEncodeFormatString; ((BoundField)newField).NullDisplayText = NullDisplayText; ((BoundField)newField).ReadOnly = ReadOnly; base.CopyProperties(newField); } protected override DataControlField CreateField() { return new BoundField(); } /// /// Extracts the value(s) from the given cell and puts the value(s) into a dictionary. Indicate includeReadOnly /// to have readonly fields' values inserted into the dictionary. /// public override void ExtractValuesFromCell(IOrderedDictionary dictionary, DataControlFieldCell cell, DataControlRowState rowState, bool includeReadOnly) { Control childControl = null; string dataField = DataField; object value = null; string nullDisplayText = NullDisplayText; if (((rowState & DataControlRowState.Insert) != 0) && !InsertVisible) { return; } if (cell.Controls.Count > 0) { childControl = cell.Controls[0]; TextBox editBox = childControl as TextBox; if (editBox != null) { value = editBox.Text; } } else { if (includeReadOnly == true) { string cellText = cell.Text; if (cellText == " ") { // nothing HtmlEncodes to  , so we know that this means it was empty. value = String.Empty; } else { if (SupportsHtmlEncode && HtmlEncode) { value = HttpUtility.HtmlDecode(cellText); } else { value = cellText; } } } } if (value != null) { if ((value is string) && (((string)value).Length == 0) && ConvertEmptyStringToNull) { value = null; } if (value is string && (string)value == nullDisplayText && nullDisplayText.Length > 0) { value = null; } if (dictionary.Contains(dataField)) { dictionary[dataField] = value; } else { dictionary.Add(dataField, value); } } } /// /// Returns the value of the field formatted as text for the cell. /// protected virtual string FormatDataValue(object dataValue, bool encode) { string formattedValue = String.Empty; if (!DataBinder.IsNull(dataValue)) { string dataValueString = dataValue.ToString(); string formatting = DataFormatString; int dataValueStringLength = dataValueString.Length; if (!HtmlEncodeFormatString) { // Back-compat (Whidbey) behavior when HtmlEncodeFormatString=false if (dataValueStringLength > 0 && encode) { dataValueString = HttpUtility.HtmlEncode(dataValueString); } if (dataValueStringLength == 0 && ConvertEmptyStringToNull) { formattedValue = NullDisplayText; } else if (formatting.Length == 0) { formattedValue = dataValueString; } else { if (encode) { formattedValue = String.Format(CultureInfo.CurrentCulture, formatting, dataValueString); } else { formattedValue = String.Format(CultureInfo.CurrentCulture, formatting, dataValue); } } } else { // New default behavior (Orcas) when HtmlEncodeFormatString=true // If the result is still empty and ConvertEmptyStringToNull=true, replace the value with the NullDisplayText if (dataValueStringLength == 0 && ConvertEmptyStringToNull) { dataValueString = NullDisplayText; } else { // If there's a format string, apply it to the raw data value // If there's no format string, then dataValueString already has the right value if (!String.IsNullOrEmpty(formatting)) { dataValueString = String.Format(CultureInfo.CurrentCulture, formatting, dataValue); } // Optionally HTML encode the value (including the format string, if any was applied) if (!String.IsNullOrEmpty(dataValueString) && encode) { dataValueString = HttpUtility.HtmlEncode(dataValueString); } } formattedValue = dataValueString; } } else { formattedValue = NullDisplayText; } return formattedValue; } /// /// Returns a value to be used for design-time rendering /// protected virtual object GetDesignTimeValue() { return SR.GetString(SR.Sample_Databound_Text); } /// /// Retrieves the value of the field to be databound to the BoundField. /// protected virtual object GetValue(Control controlContainer) { Debug.Assert(DataField.Length != 0, "Shouldn't be DataBinding without a DataField"); object data = null; object dataItem = null; string boundField = DataField; if (controlContainer == null) { throw new HttpException(SR.GetString(SR.DataControlField_NoContainer)); } dataItem = DataBinder.GetDataItem(controlContainer); if (dataItem == null) { if (DesignMode) { return GetDesignTimeValue(); } else { throw new HttpException(SR.GetString(SR.DataItem_Not_Found)); } } if (boundField.Equals(ThisExpression)) { if (DesignMode) { return GetDesignTimeValue(); } else { return dataItem; } } if (!TryGetSimplePropertyValue(dataItem, out data)) { data = DataBinder.Eval(dataItem, boundField); } return data; } private bool TryGetSimplePropertyValue(object dataItem, out object data) { string boundField = DataField; data = null; if (!_boundFieldDescInitialized) { //For simple properties , we cache the property descriptor for performance reasons. _boundFieldDesc = TypeDescriptor.GetProperties(dataItem).Find(boundField, true); _boundFieldDescInitialized = true; } if (_boundFieldDesc != null) { // simple property case data = _boundFieldDesc.GetValue(dataItem); return true; } else if (DesignMode) { data = GetDesignTimeValue(); return true; } else if (!boundField.Contains(_expressionPartSeparator)) { throw new HttpException(SR.GetString(SR.Field_Not_Found, boundField)); } else { // complex property case return false; } } /// /// Initializes the field and resets member variables. /// public override bool Initialize(bool enableSorting, Control control) { base.Initialize(enableSorting, control); _boundFieldDesc = null; _boundFieldDescInitialized = false; return false; } /// /// Initializes a cell in the DataControlField. /// public override void InitializeCell(DataControlFieldCell cell, DataControlCellType cellType, DataControlRowState rowState, int rowIndex) { string unencodedHeaderText = null; bool changedHeaderText = false; bool encode = false; // if this is a header cell and we're htmlEncoding, htmlEncode the HeaderText before the base class tries to render it if (cellType == DataControlCellType.Header && SupportsHtmlEncode && HtmlEncode) { unencodedHeaderText = HeaderText; encode = true; } if (encode && !String.IsNullOrEmpty(unencodedHeaderText)) { _suppressHeaderTextFieldChange = true; HeaderText = HttpUtility.HtmlEncode(unencodedHeaderText); changedHeaderText = true; } base.InitializeCell(cell, cellType, rowState, rowIndex); // restore the HeaderText property if (changedHeaderText) { HeaderText = unencodedHeaderText; _suppressHeaderTextFieldChange = false; } switch (cellType) { case DataControlCellType.DataCell: InitializeDataCell(cell, rowState); break; } } protected virtual void InitializeDataCell(DataControlFieldCell cell, DataControlRowState rowState) { Control childControl = null; Control boundControl = null; if (((rowState & DataControlRowState.Edit) != 0 && ReadOnly == false) || (rowState & DataControlRowState.Insert) != 0) { // TextBox editor = new TextBox(); editor.ToolTip = HeaderText; childControl = editor; if (DataField.Length != 0 && (rowState & DataControlRowState.Edit) != 0) { boundControl = editor; } } else if (DataField.Length != 0) { boundControl = cell; } if (childControl != null) { cell.Controls.Add(childControl); } if (boundControl != null && Visible) { boundControl.DataBinding += new EventHandler(this.OnDataBindField); } } /// /// protected virtual void OnDataBindField(object sender, EventArgs e) { Control boundControl = (Control)sender; Control controlContainer = boundControl.NamingContainer; object data = GetValue(controlContainer); bool encodeValue = SupportsHtmlEncode && HtmlEncode && boundControl is TableCell; string dataValue = FormatDataValue(data, encodeValue); if (boundControl is TableCell) { if (dataValue.Length == 0) { dataValue = " "; } ((TableCell)boundControl).Text = dataValue; } else { if (!(boundControl is TextBox)) { throw new HttpException(SR.GetString(SR.BoundField_WrongControlType, DataField)); } if (ApplyFormatInEditMode) { ((TextBox)boundControl).Text = dataValue; } else { if (data != null) { ((TextBox)boundControl).Text = data.ToString(); } } if (data != null) { // size down the textbox for certain types if (data.GetType().IsPrimitive) { ((TextBox)boundControl).Columns = 5; } } } } protected override void LoadViewState(object state) { // DevDiv Bugs 188902: Clear cached values as they may be stale after ViewState loads. _dataField = null; _dataFormatString = null; _htmlEncodeSet = false; _htmlEncodeFormatStringSet = false; base.LoadViewState(state); } /// /// Override with an empty body if the field's controls all support callback. /// Otherwise, override and throw a useful error message about why the field can't support callbacks. /// public override void ValidateSupportsCallback() { } } }