2010-05-18 13 views
6

Questa domanda originariamente riguardava il funzionamento del collegamento bidirezionale, ma a causa della mancanza di risposte specifiche e di altri progressi lungo il percorso, l'ho aggiornato - Puoi controllare la cronologia delle modifiche, ma ho pensato che fosse meglio per chiarezza.Associazione dati bidirezionale di un controllo personalizzato per asp.net

Il seguente elenco di codici consente a un singolo oggetto di essere un database bidirezionale a un controllo basato su modelli. Mi piacerebbe estendere questo esempio nel modo più semplice possibile per consentire l'annidamento di controlli basati su modelli di controllo bidirezionale abilitati per i collegamenti bidirezionali per le proprietà tipizzate complesse dell'oggetto root-most. Ad esempio, SampleFormData ha una proprietà List<string> Items. Mi piacerebbe essere in grado di associare a questo elenco all'interno del modello di root-most (da questo elenco di codici), e visualizzare i dati di stringa in un elenco modificabile di caselle di testo, forse, con i comandi per inserire, eliminare, rebind-inserito -cambiamenti (torna alla proprietà Elenco dell'oggetto associato). Inoltre, se si trattasse di un elenco di un tipo complesso (SampleFormChildData, anziché stringa), un nuovo SampleSpecificEntryForm incorporato potrebbe essere utilizzato all'interno dell'elenco, associato a ciascuno degli elementi dell'elenco, come un ripetitore. E così via fino alle proprietà semplici della foglia, se l'autore lo sceglie. I campi ui non devono necessariamente essere generati automaticamente, sono disponibili solo per il binding.

Nota: il caso dello List<string> è speciale perché anche i binding predefiniti non possono gestire stringhe come DataItem direttamente: il binding alle stringhe direttamente come elementi nel nostro elenco non è un requisito, ma sicuramente un valore.

Questo è diverso da un FormView perché non è costruito per prevedere il binding a uno di un elenco di elementi, solo a un singolo elemento come conservato in viewstate o dove mai. A differenza di FormView, questo ha un solo modello predefinito simile a EditView di FormView. Allo stesso modo, l'associazione a una proprietà simile a una raccolta avrebbe anche solo una vista - modifica. Non c'è alcuna selezione della riga e quindi modifica. Tutto è modificabile tutto il tempo. Lo scopo è quello di rendere più facili da costruire le forme a doppio senso.

Mi sembra che ci debbano essere due tipi di legame. SingleEntityBinding e CollectionBinding. SingleEntityBinding accetta una singola istanza di oggetto come origine dati (come prototipo di SampleSpecificEntryForm) mentre CollectionBinding potrebbe essere associato al padre SingleEntityBinding con gli attributi di DataSourceID="EntryForm1" DataMember="Items" come nell'esempio di codice per DataList1 di seguito. L'annidamento di entrambi i tipi dovrebbe essere supportato in entrambi i tipi. La manipolazione delle liste come le operazioni di inserimento/modifica/cancellazione dei caratteri rispetto ai dati dell'oggetto di backup sono responsabilità dell'autore del modulo; tuttavia, tali meccanismi sarebbero relativamente semplici da implementare.

Ecco un po 'di codice, spero che aiuti qualcuno. 200 punti sono là fuori per i migliori suggerimenti verso questo obiettivo laid-out ...

using System.ComponentModel; 
using System.Collections.Specialized; 
using System.Collections.Generic; 

namespace System.Web.UI.WebControls.Special 
{ 
    [Serializable] 
    public class SampleFormData 
    { 
     public string SampleString { get; set; } 
     public int SampleInt { get; set; } 
     public List<string> Items { get; set; } 

     public SampleFormData() 
     { 
      SampleString = "Sample String Data"; 
      SampleInt = 5; 
      Items = new List<string>(); 
     } 
    } 

    [ToolboxItem(false)] 
    public class SampleSpecificFormDataContainer : WebControl, INamingContainer, IDataItemContainer 
    { 
     SampleSpecificEntryForm entryForm; 

     internal SampleSpecificEntryForm EntryForm 
     { 
      get { return entryForm; } 
     } 

     [Bindable(true), Category("Data")] 
     public string SampleString 
     { 
      get { return entryForm.FormData.SampleString; } 
      set { entryForm.FormData.SampleString = value; } 
     } 

     [Bindable(true), Category("Data")] 
     public int SampleInt 
     { 
      get { return entryForm.FormData.SampleInt; } 
      set { entryForm.FormData.SampleInt = value; } 
     } 

     [Bindable(true), Category("Data")] 
     public List<string> Items 
     { 
      get { return entryForm.FormData.Items; } 
      set { entryForm.FormData.Items = value; } 
     } 

