You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

ASP.NET WebForms 4.8 FormView绑定含自定义列表模型的问题

ASP.NET WebForms 4.8 FormView列表属性绑定问题及解决方案

问题描述

在ASP.NET WebForms 4.8项目中使用FormView双向模型绑定,标量属性和枚举类型绑定正常,但包含自定义类型列表的属性存在三类问题:

  • 问题1:SelectMethod返回的模型包含List<自定义类>属性,Repeater可在EditView中正常渲染(自定义类已标记[Serializable]),但UpdateMethod中该属性始终为null;
  • 问题2:已创建添加列表项的按钮,但后台方法无法引用当前编辑的模型实例来添加新项;
  • 问题3:添加项后,无法重新渲染Repeater显示新项,且希望保留模型其他未保存的更改。

示例代码

ASPX页面

<%@ Page Title="Home Page" Language="C#" AutoEventWireup="true" Inherits="ModelExample._Default" CodeBehind="~/Default.aspx.cs" %>
<html>
<head></head>
<body>
    <form runat="server" id="MainForm">
        <asp:FormView ID="frmVw" runat="server" DefaultMode="Edit"
            EnableModelValidation="true" ItemType="Models.PetModel"
            DataKeyNames="PetId"
            SelectMethod="frmVw_GetItem" UpdateMethod="frmVw_UpdateItem">
            <EmptyDataTemplate>
                No pet found, try adding ?PetId=11 as a query string parameter.
            </EmptyDataTemplate>
            <EditItemTemplate>
                <div id="EditPetDetails">
                    <div><h2>Pet Details:</h2></div>
                    <div>
                        <asp:Label runat="server" ID="lblPetName" Text="Name"></asp:Label>
                        <asp:TextBox runat="server" ID="tbPetName" Text="<%# BindItem.Name %>"></asp:TextBox>
                    </div>
                    <div>
                        <asp:Label runat="server" ID="lblDob" Text="Date of Birth"></asp:Label>
                        <asp:TextBox runat="server" ID="tbDob" Text='<%# BindItem.DateOfBirth %>' TextMode="Date"></asp:TextBox>
                    </div>
                    <div>
                        <asp:Label runat="server" ID="lblSpecies" Text="Species"></asp:Label>
                        <asp:DropDownList runat="server" ID="ddlSpecies" SelectMethod="ddlSpecies_Get" SelectedValue='<%# BindItem.Species %>'></asp:DropDownList>
                    </div>
                    <div>
                        <asp:Label runat="server" ID="lblWeight" Text="Weight (kg)"></asp:Label>
                        <asp:TextBox runat="server" ID="tbWeight" Text="<%# BindItem.Weight %>" TextMode="Number"></asp:TextBox>
                    </div>
                    <div>
                        <asp:Button runat="server" ID="btnSave" Text="Save" CommandName="Update" />
                    </div>
                </div>
                <hr />
                <div id="AddNotes">
                    <div>
                        <h2>Add Note:</h2>
                    </div>
                    <div>
                        <asp:Label runat="server" ID="lblNewNoteType" Text="Type"></asp:Label>
                        <asp:DropDownList runat="server" ID="ddlNewNoteType" SelectMethod="NoteType_Get"></asp:DropDownList>
                    </div>
                    <div>
                        <asp:TextBox runat="server" ID="tbNewNoteBody" TextMode="MultiLine"
                            Rows="5" Columns="50"></asp:TextBox>
                    </div>
                    <div>
                        <asp:Button runat="server" ID="btnAddNote" Text="Add Note" OnClick="btnAddNote_Click" />
                    </div>
                </div>
                <hr />
                <div id="NotesHistory">
                    <h2>Notes Histroy:</h2>
                    <asp:Repeater runat="server" ID="rptNotes" ItemType="Models.VisitNote" DataSource="<%# BindItem.VisitNotes %>">
                        <ItemTemplate>
                            <div>
                                <asp:Label runat="server" ID="lblNoteCreatedOn" Text="Date:"></asp:Label>
                                <asp:TextBox runat="server" ID="tbNoteCreatedOne" Text="<%# Item.CreatedOn %>" TextMode="Date" ReadOnly="true"></asp:TextBox>
                            </div>
                            <div>
                                <asp:Label runat="server" ID="lblNoteCreatedBy" Text="Author:"></asp:Label>
                                <asp:Label runat="server" ID="tbNoteCreatedBy" Text="<%# Item.CreatedBy %>"></asp:Label>
                            </div>
                            <div>
                                <asp:Label runat="server" ID="lblNoteType" Text="Type:"></asp:Label>
                                <asp:Label runat="server" ID="tbNoteType" Text="<%# Item.NoteType %>"></asp:Label>
                            </div>
                            <div>
                                <asp:TextBox runat="server" ID="tbNoteBody" Text="<%# Item.NoteBody %>"
                                    ReadOnly="true" TextMode="MultiLine" Rows="5" Columns="50"></asp:TextBox>
                            </div>
                        </ItemTemplate>
                    </asp:Repeater>
                </div>
            </EditItemTemplate>
        </asp:FormView>
    </form>
</body>
</html>

后台代码

using Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.ModelBinding;
using System.Web.UI;
using System.Web.UI.WebControls;

