Как я могу восстановить экземпляры динамических пользовательских элементов управления ASP.NET без использования базы данных? - PullRequest
5 голосов
/ 24 августа 2009

В настоящее время я работаю с частью моего приложения, которая использует Dynamic Web User Controls, и у меня возникли проблемы с поиском наилучшего способа создания экземпляров элементов управления при обратной передаче с помощью ViewState или другого метода, который не требует, чтобы я запрашивал базу данных при каждой обратной передаче.

В основном на моей странице есть пользовательский элемент управления, который содержит панель для хранения переменного количества дочерних пользовательских элементов управления и кнопку с надписью «Добавить элемент управления», функция которой довольно понятна.

Дочерний пользовательский элемент управления довольно прост; это просто кнопка удаления, выпадающий список и элемент управления выбора времени, расположенные в ряд. Каждый раз, когда пользователь нажимает кнопку «Добавить элемент управления» на родительском элементе управления, на панель с дочерними элементами управления добавляется новая «строка».

То, что я хотел бы сделать, - это иметь возможность добавлять и удалять элементы управления в эту коллекцию, изменять значения и выполнять любые необходимые мне операции «в памяти» без необходимости каких-либо обращений к базе данных. Когда я закончу добавлять элементы управления и заполнять их значения, я хочу нажать «сохранить», чтобы сохранить / обновить все данные из элементов управления в базе данных сразу. В настоящее время единственное решение, которое я нашел, - просто сохранять данные в базе данных каждый пост обратно, а затем использовать строки, хранящиеся в БД, для повторного создания элементов управления постбэком. Очевидно, что это вынуждает пользователя сохранять изменения в БД против их воли, и в случае, если он хочет отменить работу с элементами управления без сохранения своих данных, необходимо выполнить дополнительную работу, чтобы гарантировать, что ранее зафиксированные строки были удалены.

Из того, что я узнал об использовании динамических элементов управления, я знаю, что лучше всего добавлять элементы управления на страницу на этапе инициализации жизненного цикла, а затем заполнять их значения на этапе загрузки. Я также узнал, что единственный способ убедиться в том, что вы можете сохранить состояние представления элемента управления, это убедиться, что вы присваиваете каждому динамическому элементу управления уникальный идентификатор и обязательно назначаете ему точно такой же идентификатор при повторном создании элемента управления. Я также узнал, что ViewState на самом деле не загружается до окончания этапа Init в жизненном цикле. Вот где моя проблема. Как мне сохранить и получить имена этих элементов управления, если я не могу использовать состояние представления и не хочу выполнять какие-либо вызовы в базе данных? Возможна ли подобная манипуляция в памяти / пакетное сохранение значений даже с использованием ASP.net?

Любая помощь с благодарностью,

Mike

Ответы [ 3 ]

3 голосов
/ 24 августа 2009

Вы можете хранить минимум того, что вам нужно знать, чтобы воссоздать элементы управления в коллекции, хранящейся в сессии. Сессия доступна на начальных этапах страницы.

Вот пример для вас. Он состоит из:

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 для хранения управляющей информации. Это позволяет вещам происходить во время фазы инициализации. Важно отметить, что это решение потребляет больше ресурсов сервера, поэтому в некоторых ситуациях оно может не подходить в зависимости от вашей стратегии масштабирования.

3 голосов
/ 24 августа 2009

Я делал это в прошлом. Мне не приходилось делать это со времен .NET 1.1, но принципал удаляет то же самое.

Я сделал это на Page_Load, но Init не нужно перезагружать элементы управления, которые вы создали на последнем цикле страницы.

Сначала вы должны отслеживать элементы управления, которые вы создали на каждом цикле страницы. Это включает в себя тип, имя и т. Д. .

Затем при каждой загрузке страницы их нужно перестраивать.

Это делается путем повторного создания элемента управления, присвоения ему того же идентификатора, добавления его в место sampe на странице и, наконец, в ViewState ["LoadedControl"] для типа элемента управления.

Вот код, который я использовал, я делал это только с пользовательскими элементами управления, которые создал. Я не пробовал это с элементом управления ASP.NET, но я думаю, что он будет работать так же.

В этом случае у меня есть ArrayList of Triplets (имейте в виду, что это .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 голос
/ 06 июля 2011

Я реализовал страницу, очень похожую на пример Даниэля, но не смог использовать Session из-за технических ограничений. Тем не менее, я обнаружил, что, используя поле для страницы, я могу отправить обратно и получить его значение во время события Page_Init.

...