e79aa3c0ed
Former-commit-id: a2155e9bd80020e49e72e86c44da02a8ac0e57a4
1040 lines
36 KiB
C#
1040 lines
36 KiB
C#
//------------------------------------------------------------------------------
|
|
// <copyright file="Repeater.cs" company="Microsoft">
|
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
// </copyright>
|
|
//------------------------------------------------------------------------------
|
|
|
|
namespace System.Web.UI.WebControls {
|
|
using System;
|
|
using System.Collections;
|
|
using System.ComponentModel;
|
|
using System.ComponentModel.Design;
|
|
using System.Web;
|
|
using System.Web.UI;
|
|
using System.Web.Util;
|
|
|
|
/// <devdoc>
|
|
/// <para>Defines the properties, methods, and events of a <see cref='System.Web.UI.WebControls.Repeater'/> class.</para>
|
|
/// </devdoc>
|
|
[
|
|
DefaultEvent("ItemCommand"),
|
|
DefaultProperty("DataSource"),
|
|
Designer("System.Web.UI.Design.WebControls.RepeaterDesigner, " + AssemblyRef.SystemDesign),
|
|
ParseChildren(true),
|
|
PersistChildren(false)
|
|
]
|
|
public class Repeater : Control, INamingContainer {
|
|
|
|
private static readonly object EventItemCreated = new object();
|
|
private static readonly object EventItemDataBound = new object();
|
|
private static readonly object EventItemCommand = new object();
|
|
private static readonly object EventCreatingModelDataSource = new object();
|
|
private static readonly object EventCallingDataMethods = new object();
|
|
|
|
internal const string ItemCountViewStateKey = "_!ItemCount";
|
|
|
|
private object dataSource;
|
|
private ITemplate headerTemplate;
|
|
private ITemplate footerTemplate;
|
|
private ITemplate itemTemplate;
|
|
private ITemplate alternatingItemTemplate;
|
|
private ITemplate separatorTemplate;
|
|
|
|
private ArrayList itemsArray;
|
|
private RepeaterItemCollection itemsCollection;
|
|
|
|
private bool _requiresDataBinding;
|
|
private bool _inited;
|
|
private bool _throwOnDataPropertyChange;
|
|
|
|
private DataSourceView _currentView;
|
|
private bool _currentViewIsFromDataSourceID;
|
|
private bool _currentViewValid;
|
|
private DataSourceSelectArguments _arguments;
|
|
private bool _pagePreLoadFired;
|
|
|
|
private string _itemType;
|
|
private string _selectMethod;
|
|
private ModelDataSource _modelDataSource;
|
|
private bool _asyncSelectPending;
|
|
|
|
|
|
/// <devdoc>
|
|
/// <para>Initializes a new instance of the <see cref='System.Web.UI.WebControls.Repeater'/> class.</para>
|
|
/// </devdoc>
|
|
public Repeater() {
|
|
}
|
|
|
|
private bool IsUsingModelBinders {
|
|
get {
|
|
return !String.IsNullOrEmpty(SelectMethod);
|
|
}
|
|
}
|
|
|
|
private void UpdateModelDataSourceProperties(ModelDataSource modelDataSource) {
|
|
if (modelDataSource == null) {
|
|
throw new ArgumentNullException("modelDataSource");
|
|
}
|
|
|
|
modelDataSource.UpdateProperties(ItemType, SelectMethod);
|
|
}
|
|
|
|
private ModelDataSource ModelDataSource {
|
|
get {
|
|
if (_modelDataSource == null) {
|
|
_modelDataSource = new ModelDataSource(this);
|
|
}
|
|
return _modelDataSource;
|
|
}
|
|
set {
|
|
if (value == null) {
|
|
throw new ArgumentNullException("value");
|
|
}
|
|
|
|
_modelDataSource = value;
|
|
}
|
|
}
|
|
|
|
protected virtual void OnCreatingModelDataSource(CreatingModelDataSourceEventArgs e) {
|
|
CreatingModelDataSourceEventHandler handler = Events[EventCreatingModelDataSource] as CreatingModelDataSourceEventHandler;
|
|
if (handler != null) {
|
|
handler(this, e);
|
|
}
|
|
}
|
|
|
|
[
|
|
WebCategory("Data"),
|
|
WebSysDescription(SR.DataBoundControl_OnCreatingModelDataSource)
|
|
]
|
|
public event CreatingModelDataSourceEventHandler CreatingModelDataSource {
|
|
add {
|
|
Events.AddHandler(EventCreatingModelDataSource, value);
|
|
}
|
|
remove {
|
|
Events.RemoveHandler(EventCreatingModelDataSource, value);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// The name of the model type used in the SelectMethod, InsertMethod, UpdateMethod, and DeleteMethod.
|
|
/// </summary>
|
|
[
|
|
DefaultValue(""),
|
|
Themeable(false),
|
|
WebCategory("Data"),
|
|
WebSysDescription(SR.DataBoundControl_ItemType)
|
|
]
|
|
public virtual string ItemType {
|
|
get {
|
|
return _itemType ?? String.Empty;
|
|
}
|
|
set {
|
|
if (!String.Equals(_itemType, value, StringComparison.OrdinalIgnoreCase)) {
|
|
_itemType = value;
|
|
OnDataPropertyChanged();
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// The name of the method on the page which is called when this Control does a select operation.
|
|
/// </summary>
|
|
[
|
|
DefaultValue(""),
|
|
Themeable(false),
|
|
WebCategory("Data"),
|
|
WebSysDescription(SR.DataBoundControl_SelectMethod)
|
|
]
|
|
public virtual string SelectMethod {
|
|
get {
|
|
return _selectMethod ?? String.Empty;
|
|
}
|
|
set {
|
|
if (!String.Equals(_selectMethod, value, StringComparison.OrdinalIgnoreCase)) {
|
|
_selectMethod = value;
|
|
OnDataPropertyChanged();
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Occurs before model methods are invoked for data operations.
|
|
/// Handle this event if the model methods are defined on a custom type other than the code behind file.
|
|
/// </summary>
|
|
[
|
|
WebCategory("Data"),
|
|
WebSysDescription(SR.DataBoundControl_CallingDataMethods)
|
|
]
|
|
public event CallingDataMethodsEventHandler CallingDataMethods {
|
|
add {
|
|
Events.AddHandler(EventCallingDataMethods, value);
|
|
}
|
|
remove {
|
|
Events.RemoveHandler(EventCallingDataMethods, value);
|
|
if (_modelDataSource != null) {
|
|
_modelDataSource.CallingDataMethods -= value;
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <devdoc>
|
|
/// <para>Gets or sets the <see cref='System.Web.UI.ITemplate' qualify='true'/> that defines how alternating (even-indexed) items
|
|
/// are rendered. </para>
|
|
/// </devdoc>
|
|
[
|
|
Browsable(false),
|
|
DefaultValue(null),
|
|
PersistenceMode(PersistenceMode.InnerProperty),
|
|
TemplateContainer(typeof(RepeaterItem)),
|
|
WebSysDescription(SR.Repeater_AlternatingItemTemplate)
|
|
]
|
|
public virtual ITemplate AlternatingItemTemplate {
|
|
get {
|
|
return alternatingItemTemplate;
|
|
}
|
|
set {
|
|
alternatingItemTemplate = value;
|
|
}
|
|
}
|
|
|
|
|
|
public override ControlCollection Controls {
|
|
get {
|
|
EnsureChildControls();
|
|
return base.Controls;
|
|
}
|
|
}
|
|
|
|
|
|
/// <devdoc>
|
|
/// <para>[To be supplied.]</para>
|
|
/// </devdoc>
|
|
[
|
|
DefaultValue(""),
|
|
WebCategory("Data"),
|
|
WebSysDescription(SR.Repeater_DataMember)
|
|
]
|
|
public virtual string DataMember {
|
|
get {
|
|
object o = ViewState["DataMember"];
|
|
if (o != null)
|
|
return (string)o;
|
|
return String.Empty;
|
|
}
|
|
set {
|
|
ViewState["DataMember"] = value;
|
|
OnDataPropertyChanged();
|
|
}
|
|
}
|
|
|
|
|
|
/// <devdoc>
|
|
/// <para> Gets or sets the data source that provides data for
|
|
/// populating the list.</para>
|
|
/// </devdoc>
|
|
[
|
|
Bindable(true),
|
|
WebCategory("Data"),
|
|
DefaultValue(null),
|
|
DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden),
|
|
WebSysDescription(SR.BaseDataBoundControl_DataSource)
|
|
]
|
|
public virtual object DataSource {
|
|
get {
|
|
return dataSource;
|
|
}
|
|
set {
|
|
if ((value == null) || (value is IListSource) || (value is IEnumerable)) {
|
|
dataSource = value;
|
|
OnDataPropertyChanged();
|
|
}
|
|
else {
|
|
throw new ArgumentException(SR.GetString(SR.Invalid_DataSource_Type, ID));
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// The ID of the DataControl that this control should use to retrieve
|
|
/// its data source. When the control is bound to a DataControl, it
|
|
/// can retrieve a data source instance on-demand, and thereby attempt
|
|
/// to work in auto-DataBind mode.
|
|
/// </summary>
|
|
[
|
|
DefaultValue(""),
|
|
IDReferenceProperty(typeof(DataSourceControl)),
|
|
WebCategory("Data"),
|
|
WebSysDescription(SR.BaseDataBoundControl_DataSourceID)
|
|
]
|
|
public virtual string DataSourceID {
|
|
get {
|
|
object o = ViewState["DataSourceID"];
|
|
if (o != null) {
|
|
return (string)o;
|
|
}
|
|
return String.Empty;
|
|
}
|
|
set {
|
|
ViewState["DataSourceID"] = value;
|
|
OnDataPropertyChanged();
|
|
}
|
|
}
|
|
|
|
/// <devdoc>
|
|
/// <para>Gets and sets a value indicating whether theme is enabled.</para>
|
|
/// </devdoc>
|
|
[
|
|
Browsable(true)
|
|
]
|
|
public override bool EnableTheming {
|
|
get {
|
|
return base.EnableTheming;
|
|
}
|
|
set {
|
|
base.EnableTheming = value;
|
|
}
|
|
}
|
|
|
|
|
|
/// <devdoc>
|
|
/// <para>Gets or sets the <see cref='System.Web.UI.ITemplate' qualify='true'/> that defines how the control footer is
|
|
/// rendered. </para>
|
|
/// </devdoc>
|
|
[
|
|
Browsable(false),
|
|
DefaultValue(null),
|
|
PersistenceMode(PersistenceMode.InnerProperty),
|
|
TemplateContainer(typeof(RepeaterItem)),
|
|
WebSysDescription(SR.Repeater_FooterTemplate)
|
|
]
|
|
public virtual ITemplate FooterTemplate {
|
|
get {
|
|
return footerTemplate;
|
|
}
|
|
set {
|
|
footerTemplate = value;
|
|
}
|
|
}
|
|
|
|
|
|
/// <devdoc>
|
|
/// <para>Gets or sets the <see cref='System.Web.UI.ITemplate' qualify='true'/> that defines how the control header is rendered. </para>
|
|
/// </devdoc>
|
|
[
|
|
Browsable(false),
|
|
DefaultValue(null),
|
|
PersistenceMode(PersistenceMode.InnerProperty),
|
|
TemplateContainer(typeof(RepeaterItem)),
|
|
WebSysDescription(SR.WebControl_HeaderTemplate)
|
|
]
|
|
public virtual ITemplate HeaderTemplate {
|
|
get {
|
|
return headerTemplate;
|
|
}
|
|
set {
|
|
headerTemplate = value;
|
|
}
|
|
}
|
|
|
|
|
|
protected bool Initialized {
|
|
get {
|
|
return _inited;
|
|
}
|
|
}
|
|
|
|
|
|
protected bool IsBoundUsingDataSourceID {
|
|
get {
|
|
return (DataSourceID.Length > 0);
|
|
}
|
|
}
|
|
|
|
protected bool IsDataBindingAutomatic {
|
|
get {
|
|
return IsBoundUsingDataSourceID || IsUsingModelBinders;
|
|
}
|
|
}
|
|
|
|
|
|
/// <devdoc>
|
|
/// Gets the <see cref='System.Web.UI.WebControls.RepeaterItem'/> collection.
|
|
/// </devdoc>
|
|
[
|
|
Browsable(false),
|
|
DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden),
|
|
WebSysDescription(SR.Repeater_Items)
|
|
]
|
|
public virtual RepeaterItemCollection Items {
|
|
get {
|
|
if (itemsCollection == null) {
|
|
if (itemsArray == null) {
|
|
EnsureChildControls();
|
|
}
|
|
itemsCollection = new RepeaterItemCollection(itemsArray);
|
|
}
|
|
return itemsCollection;
|
|
}
|
|
}
|
|
|
|
|
|
/// <devdoc>
|
|
/// <para>Gets or sets the <see cref='System.Web.UI.ITemplate' qualify='true'/> that defines how items are rendered. </para>
|
|
/// </devdoc>
|
|
[
|
|
Browsable(false),
|
|
DefaultValue(null),
|
|
PersistenceMode(PersistenceMode.InnerProperty),
|
|
TemplateContainer(typeof(RepeaterItem)),
|
|
WebSysDescription(SR.Repeater_ItemTemplate)
|
|
]
|
|
public virtual ITemplate ItemTemplate {
|
|
get {
|
|
return itemTemplate;
|
|
}
|
|
set {
|
|
itemTemplate = value;
|
|
}
|
|
}
|
|
|
|
|
|
protected bool RequiresDataBinding {
|
|
get {
|
|
return _requiresDataBinding;
|
|
}
|
|
set {
|
|
_requiresDataBinding = value;
|
|
}
|
|
}
|
|
|
|
protected DataSourceSelectArguments SelectArguments {
|
|
get {
|
|
if (_arguments == null) {
|
|
_arguments = CreateDataSourceSelectArguments();
|
|
}
|
|
return _arguments;
|
|
}
|
|
}
|
|
|
|
|
|
/// <devdoc>
|
|
/// <para>Gets or sets the <see cref='System.Web.UI.ITemplate' qualify='true'/> that defines how separators
|
|
/// in between items are rendered.</para>
|
|
/// </devdoc>
|
|
[
|
|
Browsable(false),
|
|
DefaultValue(null),
|
|
PersistenceMode(PersistenceMode.InnerProperty),
|
|
TemplateContainer(typeof(RepeaterItem)),
|
|
WebSysDescription(SR.Repeater_SeparatorTemplate)
|
|
]
|
|
public virtual ITemplate SeparatorTemplate {
|
|
get {
|
|
return separatorTemplate;
|
|
}
|
|
set {
|
|
separatorTemplate = value;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/// <devdoc>
|
|
/// <para>Occurs when a button is clicked within the <see cref='System.Web.UI.WebControls.Repeater'/> control tree.</para>
|
|
/// </devdoc>
|
|
[
|
|
WebCategory("Action"),
|
|
WebSysDescription(SR.Repeater_OnItemCommand)
|
|
]
|
|
public event RepeaterCommandEventHandler ItemCommand {
|
|
add {
|
|
Events.AddHandler(EventItemCommand, value);
|
|
}
|
|
remove {
|
|
Events.RemoveHandler(EventItemCommand, value);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/// <devdoc>
|
|
/// <para> Occurs when an item is created within the <see cref='System.Web.UI.WebControls.Repeater'/> control tree.</para>
|
|
/// </devdoc>
|
|
[
|
|
WebCategory("Behavior"),
|
|
WebSysDescription(SR.DataControls_OnItemCreated)
|
|
]
|
|
public event RepeaterItemEventHandler ItemCreated {
|
|
add {
|
|
Events.AddHandler(EventItemCreated, value);
|
|
}
|
|
remove {
|
|
Events.RemoveHandler(EventItemCreated, value);
|
|
}
|
|
}
|
|
|
|
|
|
/// <devdoc>
|
|
/// <para>Occurs when an item is databound within a <see cref='System.Web.UI.WebControls.Repeater'/> control tree.</para>
|
|
/// </devdoc>
|
|
[
|
|
WebCategory("Behavior"),
|
|
WebSysDescription(SR.DataControls_OnItemDataBound)
|
|
]
|
|
public event RepeaterItemEventHandler ItemDataBound {
|
|
add {
|
|
Events.AddHandler(EventItemDataBound, value);
|
|
}
|
|
remove {
|
|
Events.RemoveHandler(EventItemDataBound, value);
|
|
}
|
|
}
|
|
|
|
|
|
/// <devdoc>
|
|
/// Connects this data bound control to the appropriate DataSourceView
|
|
/// and hooks up the appropriate event listener for the
|
|
/// DataSourceViewChanged event. The return value is the new view (if
|
|
/// any) that was connected to. An exception is thrown if there is
|
|
/// a problem finding the requested view or data source.
|
|
/// </devdoc>
|
|
private DataSourceView ConnectToDataSourceView() {
|
|
if (_currentViewValid && !DesignMode) {
|
|
// If the current view is correct, there is no need to reconnect
|
|
return _currentView;
|
|
}
|
|
|
|
// Disconnect from old view, if necessary
|
|
if ((_currentView != null) && (_currentViewIsFromDataSourceID)) {
|
|
// We only care about this event if we are bound through the DataSourceID property
|
|
_currentView.DataSourceViewChanged -= new EventHandler(OnDataSourceViewChanged);
|
|
}
|
|
|
|
// Connect to new view
|
|
IDataSource ds = null;
|
|
if (!DesignMode && IsUsingModelBinders) {
|
|
if (DataSourceID.Length != 0 || DataSource != null) {
|
|
throw new InvalidOperationException(SR.GetString(SR.DataControl_ItemType_MultipleDataSources, ID));
|
|
}
|
|
//Let the developer choose a custom ModelDataSource.
|
|
CreatingModelDataSourceEventArgs e = new CreatingModelDataSourceEventArgs();
|
|
OnCreatingModelDataSource(e);
|
|
if (e.ModelDataSource != null) {
|
|
ModelDataSource = e.ModelDataSource;
|
|
}
|
|
|
|
//Update the properties of ModelDataSource so that it's ready for data-binding.
|
|
UpdateModelDataSourceProperties(ModelDataSource);
|
|
|
|
//Add the CallingDataMethodsEvent
|
|
CallingDataMethodsEventHandler handler = Events[EventCallingDataMethods] as CallingDataMethodsEventHandler;
|
|
if (handler != null) {
|
|
ModelDataSource.CallingDataMethods += handler;
|
|
}
|
|
|
|
ds = ModelDataSource;
|
|
}
|
|
else {
|
|
string dataSourceID = DataSourceID;
|
|
|
|
if (dataSourceID.Length != 0) {
|
|
// Try to find a DataSource control with the ID specified in DataSourceID
|
|
Control control = DataBoundControlHelper.FindControl(this, dataSourceID);
|
|
if (control == null) {
|
|
throw new HttpException(SR.GetString(SR.DataControl_DataSourceDoesntExist, ID, dataSourceID));
|
|
}
|
|
ds = control as IDataSource;
|
|
if (ds == null) {
|
|
throw new HttpException(SR.GetString(SR.DataControl_DataSourceIDMustBeDataControl, ID, dataSourceID));
|
|
}
|
|
}
|
|
}
|
|
|
|
if (ds == null) {
|
|
// DataSource control was not found, construct a temporary data source to wrap the data
|
|
ds = new ReadOnlyDataSource(DataSource, DataMember);
|
|
}
|
|
else {
|
|
// Ensure that both DataSourceID as well as DataSource are not set at the same time
|
|
if (DataSource != null) {
|
|
throw new InvalidOperationException(SR.GetString(SR.DataControl_MultipleDataSources, ID));
|
|
}
|
|
}
|
|
|
|
// IDataSource was found, extract the appropriate view and return it
|
|
DataSourceView newView = ds.GetView(DataMember);
|
|
if (newView == null) {
|
|
throw new InvalidOperationException(SR.GetString(SR.DataControl_ViewNotFound, ID));
|
|
}
|
|
|
|
_currentViewIsFromDataSourceID = IsDataBindingAutomatic;
|
|
_currentView = newView;
|
|
if ((_currentView != null) && (_currentViewIsFromDataSourceID)) {
|
|
// We only care about this event if we are bound through the DataSourceID property
|
|
_currentView.DataSourceViewChanged += new EventHandler(OnDataSourceViewChanged);
|
|
}
|
|
_currentViewValid = true;
|
|
|
|
return _currentView;
|
|
}
|
|
|
|
|
|
/// <devdoc>
|
|
/// </devdoc>
|
|
protected internal override void CreateChildControls() {
|
|
Controls.Clear();
|
|
|
|
if (ViewState[ItemCountViewStateKey] != null) {
|
|
// create the control hierarchy using the view state (and
|
|
// not the datasource)
|
|
CreateControlHierarchy(false);
|
|
}
|
|
else {
|
|
itemsArray = new ArrayList();
|
|
}
|
|
ClearChildViewState();
|
|
}
|
|
|
|
|
|
|
|
/// <devdoc>
|
|
/// A protected method. Creates a control
|
|
/// hierarchy, with or without the data source as specified.
|
|
/// </devdoc>
|
|
protected virtual void CreateControlHierarchy(bool useDataSource) {
|
|
IEnumerable dataSource = null;
|
|
|
|
if (itemsArray != null) {
|
|
itemsArray.Clear();
|
|
}
|
|
else {
|
|
itemsArray = new ArrayList();
|
|
}
|
|
|
|
if (!useDataSource) {
|
|
// ViewState must have a non-null value for ItemCount because we check for
|
|
// this in CreateChildControls
|
|
int count = (int)ViewState[ItemCountViewStateKey];
|
|
if (count != -1) {
|
|
dataSource = new DummyDataSource(count);
|
|
itemsArray.Capacity = count;
|
|
}
|
|
|
|
AddDataItemsIntoItemsArray(dataSource, useDataSource);
|
|
}
|
|
else {
|
|
dataSource = GetData();
|
|
PostGetDataAction(dataSource);
|
|
}
|
|
}
|
|
|
|
private void OnDataSourceViewSelectCallback(IEnumerable data) {
|
|
_asyncSelectPending = false;
|
|
PostGetDataAction(data);
|
|
}
|
|
|
|
private void PostGetDataAction(IEnumerable dataSource) {
|
|
if (_asyncSelectPending)
|
|
return;
|
|
|
|
ICollection collection = dataSource as ICollection;
|
|
if (collection != null) {
|
|
itemsArray.Capacity = collection.Count;
|
|
}
|
|
|
|
int addedItemCount = AddDataItemsIntoItemsArray(dataSource, true/*useDataSource*/);
|
|
ViewState[ItemCountViewStateKey] = addedItemCount;
|
|
}
|
|
|
|
private int AddDataItemsIntoItemsArray(IEnumerable dataSource, bool useDataSource) {
|
|
int dataItemCount = -1;
|
|
if (dataSource != null) {
|
|
RepeaterItem item;
|
|
ListItemType itemType;
|
|
int index = 0;
|
|
|
|
bool hasSeparators = (separatorTemplate != null);
|
|
dataItemCount = 0;
|
|
|
|
if (headerTemplate != null) {
|
|
CreateItem(-1, ListItemType.Header, useDataSource, null);
|
|
}
|
|
|
|
foreach (object dataItem in dataSource) {
|
|
// rather than creating separators after the item, we create the separator
|
|
// for the previous item in all iterations of this loop.
|
|
// this is so that we don't create a separator for the last item
|
|
if (hasSeparators && (dataItemCount > 0)) {
|
|
CreateItem(index - 1, ListItemType.Separator, useDataSource, null);
|
|
}
|
|
|
|
itemType = (index % 2 == 0) ? ListItemType.Item : ListItemType.AlternatingItem;
|
|
item = CreateItem(index, itemType, useDataSource, dataItem);
|
|
itemsArray.Add(item);
|
|
|
|
dataItemCount++;
|
|
index++;
|
|
}
|
|
|
|
if (footerTemplate != null) {
|
|
CreateItem(-1, ListItemType.Footer, useDataSource, null);
|
|
}
|
|
}
|
|
|
|
return dataItemCount;
|
|
}
|
|
|
|
protected virtual DataSourceSelectArguments CreateDataSourceSelectArguments() {
|
|
return DataSourceSelectArguments.Empty;
|
|
}
|
|
|
|
|
|
/// <devdoc>
|
|
/// </devdoc>
|
|
private RepeaterItem CreateItem(int itemIndex, ListItemType itemType, bool dataBind, object dataItem) {
|
|
RepeaterItem item = CreateItem(itemIndex, itemType);
|
|
RepeaterItemEventArgs e = new RepeaterItemEventArgs(item);
|
|
|
|
InitializeItem(item);
|
|
if (dataBind) {
|
|
item.DataItem = dataItem;
|
|
}
|
|
OnItemCreated(e);
|
|
Controls.Add(item);
|
|
|
|
if (dataBind) {
|
|
item.DataBind();
|
|
OnItemDataBound(e);
|
|
|
|
item.DataItem = null;
|
|
}
|
|
|
|
return item;
|
|
}
|
|
|
|
|
|
/// <devdoc>
|
|
/// <para>A protected method. Creates a <see cref='System.Web.UI.WebControls.RepeaterItem'/> with the specified item type and
|
|
/// location within the <see cref='System.Web.UI.WebControls.Repeater'/>.</para>
|
|
/// </devdoc>
|
|
protected virtual RepeaterItem CreateItem(int itemIndex, ListItemType itemType) {
|
|
return new RepeaterItem(itemIndex, itemType);
|
|
}
|
|
|
|
|
|
/// <internalonly/>
|
|
/// <devdoc>
|
|
/// </devdoc>
|
|
public override void DataBind() {
|
|
// Don't databind to a data source control when the control is in the designer but not top-level
|
|
if (IsDataBindingAutomatic && DesignMode && (Site == null)) {
|
|
return;
|
|
}
|
|
|
|
// do our own databinding
|
|
RequiresDataBinding = false;
|
|
OnDataBinding(EventArgs.Empty);
|
|
|
|
// contained items will be databound after they have been created,
|
|
// so we don't want to walk the hierarchy here.
|
|
}
|
|
|
|
|
|
protected void EnsureDataBound() {
|
|
try {
|
|
_throwOnDataPropertyChange = true;
|
|
if (RequiresDataBinding && IsDataBindingAutomatic) {
|
|
DataBind();
|
|
}
|
|
}
|
|
finally {
|
|
_throwOnDataPropertyChange = false;
|
|
}
|
|
}
|
|
|
|
|
|
/// <devdoc>
|
|
/// Returns an IEnumerable that is the DataSource, which either came
|
|
/// from the DataSource property or from the control bound via the
|
|
/// DataSourceID property.
|
|
/// </devdoc>
|
|
protected virtual IEnumerable GetData() {
|
|
DataSourceView view = ConnectToDataSourceView();
|
|
|
|
Debug.Assert(_currentViewValid);
|
|
|
|
if (view != null) {
|
|
// DevDiv 1070535: enable async model binding for Repeater
|
|
bool useAsyncSelect = false;
|
|
if (AppSettings.EnableAsyncModelBinding) {
|
|
var modelDataView = view as ModelDataSourceView;
|
|
useAsyncSelect = modelDataView != null && modelDataView.IsSelectMethodAsync;
|
|
}
|
|
|
|
if (useAsyncSelect) {
|
|
_asyncSelectPending = true; // disable post data binding action until the callback is invoked
|
|
view.Select(SelectArguments, OnDataSourceViewSelectCallback);
|
|
}
|
|
else {
|
|
return view.ExecuteSelect(SelectArguments);
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
|
|
/// <devdoc>
|
|
/// <para>A protected method. Populates iteratively the specified <see cref='System.Web.UI.WebControls.RepeaterItem'/> with a
|
|
/// sub-hierarchy of child controls.</para>
|
|
/// </devdoc>
|
|
protected virtual void InitializeItem(RepeaterItem item) {
|
|
ITemplate contentTemplate = null;
|
|
|
|
switch (item.ItemType) {
|
|
case ListItemType.Header:
|
|
contentTemplate = headerTemplate;
|
|
break;
|
|
|
|
case ListItemType.Footer:
|
|
contentTemplate = footerTemplate;
|
|
break;
|
|
|
|
case ListItemType.Item:
|
|
contentTemplate = itemTemplate;
|
|
break;
|
|
|
|
case ListItemType.AlternatingItem:
|
|
contentTemplate = alternatingItemTemplate;
|
|
if (contentTemplate == null)
|
|
goto case ListItemType.Item;
|
|
break;
|
|
|
|
case ListItemType.Separator:
|
|
contentTemplate = separatorTemplate;
|
|
break;
|
|
}
|
|
|
|
if (contentTemplate != null) {
|
|
contentTemplate.InstantiateIn(item);
|
|
}
|
|
}
|
|
|
|
|
|
/// <internalonly/>
|
|
/// <devdoc>
|
|
/// </devdoc>
|
|
protected override bool OnBubbleEvent(object sender, EventArgs e) {
|
|
bool handled = false;
|
|
|
|
if (e is RepeaterCommandEventArgs) {
|
|
OnItemCommand((RepeaterCommandEventArgs)e);
|
|
handled = true;
|
|
}
|
|
|
|
return handled;
|
|
}
|
|
|
|
|
|
/// <internalonly/>
|
|
/// <devdoc>
|
|
/// <para>A protected method. Raises the <see langword='DataBinding'/> event.</para>
|
|
/// </devdoc>
|
|
protected override void OnDataBinding(EventArgs e) {
|
|
base.OnDataBinding(e);
|
|
|
|
// reset the control state
|
|
Controls.Clear();
|
|
ClearChildViewState();
|
|
|
|
// and then create the control hierarchy using the datasource
|
|
CreateControlHierarchy(true);
|
|
ChildControlsCreated = true;
|
|
}
|
|
|
|
|
|
/// <devdoc>
|
|
/// This method is called when DataMember, DataSource, or DataSourceID is changed.
|
|
/// </devdoc>
|
|
protected virtual void OnDataPropertyChanged() {
|
|
if (_throwOnDataPropertyChange) {
|
|
throw new HttpException(SR.GetString(SR.DataBoundControl_InvalidDataPropertyChange, ID));
|
|
}
|
|
|
|
if (_inited) {
|
|
RequiresDataBinding = true;
|
|
}
|
|
_currentViewValid = false;
|
|
}
|
|
|
|
|
|
protected virtual void OnDataSourceViewChanged(object sender, EventArgs e) {
|
|
RequiresDataBinding = true;
|
|
}
|
|
|
|
|
|
protected internal override void OnInit(EventArgs e) {
|
|
base.OnInit(e);
|
|
|
|
if (Page != null) {
|
|
Page.PreLoad += new EventHandler(this.OnPagePreLoad);
|
|
if (!IsViewStateEnabled && Page.IsPostBack) {
|
|
RequiresDataBinding = true;
|
|
}
|
|
}
|
|
|
|
if (!DesignMode && !String.IsNullOrEmpty(ItemType)) {
|
|
DataBoundControlHelper.EnableDynamicData(this, ItemType);
|
|
}
|
|
}
|
|
|
|
|
|
/// <devdoc>
|
|
/// <para>A protected method. Raises the <see langword='ItemCommand'/> event.</para>
|
|
/// </devdoc>
|
|
protected virtual void OnItemCommand(RepeaterCommandEventArgs e) {
|
|
RepeaterCommandEventHandler onItemCommandHandler = (RepeaterCommandEventHandler)Events[EventItemCommand];
|
|
if (onItemCommandHandler != null) onItemCommandHandler(this, e);
|
|
}
|
|
|
|
/// <devdoc>
|
|
/// <para>A protected method. Raises the <see langword='ItemCreated'/> event.</para>
|
|
/// </devdoc>
|
|
protected virtual void OnItemCreated(RepeaterItemEventArgs e) {
|
|
RepeaterItemEventHandler onItemCreatedHandler = (RepeaterItemEventHandler)Events[EventItemCreated];
|
|
if (onItemCreatedHandler != null) onItemCreatedHandler(this, e);
|
|
}
|
|
|
|
|
|
/// <devdoc>
|
|
/// <para>A protected method. Raises the <see langword='ItemDataBound'/>
|
|
/// event.</para>
|
|
/// </devdoc>
|
|
protected virtual void OnItemDataBound(RepeaterItemEventArgs e) {
|
|
RepeaterItemEventHandler onItemDataBoundHandler = (RepeaterItemEventHandler)Events[EventItemDataBound];
|
|
if (onItemDataBoundHandler != null) onItemDataBoundHandler(this, e);
|
|
}
|
|
|
|
|
|
protected internal override void OnLoad(EventArgs e) {
|
|
_inited = true; // just in case we were added to the page after PreLoad
|
|
ConnectToDataSourceView();
|
|
if (Page != null && !_pagePreLoadFired && ViewState[ItemCountViewStateKey] == null) {
|
|
// If the control was added after PagePreLoad, we still need to databind it because it missed its
|
|
// first change in PagePreLoad. If this control was created by a call to a parent control's DataBind
|
|
// in Page_Load (with is relatively common), this control will already have been databound even
|
|
// though pagePreLoad never fired and the page isn't a postback.
|
|
if (!Page.IsPostBack) {
|
|
RequiresDataBinding = true;
|
|
}
|
|
|
|
// If the control was added to the page after page.PreLoad, we'll never get the event and we'll
|
|
// never databind the control. So if we're catching up and Load happens but PreLoad never happened,
|
|
// call DataBind. This may make the control get databound twice if the user called DataBind on the control
|
|
// directly in Page.OnLoad, but better to bind twice than never to bind at all.
|
|
else if (IsViewStateEnabled) {
|
|
RequiresDataBinding = true;
|
|
}
|
|
}
|
|
|
|
base.OnLoad(e);
|
|
}
|
|
|
|
private void OnPagePreLoad(object sender, EventArgs e) {
|
|
_inited = true;
|
|
|
|
if (Page != null) {
|
|
Page.PreLoad -= new EventHandler(this.OnPagePreLoad);
|
|
|
|
// Setting RequiresDataBinding to true in OnLoad is too late because the OnLoad page event
|
|
// happens before the control.OnLoad method gets called. So a page_load handler on the page
|
|
// that calls DataBind won't prevent DataBind from getting called again in PreRender.
|
|
if (!Page.IsPostBack) {
|
|
RequiresDataBinding = true;
|
|
}
|
|
|
|
// If this is a postback and viewstate is enabled, but we have never bound the control
|
|
// before, it is probably because its visibility was changed in the postback. In this
|
|
// case, we need to bind the control or it will never appear. This is a common scenario
|
|
// for Wizard and MultiView.
|
|
if (Page.IsPostBack && IsViewStateEnabled && ViewState[ItemCountViewStateKey] == null) {
|
|
RequiresDataBinding = true;
|
|
}
|
|
_pagePreLoadFired = true;
|
|
|
|
}
|
|
}
|
|
|
|
|
|
protected internal override void OnPreRender(EventArgs e) {
|
|
EnsureDataBound();
|
|
base.OnPreRender(e);
|
|
}
|
|
|
|
/// <devdoc>
|
|
/// Loads view state.
|
|
/// </devdoc>
|
|
protected override void LoadViewState(object savedState) {
|
|
if (IsUsingModelBinders) {
|
|
Pair myState = (Pair)savedState;
|
|
|
|
if (savedState == null) {
|
|
base.LoadViewState(null);
|
|
}
|
|
else {
|
|
base.LoadViewState(myState.First);
|
|
|
|
if (myState.Second != null) {
|
|
((IStateManager)ModelDataSource).LoadViewState(myState.Second);
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
base.LoadViewState(savedState);
|
|
}
|
|
}
|
|
|
|
/// <devdoc>
|
|
/// Saves view state.
|
|
/// </devdoc>
|
|
protected override object SaveViewState() {
|
|
// Bug 322689: In the web farms scenario, if a web site is hosted in 4.0 and 4.5 servers
|
|
// (though this is not a really supported scenario, we are fixing this instance),
|
|
// the View state created by 4.0 should be able to be understood by 4.5 controls.
|
|
// So, we create a Pair only if we are using model binding and otherwise fallback to 4.0 behavior.
|
|
|
|
object baseViewState = base.SaveViewState();
|
|
|
|
if (IsUsingModelBinders) {
|
|
Pair myState = new Pair();
|
|
|
|
myState.First = baseViewState;
|
|
myState.Second = ((IStateManager)ModelDataSource).SaveViewState();
|
|
|
|
if ((myState.First == null) &&
|
|
(myState.Second == null)) {
|
|
return null;
|
|
}
|
|
|
|
return myState;
|
|
}
|
|
else {
|
|
return baseViewState;
|
|
}
|
|
}
|
|
|
|
/// <devdoc>
|
|
/// Starts tracking view state.
|
|
/// </devdoc>
|
|
protected override void TrackViewState() {
|
|
base.TrackViewState();
|
|
|
|
if (IsUsingModelBinders) {
|
|
((IStateManager)ModelDataSource).TrackViewState();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|