c# - 自定义模板asp.net控件的双向数据绑定(bind)

标签 c# asp.net 2-way-object-databinding

这个问题原本是关于双向绑定(bind)完全起作用的,但是由于缺少具体的答案,并且在此过程中一直没有进展,因此我一直在对其进行更新-您可以查看编辑历史记录,但是我认为这样做对明晰。

下面的代码 list 允许将单个对象与模板控件进行双向数据绑定(bind)。我想以最简单的方式扩展此示例,以允许为最根对象的复杂类型的属性嵌套类似的启用了双向数据绑定(bind)的模板控件。例如,SampleFormData具有属性List<string> Items。我希望能够绑定(bind)到最底层模板(来自此代码 list )中的此列表,并且可以在文本框的可编辑列表中显示字符串数据,也许还可以使用插入,删除,重新绑定(bind)的命令输入-changes(返回绑定(bind)对象的List属性)。同样,如果这是复杂类型的列表(SampleFormChildData,而不是字符串),则可以在列表内使用新的嵌入式SampleSpecificEntryForm,绑定(bind)到列表的每个项目,例如转发器。如果作者选择,依此类推,直到叶子简单的属性。 ui字段无需自动生成,仅可用于绑定(bind)。

注意:List<string>的情况很特殊,因为即使内置绑定(bind)也不能直接将字符串作为DataItem处理-作为我们列表中的项直接绑定(bind)到字符串不是必需条件,但肯定很有值(value)。

这与FormView不同,因为它并不是为了期望绑定(bind)到项目列表之一,而是绑定(bind)到在viewstate或任何地方存在的单个项目而设计的。与FormView不同,它只有一个类似于FormView的EditTemplate的默认模板。同样,绑定(bind)到类似集合的属性也只有一个 View -编辑。没有选择该行然后进行编辑。一切都是可编辑的。目的是使双向绑定(bind)表单更易于构建。

在我看来,应该有两种绑定(bind)。 SingleEntityBindingCollectionBindingSingleEntityBinding将单个对象实例作为数据源(由SampleSpecificEntryForm原型(prototype)化),而CollectionBinding可以绑定(bind)到其父SingleEntityBinding并具有DataSourceID="EntryForm1" DataMember="Items"的属性,如下面的DataList1的代码示例所示。任何一种类型都应支持任何一种嵌套。列表创建者负责列表操作,例如对后备对象的数据进行插入/更改/删除类型操作。但是,这种机制实现起来相对简单。

这是一些代码,希望对您有所帮助。有200分可以为实现这一既定目标提供最佳建议...

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

通过ASP.NET页面,可以执行以下操作:
<%@ 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();
        }
    }
}

我还实现了IDataSource,以试图能够像这样在(中)嵌套一个列表组件:
<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>

关于如何以级联方式进行这项工作的任何想法都很棒(例如,在Items列表属性上)。这里的挑战之一是Bind()不能引用数据绑定(bind)对象本身(在这种情况下为字符串),而是引用该项目的属性-绑定(bind)到列表很尴尬。

谢谢你的帮助!

一路发现

实现了IDataItemContainer。我非常希望可以解决此问题,但是没有。没有明显的变化。糟糕,在错误的类(class)上实现了它。现在是Binding,但是值不会在回发时反弹到绑定(bind)的对象上。嗯...

正如 this article 所建议的那样,Page.GetDataItem()是异常的来源。如果页面的_dataBindingContext为null或为空,则抛出此异常。本文确实对此进行了解释,但没有说明如何确保填充页面的_dataBindingContext。我会继续寻找。

正如MSDN文档所说,DataBoundControl应该实现PerformDataBinding,而不是重写DataBind()。我这样做了,并且做了双向绑定(bind)工作。这段代码是必需的还是我应该使用内置的东西?

最佳答案

您是否尝试过Databinder.Eval(Container.DataItem,...)语法?

另请参阅Bind()上的这篇文章。

PS。除非您使用Viewstate保留值,否则您需要在每个回发中进行Databind。

关于c# - 自定义模板asp.net控件的双向数据绑定(bind),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2854234/

相关文章:

wpf - DataGrid 绑定(bind)到 List<int?>

c# - 如果我在一个方法中有两个 await Task.Run(),第二个会等待第一个完成吗?

C#插入表,报错

c# - 计算从具有不同高度的 (0,0) 以外的位置击中坐标 (x,y) 所需的角度

c# - 在 Asp.net 中检查数据读取器是否有行

asp.net - ASP.NET 中什么是双向(2-way)数据绑定(bind)?

c# - Observable.ObserveOn() 似乎没有效果

javascript - 如何使用位于 contentplaceholder 中的 asp.net 复选框控件的 javascript 函数?

c# - 为多个ASP.NET用户缓存WCF ChannelFactories

Android 数据绑定(bind) : can't find setter for attribute android:onClick