我目前正在处理使用动态Web用户控件的应用程序的一部分,我在使用ViewState或其他一些方法找出重新实例化回发控件的最佳方法时遇到了一些麻烦。不要求我在每次回发时查询数据库。
基本上我的页面上有一个用户控件,其中包含一个用于保存可变数量的子用户控件的面板和一个显示“Add Control”的按钮,其功能非常自我解释。
子用户控件非常简单;它只是一个删除按钮,一个下拉菜单和一个排成一排的时间选择器控件。只要用户单击父控件上的“添加控件”按钮,就会向包含子控件的面板添加一个新的“行”。
我想要做的是能够添加和删除对此集合的控制,修改值,并执行我需要在内存中执行的任何操作,而无需对数据库进行任何调用。当我完成添加控件并填充其值时,我想单击“保存”以立即将控件中的所有数据保存/更新到数据库。目前我发现的唯一解决方案是简单地将每个帖子中的数据保存在数据库中,然后使用存储在数据库中的行来重新实例化回发上的控件。显然,这会强制用户根据自己的意愿保存对数据库的更改,并且如果他们想要取消使用控件而不保存数据,则必须进行额外的工作以确保删除先前提交的行。
根据我对使用动态控件的了解,我知道最好在生命周期的Init阶段将控件添加到页面,然后在加载阶段填充它们的值。我还了解到,确保您可以保持控件的视图状态的唯一方法是确保为每个动态控件提供唯一的ID,并确保在重新实例化控件时为其分配完全相同的ID。我还了解到,在生命周期的Init阶段之后,ViewState实际上才会被加载。这就是我的问题所在。如果我无法使用viewstate并且我不想对数据库执行任何调用,如何存储和检索这些控件的名称?是否可以使用ASP.net进行内存操作/批量保存值?
非常感谢任何帮助,
麦克
答案 0 :(得分:3)
我过去做过这个。自.NET 1.1以来,我没有必要这样做,但是委托人删除了相同的内容。
我是在Page_Load上做的,而不是Init必须重新加载你在最后一页循环中创建的控件。
首先,您需要跟踪在每个页面循环中创建的控件。这包括类型,名称等。
然后在每个页面加载时,您需要重建它们。
你可以通过重新创建控件,为它指定完全相同的id,将它添加到页面上的sampe位置,最后在ViewState [“LoadedControl”]中添加到控件类型。
这是我使用的代码,我只使用我创建的用户控件执行此操作。我没有尝试使用ASP.NET控件,但我认为它的工作原理相同。
在这种情况下,我有一个Triplets的ArrayList(请记住这是.NET 1.1),第一项是PageView ID。您的应用程序可能不需要它。
protected void Page_Load(object sender, System.EventArgs e)
{
//**********************************************************
//* dynCtlArray will hold a triplet with the PageViewID, *
//* ControlID, and the Control Name *
//**********************************************************
ArrayList dynCtlArray = (ArrayList)this.ViewState["dynCtlArray"];
if (dynCtlArray != null)
{
foreach (object obj in dynCtlArray)
{
Triplet ctrlInfo = (Triplet)obj;
DynamicLoadControl(ctrlInfo);
}
}
}
private void DynamicLoadControl(Triplet ctrlInfo)
{
// ERROR HANDLING REMOVED FOR ANSWER BECAUSE IT IS NOT IMPORTANT YOU SHOULD HANDLE ERRORS IN THIS METHOD
Control ctrl = this.LoadControl(Request.ApplicationPath
+ "/UC/" + (string)ctrlInfo.Third);
ctrl.ID = (string)ctrlInfo.Second;
// Create New PageView Item
Telerik.WebControls.PageView pvItem = this.RadMultiPage1.PageViews[(int)ctrlInfo.First];
pvItem.Controls.Add(ctrl);
/******************************************************
* The ControlName must be preserved to track the *
* currently loaded control *
* ****************************************************/
ViewState["LoadedControl"] = (string)ctrlInfo.Third;
}
private void RegisterDynControl(Triplet trip)
{
ArrayList dynCtlArray = (ArrayList)this.ViewState["dynCtlArray"];
if (dynCtlArray == null)
{
dynCtlArray = new ArrayList();
this.ViewState.Add("dynCtlArray", dynCtlArray);
}
dynCtlArray.Add(trip);
}
在您网页上的某些方法中
// Create new Control
Control ctrl = Page.LoadControl("../UC/MyUserControl.ascx");
// . . . snip .. .
// Create Triplet
Triplet ctrlInfo = new Triplet(0, ctrl.ID, "MyUserControl.ascx");
// RegisterDynControl to ViewState
RegisterDynControl(ctrlInfo);
// . . . snip .. .
要访问控件以保存信息,您必须执行this.Page.FindControl('');
答案 1 :(得分:3)
您可以存储您需要知道的最低限度,以便在会话中保留的集合中重新创建控件。会话在页面的初始阶段可用。
以下是您的示例。它包括:
Default.aspx,cs
- 用于存储用户控件的面板
- “添加控制按钮”,每次单击时都会添加用户控件
TimeTeller.ascx,cs
- 有一个名为SetTime的方法,它将控件上的标签设置为指定的时间。
Default.aspx
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="DynamicControlTest._Default" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
<title></title>
</head>
<body>
<form id="form1" runat="server">
<div>
<asp:Panel ID="pnlDynamicControls" runat="server">
</asp:Panel>
<br />
<asp:Button ID="btnAddControl" runat="server" Text="Add User Control"
onclick="btnAddControl_Click" />
</div>
</form>
</body>
</html>
Default.aspx.cs:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
namespace DynamicControlTest
{
public partial class _Default : System.Web.UI.Page
{
Dictionary<string, string> myControlList; // ID, Control ascx path
protected void Page_Load(object sender, EventArgs e)
{
}
protected override void OnInit(EventArgs e)
{
base.OnInit(e);
if (!IsPostBack)
{
myControlList = new Dictionary<string, string>();
Session["myControlList"] = myControlList;
}
else
{
myControlList = (Dictionary<string, string>)Session["myControlList"];
foreach (var registeredControlID in myControlList.Keys)
{
UserControl controlToAdd = new UserControl();
controlToAdd = (UserControl)controlToAdd.LoadControl(myControlList[registeredControlID]);
controlToAdd.ID = registeredControlID;
pnlDynamicControls.Controls.Add(controlToAdd);
}
}
}
protected void btnAddControl_Click(object sender, EventArgs e)
{
UserControl controlToAdd = new UserControl();
controlToAdd = (UserControl)controlToAdd.LoadControl("TimeTeller.ascx");
// Set a value to prove viewstate is working
((TimeTeller)controlToAdd).SetTime(DateTime.Now);
controlToAdd.ID = Guid.NewGuid().ToString(); // does not have to be a guid, just something unique to avoid name collision.
pnlDynamicControls.Controls.Add(controlToAdd);
myControlList.Add(controlToAdd.ID, controlToAdd.AppRelativeVirtualPath);
}
}
}
TimeTeller.ascx
<%@ Control Language="C#" AutoEventWireup="true" CodeBehind="TimeTeller.ascx.cs" Inherits="DynamicControlTest.TimeTeller" %>
<asp:Label ID="lblTime" runat="server"/>
TimeTeller.ascx.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
namespace DynamicControlTest
{
public partial class TimeTeller : System.Web.UI.UserControl
{
protected void Page_Load(object sender, EventArgs e)
{
}
public void SetTime(DateTime time)
{
lblTime.Text = time.ToString();
}
protected override void LoadViewState(object savedState)
{
base.LoadViewState(savedState);
lblTime.Text = (string)ViewState["lblTime"];
}
protected override object SaveViewState()
{
ViewState["lblTime"] = lblTime.Text;
return base.SaveViewState();
}
}
}
正如您所看到的,我仍然需要管理用户控件的内部视图状态,但viewstate包正在保存到页面并在回发时传回控件。我认为重要的是要注意我的解决方案与David的非常接近。我的例子中唯一的主要区别是它使用session而不是viewstate来存储控件信息。这允许在初始化阶段发生事情。请务必注意,此解决方案占用的服务器资源更多,因此在某些情况下可能不合适,具体取决于您的扩展策略。
答案 2 :(得分:1)
我实现了一个与Daniel的示例非常相似的页面,但由于技术限制,无法使用Session。但是,我发现通过在页面中使用字段,我可以在Page_Init事件期间回发并检索其值。