Sviluppo di un controllo basato su template con associazione a dati

Associare la proprietà di un controllo a un elemento o un'espressione di dati con la sintassi di associazione dati di ASP.NET è molto semplice. In questa sezione viene descritto lo scenario più complesso di sviluppo di un controllo con proprietà basate su template e associate a un'origine dati che è un tipo di insieme (System.Collections.ICollection o System.Collections.IEnumerable). I modelli consentono allo sviluppatore della pagina di personalizzare la presentazione dei dati associati al controllo. I controlli Repeater e DataList sono esempi di controlli con associazione a dati e basati su template.

Per una descrizione generale dell'associazione dati in pagine di ASP.NET, vedere Guida rapida di ASP.NET —> Web Form ASP.NET —> Associazione dati di controlli server. Per informazioni di base sulla creazione di un controllo basato su template, vedere Sviluppo di un controllo basato su template.

Un controllo con associazione a dati e basato su template dispone di una proprietà di origine dati di tipo ICollection o IEnumerable e di una o più proprietà di tipo ITemplate. Il contenitore di una delle proprietà di modello consente di definire una proprietà (generalmente denominata DataItem) a cui associare i dati. Il controllo implementa la logica di associazione a dati nel metodo Databind che eredita da Control. Il controllo esegue l'override del metodo CreateChildControls per ricreare la gerarchia dei controlli figlio in seguito al postback. I passaggi vengono illustrati più dettagliatamente nella descrizione che segue.

