Question

Cette question a été à l'origine à obtenir deux voies de liaison au travail du tout, mais en raison du manque de réponses spécifiques et par ailleurs progresser sur le chemin, je l'ai été mise à jour - vous pouvez vérifier l'historique des modifications, mais je me suis dit ce est mieux pour plus de clarté.

Le code ci-dessous la liste permet à un seul objet à databound dans les deux sens à un contrôle basé sur un modèle. Je voudrais étendre cet exemple dans la manière la plus simple possible pour permettre l'imbrication des contrôles à deux voies de liaison de données compatibles de la même basé sur un modèle pour les propriétés complexes typé de la racine objet le plus. Par exemple, SampleFormData a une List<string> Items de propriété. Je voudrais être capable de se lier à cette liste dans le modèle racine le plus (de ce code liste), et soit afficher les données de chaîne dans une liste modifiable de zones de texte, peut-être, avec des commandes pour insérer, supprimer, entré retirage- -Changements (retour à la propriété Liste de l'objet lié). De plus, si cela était une liste d'un type complexe (SampleFormChildData, plutôt que de chaîne), un nouveau SampleSpecificEntryForm embarqué pourrait être utilisé dans la liste, liée à chacun des éléments de la liste, comme un répéteur. Et ainsi de suite aux propriétés de feuille simple, si l'auteur choisit donc. Les champs ui ne doivent pas être généré automatiquement, juste disponible pour la liaison.

Note: Le cas du List<string> est spéciale parce que même les liaisons intégrées ne peuvent pas gérer la chaîne comme DataItem directement - la liaison à cordes directement les articles dans notre liste n'est pas une exigence, mais certainement de valeur

Ceci est différent d'un FormView car il ne se construit pas à attendre de se lier à une d'une liste d'articles, pour un seul élément comme persisté dans viewstate ou si jamais. Contrairement à la FormView, ce qui a un seul modèle par défaut semblable à EditTemplate de FormView. De même, la liaison à une propriété de collection comme ne pourra lui aussi avoir un point de vue - modifier. Il n'y a pas de sélection de la ligne, puis l'édition. Tout est modifiable tout le temps. Le but est de faire des formes liées à deux voies plus faciles à construire.

me semble qu'il devrait y avoir deux types de reliure. SingleEntityBinding et CollectionBinding. SingleEntityBinding prend une seule instance d'objet en tant que source de données (comme prototype par SampleSpecificEntryForm), tandis que CollectionBinding pourrait être lié à elle SingleEntityBinding de parent avec des attributs de DataSourceID="EntryForm1" DataMember="Items" comme dans l'exemple de code pour DataList1 ci-dessous. Imbrication de chaque type doit être pris en charge dans les deux types. Liste manipulation telles que insertion / modification / suppression des opérations de type contre les données de l'appui-objet sont la responsabilité de l'auteur de la forme; Toutefois, ces mécanismes seraient relativement simples à mettre en œuvre.

Voici un code, il aide quelqu'un l'espoir. 200 points sont là pour les meilleures suggestions pour atteindre cet objectif décontracté à ...

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;
        }
    }
}

Avec une page ASP.NET la suivante:

<%@ 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();
        }
    }
}

Je l'ai mis en œuvre IDataSource ainsi, dans une tentative de pouvoir imbriquer un composant de liste comme si (au):

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

Toute réflexion sur la façon de faire ce travail d'une manière en cascade serait génial (sur la propriété de la liste des articles, par exemple). L'un des défis est que bind () ne peut pas se référer à l'objet lui-même databound (une chaîne dans ce cas), mais sur une propriété de cet élément -. Rendant obligatoires à une liste maladroite

Merci pour toute aide!


Découvertes le long du chemin

Mis en œuvre IDataItemContainer. J'ai été très bon espoir que cela réglerait le problème, mais non. Aucun changement notable. Oops, mis en œuvre sur la mauvaise classe. Maintenant, il est obligatoire, mais les valeurs ne sont pas de rebond à l'objet lié à postback. Hmmm ...

cet article suggère, Page.GetDataItem () est le source de l'exception. cette exception est levée si est nul ou vide. _dataBindingContext de la page l'article n'explique cela, mais il ne dit pas comment faire en sorte que est peuplée _dataBindingContext de la page. Je vais continuer à chercher.

Comme la documentation MSDN dit, DataBoundControl devrait mettre en œuvre PerformDataBinding au lieu de passer outre DataBind (). Je l'ai fait et fait ambivalents contraignant travail. Est-ce code nécessaire ou devrais-je utiliser intégré quelque chose?

Était-ce utile?

La solution

Avez-vous essayé DataBinder.Eval (Container.DataItem, ...) syntaxe?

Voir aussi cet article sur Bind () .

PS. Vous devez Databind sur chaque postback sauf si vous utilisez Viewstate pour préserver les valeurs.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top