Domanda

Questa domanda è stata originariamente su come ottenere il legame con lavoro a tutti a due vie, ma a causa della mancanza di specifiche risposte e comunque progredire lungo la strada, sono stato suo aggiornamento - È possibile controllare la cronologia di modifica, ma ho pensato che questo è meglio per chiarezza.

Il codice messa sotto permette un singolo oggetto sia bidirezionale databound a un controllo su modelli. Vorrei estendere questo esempio nel modo più semplice possibile per consentire di annidamento di controllo simile a due vie databinding abilitato templato oggetti complessi tipizzato della radice oggetto più. Ad esempio, ha un SampleFormData List<string> Items proprietà. Mi piacerebbe essere in grado di legarsi a questa lista all'interno del Modello di radice-più (da questo annuncio in codice), e sia visualizzare i dati di stringa in un elenco modificabile di caselle di testo, forse, con comandi per inserire, eliminare Rebind-entrato -Cambiamenti (torna alla proprietà Elenco dell'oggetto legato). Inoltre, se questo fosse un elenco di un tipo complesso (SampleFormChildData, invece di stringa), una nuova SampleSpecificEntryForm incorporato potrebbe essere utilizzato all'interno dell'elenco, legato a ciascuna delle voci della lista, come un ripetitore. E così via fino alle proprietà foglia-semplice, se l'autore lo desidera. L'interfaccia utente-campi non devono essere generati automaticamente, appena disponibile per il legame.

Nota: Il caso del List<string> è speciale perché anche il built-in attacchi non possono gestire stringa come DataItem direttamente - il legame con le stringhe direttamente come elementi nella nostra lista non è un requisito, ma di indubbia utilità

Questo è diverso da un FormView perché non è costruita per aspettare di legarsi a uno di un elenco di elementi, solo per un singolo elemento come persistito nel ViewState o dove mai. A differenza del FormView, questo ha solo un unico modello predefinito simile a EditTemplate di FormView. Allo stesso modo, il legame con una proprietà della raccolta simile avrebbe anche una sola vista - modifica. Non v'è alcuna selezione della riga e quindi modifica. Tutto è modificabile in ogni momento. Lo scopo è quello di rendere bidirezionali forme legate più facile da costruire.

Mi sembra che non ci dovrebbero essere due tipi di rilegatura. SingleEntityBinding e CollectionBinding. SingleEntityBinding prende una singola istanza di oggetto come origine dati (come prototipo da SampleSpecificEntryForm) mentre CollectionBinding potrebbe essere vincolata ad esso genitore SingleEntityBinding con attributi di DataSourceID="EntryForm1" DataMember="Items" come nell'esempio di codice per DataList1 sotto. Nidificazione di entrambi i tipi dovrebbe essere sostenuta in entrambi i tipi. manipolazione lista come le operazioni di inserimento / cambio / tipo di eliminazione con i dati del supporto-oggetto sono di responsabilità della forma dell'autore; Tuttavia, tali meccanismi sarebbero relativamente semplice da implementare.

Ecco un po 'di codice, spero che aiuta 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 quanto segue:

<%@ 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>

Qualche idea su come fare questo lavoro in modo cascata sarebbe fantastico (sulla proprietà nell'elenco Elementi, per esempio). Una delle sfide qui è che Bind () non può fare riferimento all'oggetto databound stesso (una stringa in questo caso), ma su una proprietà di tale elemento -. Rendendo vincolante a un elenco imbarazzante

Grazie per qualsiasi aiuto!


Scoperte lungo la strada

Implementato IDataItemContainer. Ero molto fiducioso che questo sarebbe risolvere il problema, ma no. Nessun cambiamento notevole. Ops, implementato sulla classe sbagliata. Ora è vincolante, ma i valori non vengono rimbalzo per l'oggetto associato il postback. Hmmm ...

questo articolo suggerisce, Page.GetDataItem () è la fonte della deroga. questa eccezione viene generata se della pagina _dataBindingContext è nullo o vuoto. l'articolo non spiegare questo, ma non dice come assicurare che _dataBindingContext della pagina è popolato. io continuerò a cercare.

Come la documentazione MSDN dice, DataBoundControl dovrebbe attuare PerformDataBinding invece di rilevante DataBind (). Ho fatto così e ha fatto il lavoro sia a senso unico vincolante. È questo codice necessario o dovrei usare qualcosa di built-in?

È stato utile?

Soluzione

Hai provato DataBinder.Eval (Container.DataItem, ...) sintassi?

vedi anche questo articolo su Bind () .

PS. È necessario DataBind su ogni postback a meno che non si utilizza ViewState di preservare i valori.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top