     internal SampleSpecificFormDataContainer(SampleSpecificEntryForm entryForm) 
     { 
      this.entryForm = entryForm; 
     } 

     #region IDataItemContainer Members 
     public object DataItem { get { return entryForm.FormData; } } 

     public int DataItemIndex { get { return 0; } } 

     public int DisplayIndex { get { return 0; } } 
     #endregion 
    } 

    public class SampleSpecificEntryForm : DataBoundControl, INamingContainer, IDataSource 
    { 
     #region Template 
     private IBindableTemplate formTemplate = null; 

     [Browsable(false), DefaultValue(null), 
     TemplateContainer(typeof(SampleSpecificFormDataContainer), ComponentModel.BindingDirection.TwoWay), 
     PersistenceMode(PersistenceMode.InnerProperty)] 
     public virtual IBindableTemplate FormTemplate 
     { 
      get { return formTemplate; } 
      set { formTemplate = value; } 
     } 
     #endregion 

     public override ControlCollection Controls 
     { 
      get 
      { 
       EnsureChildControls(); 
       return base.Controls; 
      } 
     } 

     private SampleSpecificFormDataContainer formDataContainer = null; 

     [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] 
     public SampleSpecificFormDataContainer FormDataContainer 
     { 
      get 
      { 
       EnsureChildControls(); 
       return formDataContainer; 
      } 
     } 

     [Bindable(true), Browsable(false)] 
     public SampleFormData FormData 
     { 
      get 
      { 
       SampleFormData data = ViewState["FormData"] as SampleFormData; 

       if (data == null) 
       { 
        data = new SampleFormData(); 
        ViewState["FormData"] = data; 
       } 

       return data; 
      } 
     } 

     protected override void CreateChildControls() 
     { 
      if (!this.ChildControlsCreated) 
      { 
       this.ChildControlsCreated = true; 
       Controls.Clear(); 
       formDataContainer = new SampleSpecificFormDataContainer(this); 

       Controls.Add(formDataContainer); 
       FormTemplate.InstantiateIn(formDataContainer); 
      } 
     } 

     protected override void PerformDataBinding(Collections.IEnumerable ignore) 
     { 
      CreateChildControls(); 

      if (Page.IsPostBack) 
      { 
       //OrderedDictionary fields = new OrderedDictionary(); 

       //ExtractValuesFromBindableControls(fields, formDataContainer); // Don't know what this would be for 

       foreach (System.Collections.DictionaryEntry entry in formTemplate.ExtractValues(formDataContainer)) 
       { 
        if (((string)entry.Key).Equals("SampleString", StringComparison.Ordinal)) 
        { 
         FormData.SampleString = (string)entry.Value; 
        } 

        if (((string)entry.Key).Equals("SampleInt", StringComparison.Ordinal)) 
        { 
         int i; 
         if (int.TryParse((string)entry.Value, out i)) 
         { 
          FormData.SampleInt = i; 
         } 
        } 
       } 
      } 

      formDataContainer.DataBind(); 
     } 

     public SampleSpecificEntryForm() 
     { 
      this.PreRender += new EventHandler(SampleSpecificEntryForm_PreRender); 
     } 

     void SampleSpecificEntryForm_PreRender(object sender, EventArgs e) 
     { 
      SaveViewState(); 
     } 

     #region IDataSource Members 

     public event EventHandler DataSourceChanged; 

     public DataSourceView GetView(string viewName) 
     { 
      return new PropertyView(this, viewName); 
     } 

     public Collections.ICollection GetViewNames() 
     { 
      return new List<string>() { "SampleString", "SampleInt", "Items" }; 
     } 

     #endregion 
    } 

    // Not yet used ... 
    public class PropertyView : DataSourceView 
    { 
     SampleSpecificEntryForm owner; 
     string viewName; 

     protected override Collections.IEnumerable ExecuteSelect(DataSourceSelectArguments arguments) 
     { 
      if (viewName.Equals("SampleString", StringComparison.Ordinal)) 
      { 
       return new object[] { owner.FormData.SampleString }; 
      } 

      if (viewName.Equals("SampleInt", StringComparison.Ordinal)) 
      { 
       return new object[] { owner.FormData.SampleInt }; 
      } 

      if (viewName.Equals("Items", StringComparison.Ordinal)) 
      { 
       return new object[] { owner.FormData.Items }; 
      } 

      throw new InvalidOperationException(); 
     } 

     public PropertyView(SampleSpecificEntryForm owner, string viewName) 
      : base(owner, viewName) 
     { 
      this.owner = owner; 
      this.viewName = viewName; 
     } 
    } 
} 

