2009-08-24 2 views
5

현재 동적 웹 사용자 정의 컨트롤을 사용하는 응용 프로그램의 일부로 작업 중이며 ViewState 또는 다른 방법을 사용하여 다시 게시 컨트롤을 다시 인스턴스화하는 가장 좋은 방법을 찾는 데 문제가 있습니다. 각 포스트 백에서 데이터베이스를 쿼리해야합니다.데이터베이스를 사용하지 않고 동적 ASP.NET 사용자 컨트롤을 어떻게 다시 인스턴스화 할 수 있습니까?

기본적으로 내 페이지에있는 것은 다양한 양의 하위 사용자 컨트롤과 "기능 추가"기능이 포함 된 사용자 컨트롤입니다.

자식 사용자 컨트롤은 매우 간단합니다. 그것은 단지 삭제 버튼, 드롭 다운 및 시간 피커 컨트롤을 연속으로 배열 한 것입니다. 사용자가 부모 컨트롤에서 컨트롤 추가 단추를 클릭 할 때마다 자식 컨트롤이 들어있는 패널에 새로운 '행'이 추가됩니다.

내가하고 싶은 것은이 컬렉션에 컨트롤을 추가 및 제거하고, 값을 수정하고, 데이터베이스에 대한 호출을하지 않고도 '메모리 내에서'수행해야하는 모든 작업을 수행 할 수있게하려는 것입니다. 컨트롤을 추가하고 값을 채우면 "저장"을 클릭하여 컨트롤의 모든 데이터를 한 번에 데이터베이스에 저장/업데이트합니다. 현재 내가 찾은 유일한 해결책은 각 포스트에 데이터베이스에 데이터를 저장 한 다음 DB에 저장된 행을 사용하여 포스트 백에서 컨트롤을 다시 인스턴스화하는 것입니다. 분명히 이것은 사용자가 유언장에 따라 DB에 변경 사항을 저장하도록하고 데이터를 저장하지 않고 컨트롤 작업을 취소하려는 경우 이전에 커밋 된 행이 삭제되도록 추가 작업을 수행해야합니다.

동적 컨트롤 사용법에서 배운 내용을 통해 수명주기의 초기 단계에서 컨트롤을 페이지에 추가 한 다음로드 단계에서 해당 값을 채우는 것이 가장 좋습니다. 또한 컨트롤의 viewstate를 유지할 수있는 유일한 방법은 각 동적 컨트롤에 고유 한 ID를 지정하고 컨트롤을 인스턴스화 할 때 정확히 동일한 ID를 할당하는지 확인하는 것입니다. 또한 ViewState는 라이프 사이클의 Init 단계 이후에 실제로로드되지 않는다는 것을 알게되었습니다. 이것은 내 문제가있는 곳입니다. Viewstate를 사용할 수없고 데이터베이스 호출을 수행하지 않으려는 경우이 컨트롤의 이름을 저장하고 검색하는 방법은 무엇입니까? ASP.net을 사용하여 가능한 일종의 메모리 내 조작/일괄 저장 값이 있습니까? 어떤 도움이 크게 감사합니다

,

마이크

+0

조금 늦었지만 다음과 같이합니다. http://stackoverflow.com/a/15346332/52898 – Tommy

답변

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를 관리해야하지만 viewstate 가방은 페이지에 저장되고 포스트 백 컨트롤에 다시 전달됩니다. 내 해결책은 데이비드와 아주 가깝다는 것을 알아 두는 것이 중요하다고 생각합니다. 내 예제의 유일한 주요 차이점은 viewstate 대신 세션을 사용하여 제어 정보를 저장한다는 점입니다. 이것은 초기화 단계에서 일어날 수 있습니다. 이 솔루션은 더 많은 서버 리소스를 차지하므로 확장 전략에 따라 상황에 따라 적절하지 않을 수 있습니다.

+0