Per sviluppare un controllo con associazione a dati basato su template

  1. Definire un controllo che implementi l'interfaccia di System.Web.UI.INamingContainer.

    public class TemplatedList : WebControl, INamingContainer {...}
    [Visual Basic]
    Public Class TemplatedList
       Inherits WebControl
       Implements INamingContainer
       ...
    End Class
    
  2. Definire una proprietà di tipo System.Web.UI.ITemplate.

    [TemplateContainer(typeof(TemplatedListItem))]
            public virtual ITemplate ItemTemplate {
                get {
                    return itemTemplate;
                }
                set {
                    itemTemplate = value;
                }
            }
    [Visual Basic]
    <TemplateContainer(GetType(TemplatedListItem))> _
    Public Overridable Property ItemTemplate() As ITemplate
       Get
          Return _itemTemplate
       End Get
       Set
          _itemTemplate = value
       End Set
    End Property
    

    Il contenitore logico del modello, specificato nell'attributo TemplateContainerAttribute, deve disporre di una proprietà a cui associare i dati. Per convenzione, questa proprietà è denominata DataItem. Per maggiori dettagli sui contenitori logici per le proprietà di modello, vedere Sviluppo di un controllo basato su template. Nell'esempio seguente viene definito un contenitore per la proprietà di modello.

    public class TemplatedListItem : TableRow, INamingContainer {
            private object dataItem;
            public virtual object DataItem {
                get {
                    return dataItem;
                }
                set {
                    dataItem = value;
                }
    }
    [Visual Basic]
    Public Class TemplatedListItem
       Inherits TableRow
       Implements INamingContainer
       Private _dataItem As Object
       Public Overridable Property DataItem() As Object
          Get
             Return _dataItem
          End Get
          Set
             _dataItem = value
          End Set
       End Property
    End Class
    
  3. Eseguire l'override del metodo DataBind, ereditato da Control, per mettere a disposizione una logica di associazione a dati. I passaggi necessari sono i seguenti:

    1. Chiamare il metodo OnDataBinding della classe base per richiamare i gestori allegati alla pagina che consentono di valutare le espressioni di associazione dati nel controllo.
    2. Cancellare l'insieme Controls.
    3. Cancellare la proprietà ViewState dei controlli figlio.
    4. Creare i controlli figlio tramite l'origine dati.
    5. Segnalare al framework della pagina ASP.NET di tenere traccia della proprietà ViewState per il controllo.

    Il codice che segue consente di eseguire questi passaggi. CreateChildControlsHierarchy è un metodo di supporto per eseguire l'effettiva operazione di creazione dei controlli figlio. Per ulteriori dettagli, vedere il passaggio 5.

    public override void DataBind() {
        // Controls with a data-source property perform their 
        // custom data binding by overriding DataBind to
        // evaluate any data-binding expressions on the control    
        // itself.
        base.OnDataBinding(EventArgs.Empty);
    
        // Reset the control's state.
        Controls.Clear();
        ClearChildViewState();
    
        // Create the control hierarchy using the data source.
        CreateControlHierarchy(true);
        ChildControlsCreated = true;
    
        TrackViewState();
    }
    [Visual Basic]
    Public Overrides Sub DataBind()
       ' Controls with a data-source property perform their custom data 
       ' binding by overriding DataBind.
       ' Evaluate any data-binding expressions on the control itself.
       MyBase.OnDataBinding(EventArgs.Empty)
    
       ' Reset the control state.
       Controls.Clear()
       ClearChildViewState()
    
       '  Create the control hierarchy using the data source.
       CreateControlHierarchy(True)
       ChildControlsCreated = True
    
       TrackViewState()
    End Sub
    
  4. Eseguire l'override di CreateChildControls per ricreare i controlli figlio in uno scenario di postback. Questa operazione implica la cancellazione dell'insieme Controls e la creazione della gerarchia di controllo tramite lo stato di visualizzazione invece che tramite l'origine dati. L'effettiva attività di creazione dei controlli figlio è nascosta nel metodo CreateControlHierarchy descritto al passaggio 5.

    protected override void CreateChildControls() {
        Controls.Clear();
    
        if (ViewState["ItemCount"] != null) {
        // Create the control hierarchy using the view state, 
        // not the data source.
        CreateControlHierarchy(false);
        }
    }
    [Visual Basic]
    Protected Overrides Sub CreateChildControls()
       Controls.Clear() 
       If Not (ViewState("ItemCount") Is Nothing) Then
          ' Create the control hierarchy using the view state, 
          ' not the data source.
          CreateControlHierarchy(False)
       End If
    End Sub
    
  5. Definire un'origine dati con elementi null e utilizzarla al posto della vera origine dati per la creazione della gerarchia di controlli durante il postback. I passaggi 3 e 4 consentono di creare la gerarchia di controlli tramite l'origine dati e lo stato di visualizzazione salvato, rispettivamente. Un'origine dati fittizia consente a un controllo di implementare un singolo percorso di codice per gli elementi comuni di questi due passaggi.

    Nota   Nel passaggio 5 vengono descritti i dettagli di implementazione utilizzati dai controlli ASP.NET con associazione a dati in .NET Framework. La classe DummyDataSource e il metodo CreateControlHierarchy illustrati nel frammento seguente non si trovano in .NET Framework ma è necessario che siano definiti da uno sviluppatore di controlli. Non è necessario implementare questi elementi, tuttavia è consigliabile utilizzare questa tecnica o una simile, per indicare un percorso di codice comune per la creazione della gerarchia di controllo.

    Nel frammento di codice seguente viene definita un'origine dati fittizia.

    internal sealed class DummyDataSource : ICollection {
    
            private int dataItemCount;
    
            public DummyDataSource(int dataItemCount) {
                this.dataItemCount = dataItemCount;
            }
    // Implement other methods of the ICollection interface.
    ...
            public IEnumerator GetEnumerator() {
                return new DummyDataSourceEnumerator(dataItemCount);
            }
    
    
            private class DummyDataSourceEnumerator : IEnumerator {
    
                private int count;
                private int index;
    
                public DummyDataSourceEnumerator(int count) {
                    this.count = count;
                    this.index = -1;
                }
    
    
                public object Current {
                    get {
                        return null;
                    }
                }
    // Define other methods of the IEnumerator interface.
            }
        }
    [Visual Basic]
    NotInheritable Friend Class DummyDataSource
       Implements ICollection
    
       Private dataItemCount As Integer
    
       Public Sub New(dataItemCount As Integer)
          Me.dataItemCount = dataItemCount
       End Sub
    
       ' Implement other methods of the ICollection interface.
       ...
    
       Public Function GetEnumerator() As IEnumerator Implements ICollection.GetEnumerator
          Return New DummyDataSourceEnumerator(dataItemCount)
       End Function
    
       Private Class DummyDataSourceEnumerator
          Implements IEnumerator
    
          Private count As Integer
          Private index As Integer
    
          Public Sub New(count As Integer)
             Me.count = count
             Me.index = - 1
          End Sub
    
          Public ReadOnly Property Current() As Object Implements IEnumerator.Current
             Get
                Return Nothing
             End Get
          End Property
          ' Define other methods of the IEnumerator interface.
          ...
       End Class
    End Class
    

    È possibile utilizzare DummyDataSource per definire il metodo CreateControlHierarchy, come illustrato di seguito.

    private void CreateControlHierarchy(bool useDataSource) {
                IEnumerable dataSource = null;
                int count = -1;
    
                if (useDataSource == false) {
                    // ViewState must have a non-null value for ItemCount because this is checked 
                    //  by CreateChildControls.
                    count = (int)ViewState["ItemCount"];
                    if (count != -1) {
                        dataSource = new DummyDataSource(count);
                    }
                }
                else {
                    dataSource = this.dataSource;
                }
    
                if (dataSource != null) {
                    int index = 0;
                    count = 0;
                    foreach (object dataItem in dataSource) {
    ...
    // Invoke a private helper method to create each item. 
                        CreateItem(...);
                        count++;
                        index++;
                    }
                }
    
                if (useDataSource) {
                    // Save the number of items contained for use in round trips.
                    ViewState["ItemCount"] = ((dataSource != null) ? count : -1);
                }
            }
    
    [Visual Basic]
    Private Sub CreateControlHierarchy(useDataSource As Boolean)
       Dim dataSource As IEnumerable = Nothing
       Dim count As Integer = - 1
    
       If useDataSource = False Then
          ' ViewState must have a non-null value for ItemCount because this is checked 
          '  by CreateChildControls.
          count = CInt(ViewState("ItemCount"))
          If count <> - 1 Then
             dataSource = New DummyDataSource(count)
          End If
       Else
          dataSource = Me._dataSource
       End If
    
       If Not (dataSource Is Nothing) Then
          Dim table As New Table()
          Controls.Add(table)
    
          Dim selectedItemIndex As Integer = SelectedIndex
          Dim index As Integer = 0
    
          count = 0
          Dim dataItem As Object
          For Each dataItem In  dataSource
             Dim itemType As ListItemType = ListItemType.Item
             If index = selectedItemIndex Then
                itemType = ListItemType.SelectedItem
             Else
                If index Mod 2 <> 0 Then
                   itemType = ListItemType.AlternatingItem
                End If
             End If 
             CreateItem(table, index, itemType, useDataSource, dataItem)
             count += 1
             index += 1
          Next dataItem
       End If
    
       If useDataSource Then
          ' Save the number of items contained for use in round trips.
          If Not (dataSource Is Nothing) Then
             ViewState("ItemCount") = count
          Else
             ViewState("ItemCount") = -1
          End If
       End If
    End Sub
    

    Il metodo CreateItem esegue l'effettiva attività di creazione del modello e di associazione della proprietà DataItem all'origine dati. Nel frammento di codice seguente viene illustrato come viene implementato il metodo CreateItem in Esempio di controllo basato su template con associazione a dati. Si noti che il metodo CreateItem è un dettaglio di implementazione e non viene definito in .NET Framework.

    private TemplatedListItem CreateItem(Table table, int itemIndex, ListItemType itemType, bool dataBind, object dataItem) {
                TemplatedListItem item = new TemplatedListItem(itemIndex, itemType);
                TemplatedListItemEventArgs e = new TemplatedListItemEventArgs(item);
    
                if (itemTemplate != null) {
                    itemTemplate.InstantiateIn(item.Cells[0]);
                }
                if (dataBind) {
                    item.DataItem = dataItem;
                }
                OnItemCreated(e);
                table.Rows.Add(item);
    
                if (dataBind) {
                    item.DataBind();
                    OnItemDataBound(e);
    
                    item.DataItem = null;
                }
    
                return item;
            }
    [Visual Basic]
    Private Function CreateItem(table As Table, itemIndex As Integer, itemType As ListItemType, dataBind As Boolean, dataItem As Object) As TemplatedListItem
       Dim item As New TemplatedListItem(itemIndex, itemType)
       Dim e As New TemplatedListItemEventArgs(item)
    
       If Not (_itemTemplate Is Nothing) Then
          _itemTemplate.InstantiateIn(item.Cells(0))
       End If
       If dataBind Then
          item.DataItem = dataItem
       End If
       OnItemCreated(e)
       table.Rows.Add(item)
    
       If dataBind Then
          item.DataBind()
          OnItemDataBound(e)
    
          item.DataItem = Nothing
       End If
    
       Return item
    End Function
    

Per un esempio di un controllo con associazione a dati che consente di implementare i passaggi descritti in questo argomento, vedere Esempio di controllo basato su template con associazione a dati.

Vedere anche

Esempio di controllo basato su template con associazione a dati