namespace ModelExample
{
    public partial class _Default : Page
    {
        private static List<PetModel> _pets = new List<PetModel>();
        protected void Page_Load(object sender, EventArgs e)
        {
            if (!IsPostBack && !_pets.Any())
            {
                var notesCollection = new List<Models.VisitNote>()
                {
                    new Models.VisitNote() {
                        NoteId = 11, CreatedBy = "Jeanann Sutherland", CreatedOn = "2021-10-31",
                        NoteType = Models.NoteTypes.Intake,
                        NoteBody = "Pekoe has presented with sneezing, enflamed nose, nasal drainage and abnormaly clingy demeanor."
                    }
                };
                _pets.Add(new Models.PetModel()
                {
                    PetId = 11,
                    DateOfBirth = "2014-07-24",
                    Name = "Pekoe",
                    Species = Models.PetSpecies.Cat,
                    Weight = 12.3f,
                    VisitNotes = notesCollection
                });
            }
        }

        public IEnumerable<string> ddlSpecies_Get()
        {
            return Enum.GetNames(typeof(Models.PetSpecies)).ToList<string>();
        }
        public IEnumerable<string> NoteType_Get()
        {
            return Enum.GetNames(typeof(Models.NoteTypes)).ToList<string>();
        }
        public Models.PetModel frmVw_GetItem([QueryString] int? PetId)
        {
            return _pets.FirstOrDefault(p=> p.PetId == PetId);
        }

        public void frmVw_UpdateItem(int? PetId)
        {
            Models.PetModel item = _pets.FirstOrDefault(p=> p.PetId == PetId);
            if (item == null)
            {
                ModelState.AddModelError("", String.Format("Item with id {0} was not found", PetId));
                return;
            }
            var existingNotes = item.VisitNotes;
            TryUpdateModel(item);
            item.VisitNotes = existingNotes;
            var notesCount = item.VisitNotes?.Count() ?? 0;
        }

        protected void btnAddNote_Click(object sender, EventArgs e)
        {
            int petId = Convert.ToInt32(frmVw.DataKeys[0].Value);
            var pet = _pets.FirstOrDefault(p => p.PetId == petId);
            if (pet == null) return;

            var ddlNewNoteType = frmVw.FindControl("ddlNewNoteType") as DropDownList;
            var tbNewNoteBody = frmVw.FindControl("tbNewNoteBody") as TextBox;
            if (ddlNewNoteType == null || tbNewNoteBody == null) return;

            var author = "Dr P Sorthes";
            NoteTypes noteType = Enum.Parse<NoteTypes>(ddlNewNoteType.SelectedValue);
            string body = tbNewNoteBody.Text.Trim();

            pet.VisitNotes.Add(new VisitNote() 
            { 
                CreatedBy = author, 
                NoteType = noteType, 
                CreatedOn = DateTime.UtcNow.ToString("yyyy-MM-dd"), 
                NoteBody = body 
            });

            UpdateModel(pet, new FormValueProvider(ModelBindingExecutionContext));
            frmVw.DataBind();
        }
    }
}

namespace Models
{
    public class PetModel
    {
        private DateTime? dateOfBirth;
        public PetModel()
        {
            VisitNotes = new List<VisitNote>();
        }
        public int PetId { get; set; }
        public string Name { get; set; }
        public string DateOfBirth { get { return dateOfBirth?.ToString("yyyy-MM-dd") ?? ""; } set { dateOfBirth = DateTime.Parse(value); } }
        public PetSpecies Species { get; set; }
        public Single Weight { get; set; }
        public List<VisitNote> VisitNotes { get; set; }
    }
    [Serializable]
    public class VisitNote
    {
        private DateTime? createdOn;
        public int NoteId { get; set; }
        public String CreatedOn { get { return createdOn?.ToString("yyyy-MM-dd") ?? ""; } set { createdOn = DateTime.Parse(value); } }
        public String CreatedBy { get; set; }
        public NoteTypes NoteType { get; set; }
        public String NoteBody { get; set; }
    }
    [Serializable]
    public enum NoteTypes
    {
        Intake,
        Diagnosis,
        Prescription,
        Followup
    }
    [Serializable]
    public enum PetSpecies
    {
        Dog,
        Cat,
        Bird,
        Fish,
        Snake
    }
}

解决方案说明

问题1:UpdateMethod中List属性为null

  1. 初始化集合:在PetModel构造函数中初始化VisitNotes集合,避免默认null值;
  2. 保留原有集合:在Update方法中先保存原有集合,执行TryUpdateModel后再恢复集合,防止被模型绑定覆盖为null。

问题2:获取当前模型实例及控件值

  1. 通过DataKeys获取当前ID:利用FormView的DataKeys属性获取当前编辑的PetId,再从数据源中获取对应模型;
  2. FindControl获取控件值:通过FormView的FindControl方法定位新增笔记的表单控件,读取输入值。

问题3:重新渲染Repeater并保留未保存更改

  1. 同步表单值到模型:调用UpdateModel将当前表单的未保存输入同步到模型;
  2. 重新绑定FormView:执行frmVw.DataBind()刷新整个FormView,Repeater会自动加载更新后的列表数据;
  3. 避免重复初始化数据:修改Page_Load,仅在首次加载时初始化测试数据,回发时跳过,防止覆盖已修改的数据。

内容的提问来源于stack exchange,提问作者DCAggie

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.22 06:27:35