내가 정말로 관심을 갖고있는 것은 내가로드하려고하는 컨트롤의 ID (아마도 순서)이기 때문에 나는 그 생각을 좋아한다. 그 솔루션에 대한 빠른 테스트 베드를 마련하고 다시 연락하겠습니다. 감사합니다. – mclark1129

+0

나는 그것이 당신이 일할 수 있기를 바랍니다. 나는 과거에 비슷한 것을해야했다. 컨트롤 라이프 사이클이 진행될 것으로 기대되기 때문에 init 단계의 페이지에서 컨트롤을 다시 얻는 것이 가장 좋습니다. David의 솔루션이 효과가있을 것이라고 확신하지만 장기적인 유지 관리가 필요합니다. 나도 그런 식으로 일을 끝내고 결국에는 "프레임 워크와 싸우는"시나리오로 변합니다. 라이프 사이클에서 나중에 추가하면 작동하지 않는 무언가가 결국 실행될 수 있습니다. –

+0

Daniel, 세션 솔루션을 사용하여 몇 가지 진전을 보았지만 두 가지 문제가 발생했습니다. 하나는 Init() 단계에서 컨트롤을 읽는 중임에도 불구하고 View State가 복원되지 않는다는 것입니다. 나는 그것이 읽혀질 때 동적 컨트롤에 설정해야하는 느낌이 들었습니다. View State 데이터를 다시 얻는 올바른 ID입니다. 누락 된 것이 있습니까? 또한 포스트 백을 사용하지 않아도 세션 데이터가 유지됩니다. 나는 Page.IsPostBack 일 때 Session을 null로 설정 하겠지만 Init() 단계가 끝날 때까지 플래그가 설정되지 않습니다. – mclark1129

3

나는 과거에 이런 짓을했는지. 나는 .NET 1.1의 시대부터 이것을 할 필요가 없었지만, 교장은 똑같은 것을 제거한다.

Page_Load에서 마지막 페이지 사이클에서 작성한 컨트롤을 다시로드하지 않아도됩니다.

먼저 각 페이지 사이클에서 생성 한 컨트롤을 추적해야합니다. 여기에는 유형, 이름 등이 포함됩니다. .

그런 다음 각 페이지로드시 다시 빌드해야합니다.

컨트롤을 다시 만들고 정확히 같은 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('');

+0

David, 감사합니다. 아직 솔루션을 사용해 볼 기회가 없었지만 질문이있었습니다. 동적 컨트롤을로드하기 위해 과거에 Page_Load를 사용했지만 일반적으로 Load 이벤트가 발생하기 때문에 문제가 발생합니다 뷰 상태가로드되었습니다 (따라서 컨트롤 ID의 배열에 도달 할 수있는 능력). 결과적으로 사용자가 입력 한 값은 뷰 상태가로드 될 때 컨트롤이 페이지에 아직 추가되지 않았기 때문에 포스트 백에서 손실됩니다. 이러한 컨트롤을 페이지에 추가하는 방식으로 View States를 수동으로로드합니까? – mclark1129

+0

Page_Load는 뷰 상태에 액세스 할 수 있어야합니다. 하지만 네, 뷰 상태를로드합니다. 예를 들어 텍스트 상자를 동적으로로드한다고 가정 해 보겠습니다. 내 프로세스에서는 '이 컨트롤에 주목하십시오.'라는 페이지를 말하고 있으므로로드 된 후에는 텍스트 상자 또는 뷰 상태 정보의 값을 가져올 수 있습니다.수동으로 작성한 페이지를 알려 주어야합니다. 그렇지 않으면 페이지에서이를 알 수 없습니다. –

1

Daniel의 예와 매우 유사한 페이지를 구현했지만 기술적 제약으로 인해 Session을 사용할 수 없습니다. 그러나 페이지에 필드를 사용하면 Page_Init 이벤트 중에 해당 값을 다시 게시하고 검색 할 수 있다는 것을 알게되었습니다.

관련 문제