Con una pagina ASP.NET il seguente:

<%@ Page Title="Home Page" Language="C#" MasterPageFile="~/Site.master" AutoEventWireup="true" 
    CodeBehind="Default2.aspx.cs" Inherits="EntryFormTest._Default2" EnableEventValidation="false" %> 

<%@ Register Assembly="EntryForm" Namespace="System.Web.UI.WebControls.Special" TagPrefix="cc1" %> 

<asp:Content ID="HeaderContent" runat="server" ContentPlaceHolderID="HeadContent"> 
</asp:Content> 
<asp:Content ID="BodyContent" runat="server" ContentPlaceHolderID="MainContent"> 
    <h2> 
     Welcome to ASP.NET! 
    </h2> 
     <cc1:SampleSpecificEntryForm ID="EntryForm1" runat="server"> 
    <FormTemplate> 
     <asp:TextBox ID="txtSampleString" runat="server" Text='<%# Bind("SampleString") %>'></asp:TextBox><br /> 
     <asp:TextBox ID="txtSampleInt" runat="server" Text='<%# Bind("SampleInt") %>'></asp:TextBox><br /> 
     <h3> 
      (<%# Container.SampleString %>, <%# Container.SampleInt %>) - aka - 
      (<%# DataBinder.Eval(Container, "SampleString")%>, <%# DataBinder.Eval(Container, "SampleInt")%>)</h3> 
     <br /> 
     <asp:Button ID="btnUpdate" runat="server" Text="Update" /><br /> 
     <br /> 
    </FormTemplate> 
</cc1:SampleSpecificEntryForm> 
</asp:Content> 

Default2.aspx.cs:

using System; 

namespace EntryFormTest 
{ 
    public partial class _Default2 : System.Web.UI.Page 
    { 
     protected void Page_Load(object sender, EventArgs e) 
     { 
      EntryForm1.DataBind(); 
     } 
    } 
} 

ho implementato IDataSource così, nel tentativo di essere in grado di nido di un componente lista in questo modo (all'interno):

<asp:DataList ID="DataList1" runat="server" DataSourceID="EntryForm1" DataMember="Items"> 
    <EditItemTemplate> 
     <asp:TextBox ID="TextBox3" runat="server" Text="<%# Bind(".") %>"></asp:TextBox> 
    </EditItemTemplate> 
    <FooterTemplate> 
     <asp:Button ID="Button2" runat="server" Text="Add" CommandName="Add" /> 
    </FooterTemplate> 
</asp:DataList> 

Qualsiasi idea su come eseguire questa operazione in modo a cascata sarebbe eccezionale (ad esempio, nella proprietà Elenco articoli). Una delle sfide qui è che Bind() non può riferirsi all'oggetto del database stesso (una stringa in questo caso) ma a una proprietà di quell'elemento - rendendo difficile il binding a una lista.

Grazie per qualsiasi aiuto!


scoperte lungo la strada

Implementato IDataItemContainer. Ero molto fiducioso che questo avrebbe risolto il problema, ma no. Nessun cambiamento apprezzabile. Oops, l'ho implementato nella classe sbagliata. Ora è Binding, ma i valori non vengono rimbalzati sull'oggetto associato sul postback. Hmmm ...

Come this article suggerisce, Page.GetDataItem() è la fonte di eccezione. Questa eccezione viene generata se _dataBindingContext della pagina è nullo o vuoto. L'articolo spiega questo, ma non dice come assicurarsi che il _dataBindingContext della pagina sia popolato. Continuerò a cercare.

Come afferma la documentazione MSDN, DataBoundControl deve implementare PerformDataBinding anziché eseguire l'override di DataBind(). L'ho fatto e ho realizzato un lavoro vincolante in entrambe le direzioni. È necessario questo codice o dovrei usare qualcosa di built-in?

risposta

0

Hai provato la sintassi Databinder.Eval(Container.DataItem,...)?

Vedere anche questo articolo su Bind().

PS. È necessario creare un collegamento dati su ogni postback a meno che non si stia utilizzando Viewstate per conservare i valori.

+0

Sì, grazie per la risposta. Ho provato tutte le varie opzioni. Ho aggiornato il post con "Scoperte lungo la strada" con le ultime informazioni. Hai già lavorato a questo problema generale? –

+1

Sì, ho scritto i controlli personalizzati di databound, anche se ho usato severamente MVC per un po 'di tempo. PS. Devi eseguire il Databind su Postback a meno che non utilizzi Viewstate per conservare i valori. – kervin

+0

Bene, Kervin. Hai vinto! –

Problemi correlati