Pergunta

Esta pergunta era originalmente sobre conseguir uma ligação de mão dupla para o trabalho, mas devido à falta de respostas específicas e ao progresso ao longo do caminho, eu a atualizei - você pode verificar o histórico de edição, mas achei que isso é melhor para clareza.

A listagem de código abaixo permite que um único objeto seja bidirecional de banco de dados para um controle modelado. Gostaria de estender este exemplo da maneira mais simples possível para permitir o ninho de controles modelos de dados de dados bidirecionais igualmente bidirecionais para propriedades complexas do objeto mais raiz. Por exemplo, SampleFormData tem uma propriedade List<string> Items. Eu gostaria de poder vincular a esta lista dentro do modelo mais raiz (desta lista de código) e exibir os dados da string em uma lista editável de caixas de texto, talvez, com comandos para inserção, exclusão, rebindicada -Canges (de volta à propriedade List do objeto vinculado). Além disso, se essa fosse uma lista de um tipo complexo (SampleFormChildData, em vez de string), um novo incorporado SampleSpecificEntryForm poderia ser usado dentro da lista, vinculado a cada um dos itens da lista, como um repetidor. E assim por diante até as propriedades folhas simples, se o autor assim escolher. Os campos da interface do usuário não precisam ser gerados automaticamente, apenas disponíveis para ligação.

Nota: o caso do List<string> é especial porque mesmo as ligações internas não podem lidar com a string como o datAitem diretamente - vinculando -se diretamente às strings, pois os itens da nossa lista não são um requisito, mas certamente valioso.

Isso é diferente de um FormView Porque não é construído para esperar se vincular a uma de uma lista de itens, apenas a um único item persistiu no ViewState ou onde nunca. Ao contrário do FormView, ele possui apenas um único modelo padrão semelhante ao Edittemplate da FormView. Da mesma forma, a ligação a uma propriedade semelhante a uma coleção também teria apenas uma visualização - editar. Não há seleção da linha e depois edição. Tudo é editável o tempo todo. O objetivo é facilitar a construção de formas de mão dupla.

Parece -me que deveria haver dois tipos de ligação. SingleEntityBinding e CollectionBinding. SingleEntityBinding toma uma única instância de objeto como uma fonte de dados (como prototipada por SampleSpecificEntryForm) enquanto CollectionBinding pode estar vinculado ao seu pai SingleEntityBinding com atributos de DataSourceID="EntryForm1" DataMember="Items" Como no exemplo de código para DataList1 abaixo de. O ninho de qualquer tipo deve ser suportado em qualquer tipo. Manipulação da lista, como operações de tipo Inserir/Alterar/Excluir contra os dados do objeto de apoio, são de responsabilidade do autor do formulário; No entanto, essa mecânica seria relativamente simples de implementar.

Aqui está algum código, espero que ajude alguém. 200 pontos estão disponíveis para as melhores sugestões para essa meta estabelecida ...

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

Com uma página asp.net, o seguinte:

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

Também implementei o IDataSource, na tentativa de ser capaz de aninhar um componente de lista como So (dentro do):

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

Qualquer pensamento sobre como fazer isso funcionar de maneira cascata seria incrível (na propriedade Lista de itens, por exemplo). Um dos desafios aqui é que o bind () não pode se referir ao próprio objeto de banco de dados (uma string neste caso), mas em uma propriedade desse item - tornando a ligação a uma lista estranha.

Obrigado por qualquer ajuda!


Descobertas ao longo do caminho

Implementou o IDataitemContainer. Eu estava muito esperançoso de que isso consertasse, mas não. Nenhuma mudança notável. Opa, implementou -o na classe errada. Agora é vinculativo, mas os valores não estão sendo recuperados ao objeto vinculado no postback. Hmmm...

Como Este artigo sugere, Page.getDataitem () é a fonte da exceção. Esta exceção é lançada se o _databindingContext da página estiver nulo ou vazio. O artigo explica isso, mas não diz como garantir que o _databindingContext da página seja preenchido. Vou continuar procurando.

Como diz a documentação do MSDN, o DataboundControl deve implementar o desempenho do desempenho em vez de substituir o banco de dados (). Eu fiz isso e fiz um trabalho de ligação a um caminho. Este código é necessário ou devo usar algo embutido?

Foi útil?

Solução

Você tentou Databinder.eval (container.dataitem, ...) sintaxe?

Veja também este artigo sobre Ligar().

Ps. Você precisa banco de dados em cada postback, a menos que esteja usando o ViewState para preservar os valores.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top