一个自定义的模板asp.net控制的双向数据绑定 [英] Two-way databinding of a custom templated asp.net control

查看:285
本文介绍了一个自定义的模板asp.net控制的双向数据绑定的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

这个问题本来是关于得到双向绑定在所有的工作,但由于缺乏具体的答案,否则沿途的进步,我一直在更新它 - 你可以检查编辑历史记录,但我想通了这为清楚起见更好

This question was originally about getting two-way binding to work at all, but due to lack of specific answers and otherwise progress along the way, I've been updating it - You can check the edit history, but I figured this is better for clarity.

下面列出的代码允许单个对象是双向数据绑定到一个模板控制。我想在允许类似的双向数据绑定启用换根最对象的复杂类型属性模板控件嵌套可能是最简单的方式扩展这个例子。例如, SampleFormData 有一个属性列表与LT;字符串>项目。我希望能够绑定到根最模板(从这个代码清单)在这个列表中,无论是在文本框,或许是一个可编辑列表中显示的字符串数据,与INSERT命令,删除,重新绑定,进入-changes(回绑定对象的列表属性)。此外,如果这是一个复杂类型( SampleFormChildData ,而不是字符串),列表的新的嵌入式 SampleSpecificEntryForm 能列表内使用,绑定到每个列表的项目,像一个中继器。等下到叶简单的属性,如果作者愿意的话。 ui的字段不必是自动生成的,只可用于绑定。

The code listing below allows a single object to be two-way databound to a templated control. I'd like to extend this example in the simplest way possible to allow for nesting of similarly two-way databinding-enabled templated controls for complex-typed properties of the root-most object. For example, SampleFormData has a property List<string> Items. I'd like to be able to bind to this list within the root-most template (from this code listing), and either display the string data in an editable list of textboxes, perhaps, with commands for insert, delete, rebind-entered-changes (back to the bound object's List property). Also, if this were a list of a complex type (SampleFormChildData, rather than string), a new embedded SampleSpecificEntryForm could be used within the list, bound to each of the list's items, like a repeater. And so on down to the leaf-simple properties, if the author so chooses. The ui-fields need not be auto-generated, just available for binding.

注:的情况下,列表<串> 是特殊的,因为即使是内置的绑定不能处理字符串作为DataItem的直接 - 结合直接串在我们列表中的项目是不是必需的,但可以肯定的宝贵

Note: The case of the List<string> is special because even the built-in bindings can't handle string as the DataItem directly - binding to strings directly as items in our list is not a requirement, but certainly valuable.

这是从 FormView控件,因为它没有内置期望在视图状态或者其它任何持久绑定到的项目列表中的一个,只有一个项目。不像FormView控件,这只有类似于FormView的EditTemplate一个默认的模板。同样,绑定到一个集合类物业也只能有一个观点 - 编辑。没有选择的行,然后编辑。一切都是可编辑的所有时间。其目的是使双向结合形式更容易建立。

This is different from a FormView because it is not built to expect to bind to one of a list of items, only to a single item as persisted in viewstate or where ever. Unlike the FormView, this only has a single default template akin to FormView's EditTemplate. Likewise, binding to a collection-like property would also only have one view - edit. There is no selection of the row and then editing. Everything is editable all the time. The purpose is to make two-way bound forms easier to build.

在我看来,应该有分为两种类型结合。 SingleEntityBinding CollectionBinding SingleEntityBinding 需要一个对象实例作为数据源(如原型由 SampleSpecificEntryForm ),而 CollectionBinding 可以被绑定到它的父 SingleEntityBinding 的DataSourceID =EntryForm1的DataMember =项目中的 DataList1 中的代码示例。任一类型的嵌套应该在任何一种类型的支持。列表操作,如插入/变更/对后备对象的数据删除操作类型的形式作者的责任;然而,这样的机制将是实现相对简单。

Seems to me that there ought to be two kinds of binding. SingleEntityBinding and CollectionBinding. SingleEntityBinding takes a single object instance as a datasource (as prototyped by SampleSpecificEntryForm) while CollectionBinding could be bound to it's parent SingleEntityBinding with attributes of DataSourceID="EntryForm1" DataMember="Items" as in the code example for DataList1 below. Nesting of either type should be supported in either type. List manipulation such as insert/change/delete type operations against the backing-object's data are the responsibility of the form author; however, such mechanics would be relatively simple to implement.

下面是一些代码,希望它可以帮助别人。 200分是那里的朝...

Here's some code, hope it helps somebody. 200 points are out there for the best suggestions toward this laid-out goal...

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页面以下内容:

With an ASP.NET page the following:

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

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好了,企图能嵌套列表组件,像这样(在中):

I've implemented IDataSource as well, in an attempt to be able to nest a list component like so (within the ):

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



如何使在级联方式这项工作将是真棒(在项目列表属性的任何想法, 例如)。这里的一个挑战是,绑定()不能引用数据绑定对象本身(在这种情况下,一个字符串),但关于该项目的属性 - 使得结合尴尬列表

Any thoughts on how to make this work in a cascading way would be awesome (on the Items list property, for example). One of the challenges here is that Bind() can't refer to the databound object itself (a string in this case) but on a property of that item - making binding to a list awkward.

感谢您的帮助!

<击>实施IDataItemContainer。我非常希望,这将修复它,但没有。无noticable改变。哎呀,实现了它在错误的类。现在,它是有约束力的,但值不被反弹回发绑定的对象。嗯...

Implemented IDataItemContainer. I was very hopeful that this would fix it, but no. No noticable change. Oops, implemented it on the wrong class. Now it is Binding, but the values aren't being rebound to the bound object on postback. Hmmm...

<击>为这篇文章 建议,Page.GetDataItem()是个例外的来源。如果网页的_d​​ataBindingContext为null或空,抛出此异常。文章并解释这一点,但它并没有说如何确保页面的_dataBindingContext填充。我将继续寻找。

由于MSDN文档说,DataBoundControl应该实现PerformDataBinding,而不是覆盖的DataBind()。我这样做,并提出双向结合的工作。 ?这是代码必要或我应该使用一些内置的

As the MSDN documentation says, DataBoundControl should implement PerformDataBinding instead of overriding DataBind(). I've done so and made both-way-binding work. Is this code necessary or should I be using something built-in?

推荐答案

你试过的的DataBinder.Eval(集装箱.DataItem,...)语法?

也看到这篇文章的绑定()

PS。您需要在每次回传数据绑定,除非你正在使用视图状态保存值。

PS. You need to Databind on every postback unless you are using Viewstate to preserve values.

这篇关于一个自定义的模板asp.net控制的双向数据绑定的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