ASP.NET - wykorzystanie FindControl do odczytania dynamicznie utworzonych kontrolek - proszę o pomoc

0

Opiszę sprawę jeszcze raz, stworzyłem nowy przykład aby był maksymalnie prosty, jednocześnie aby dobrze ilustrował mój problem.
Na stronie mamy pierwotnie trzy kontrolki: Panel, Label i Button. Do kontrolki Panel jest wrzucana kontrolka TextBox dynamicznie utworzona podczas zdarzenia Page_Load. Po kliknięciu na Button za pomocą metody FindControl jest odczytywana wartość wprowadzona do TextBoxa, i przypisywana do właściwości Text kontrolki Label. Działa jak należy ! Problem pojawia się w drugim przykładzie:/

Plik Default.aspx :

 
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="generowanie_html_5._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="Panel1" runat="server">
        </asp:Panel>
        <br />
        <asp:Label ID="Label1" runat="server" Text="Label"></asp:Label>
        <br />
        <asp:Button ID="odczytajWartosc" runat="server" Text="Odczytaj wartosci z kontrolek" onclick="odczytajWartosc_Click" />
    </div>
    </form>
</body>
</html>

Plik 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 generowanie_html_5
{
    public partial class _Default : System.Web.UI.Page
    {
        protected void Page_Load(object sender, EventArgs e)
        {
            TextBox txt = new TextBox();
            txt.ID = "textBox";
            this.Panel1.Controls.Add(txt);
        }

        protected void odczytajWartosc_Click(object sender, EventArgs e)
        {
            TextBox txt = new TextBox();
            txt = (TextBox)this.FindControl("textBox");
            Label1.Text = txt.Text;
        }
    }
}

Problem jest kiedy w podobny sposób próbuję odczytać kontrolkę dynamicznie utworzoną po kliknięciu przycisku.
Plik Default.aspx :

<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="generowanie_html_5._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="Panel1" runat="server">
        </asp:Panel>
        <br />
        <asp:Label ID="Label1" runat="server" Text="Label"></asp:Label>
        <br />
        <asp:Button ID="dodajKontrolke" runat="server" Text="Dodaj kontrolke" onclick="dodajKontrolke_Click" />
        <br />
        <asp:Button ID="odczytajWartosc" runat="server" Text="Odczytaj wartosci z kontrolek" onclick="odczytajWartosc_Click" />
    </div>
    </form>
</body>
</html>

Plik 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 generowanie_html_5
{
    public partial class _Default : System.Web.UI.Page
    {
        protected void Page_Load(object sender, EventArgs e)
        {
            TextBox txt = new TextBox();
            txt.ID = "textBox";
            this.Panel1.Controls.Add(txt);
        }

        protected void dodajKontrolke_Click(object sender, EventArgs e)
        {
            TextBox txt = new TextBox();
            txt.ID = "textBox2";
            this.Panel1.Controls.Add(txt);
        }

        protected void odczytajWartosc_Click(object sender, EventArgs e)
        {
            TextBox txt = new TextBox();
            txt = (TextBox)this.FindControl("textBox");
            Label1.Text = txt.Text;

            TextBox txt2 = new TextBox();
            txt2 = (TextBox)this.FindControl("textBox2");
            Label1.Text = txt2.Text;
        }
    }
}

Początek kodu jest identyczny jak w poprzednim przykładzie. Dodany jest Button, który po kliknięciu dodaje do kontrolki Panel jeszcze jeden TextBox. Kliknięcie przycisku "Odczytaj wartości z kontrolek" tym razem wyrzuca błąd, a dokładnie debugger zatrzymuje się na :

 Label1.Text = txt2.Text;

wskazując że obiekt txt2 jest null - czyli jak rozumiem nie został znaleziony na stronie. Dla jasności wklejam jeszcze źródło wygenerowanej strony:



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

</title></head>
<body>
    <form name="form1" method="post" action="Default.aspx" id="form1">
<div>
<input type="hidden" name="__VIEWSTATE" id="__VIEWSTATE" value="/wEPDwUKMTM1ODgwNzc1MWRkkxiP1nScQD/61zrWMMsGLf/427k=" />
</div>

<div>

	<input type="hidden" name="__EVENTVALIDATION" id="__EVENTVALIDATION" value="/wEWBQKvw9HfBALMyt74CwLMyvbZDALTq4smAp/Yl70H37AlxIIC/7GPfV5r9N0tjZWGZFY=" />
</div>
    <div>
        <div id="Panel1">
	
        <input name="textBox" type="text" id="textBox" /><input name="textBox2" type="text" id="textBox2" />
</div>
        <br />
        <span id="Label1">Label</span>
        <br />
        <input type="submit" name="dodajKontrolke" value="Dodaj kontrolke" id="dodajKontrolke" />
        <br />
        <input type="submit" name="odczytajWartosc" value="Odczytaj wartosci z kontrolek" id="odczytajWartosc" />
    </div>
    </form>
</body>
</html>

Zachęcam do odpalenia tego kodu u siebie, próbowałem na różne sposoby, między innymi tworząc kilka stron *.aspx, przekazując formularz do kolejnej strony i odwołując się nie przez this tylko przez PreviousPage, zawsze błąd pojawiał się w tym samym momencie. Ostatecznie opisana metoda ma być wykorzystana do generowania formularza w następujący sposób: Pierwsza strona lista z nazwami Formularzy do wyboru, po kliknięciu na podstawie wybranej nazwy jest pobierany z bazy szablon formularza i jest dla niego generowany formularz na stronie (do tego etapu jest ok), następnie wartości po wypełnieniu formularza mają być zczytane i wprowadzone do bazy po kliknięciu kolejnego przycisku, tutaj właśnie pojawia się opisany wyżej błąd bo przykład jest analogiczny. Nie znajduję kontrolek TextBox na stronie mimo że w źródle strony widzę że są. Co ciekawe jeśli kontrolka jest utworzona statycznie bezpośrednio w Default.aspx lub podczas pierwszego uruchomienia w Page_Load to jest zczytywana przez FindControl prawidłowo na każdym etapie.

0

Skoro dodajesz kontrolke do Panel1, powinieneś użyć zamiast

    txt = (TextBox)this.FindControl("textBox"); 

odwolanie do Panelu, w którym kontrolka się znajduje:

txt = (TextBox)this.Panel1.FindControl("textBox"); 

Sprawdzone, działa :)

0

Sprawdzał kolega czy działa ? Bo u mnie na dal nie działa, tzn. wyrzuca zawsze przy szukaniu drugiej kontrolki txt2, dodanej przez kliknięcie przycisku. Pierwszą znajduje poprawnie obojętnie czy jest this.FindControl czy this.Panel1.FindControl. Już to przerabiałem, ale sprawdziłem dla pewności jeszcze raz.

2
piotrekg1987 napisał(a):

tutaj właśnie pojawia się opisany wyżej błąd bo przykład jest analogiczny. Nie znajduję kontrolek TextBox na stronie mimo że w źródle strony widzę że są.

Nie ma ich w źródle strony. Wciśnięcie przycisku powoduje przeładowanie strony, więc są w niej tylko te umieszczone statycznie oraz te, które dodałeś dynamicznie w Page_Load, a nie te z dodajKontrolke_Click. Tu nie ma żadnego błędu, tak po prostu działają strony internetowe.

Jeśli chcesz mieć swoje dynamicznie dodane przyciskiem kontrolki dostępne po przekierowaniu, musisz je sobie odtworzyć. A żeby móc to zrobić, musisz najpierw zapisać gdzieś ich ID, najlepiej w ViewState.

using System;
using System.Collections.Generic;
using System.Web.UI.WebControls;

namespace generowanie_html_5
{
    public partial class _Default : System.Web.UI.Page
    {
        const string IdsStoreName = "txt";

        private List<string> TextBoxesIds
        {
            get
            {
                if (this.ViewState[IdsStoreName] == null)
                {
                    this.ViewState[IdsStoreName] = new List<string>();
                }
                return (List<string>)this.ViewState[IdsStoreName];
            }
        }

        protected void Page_Load(object sender, EventArgs e)
        {
            TextBox txt = new TextBox();
            txt.ID = "textBox1";
            this.Panel1.Controls.Add(txt);

            // odtworzenie kontrolek o podanych ID
            foreach (string id in this.TextBoxesIds)
            {
                TextBox txt2 = new TextBox();
                txt2.ID = id;
                this.Panel1.Controls.Add(txt2);
            }
        }

        protected void dodajKontrolke_Click(object sender, EventArgs e)
        {
            TextBox txt = new TextBox();
            txt.ID = "textBox2";
            this.Panel1.Controls.Add(txt);

            // dodatkowo zapamiętujemy ID kontrolki w ViewState
            this.TextBoxesIds.Add(txt.ID);
        }

        protected void odczytajWartosc_Click(object sender, EventArgs e)
        {
            TextBox txt = this.FindControl("textBox1") as TextBox;
            if (txt != null)
            {
                this.Label1.Text = txt.Text;
            }

            TextBox txt2 = this.FindControl("textBox2") as TextBox;
            if (txt2 != null)
            {
                this.Label2.Text = txt2.Text;
            }
        }
    }
}
0

Fajnie teraz działa, bardzo mi to pomogło, dzięki:) Mam jednak pytanie bo nie do końca to rozumiem. Kliknięcie przycisku dodającego TextBox wysyła żądanie do serwera który to w odpowiedzi dodaje do formularza kontrolkę TextBox (czy źle to rozumiem?). Po tej operacji na stronie mam tą kontrolkę (źródło strony ją zawiera - mam na myśli źródło strony która jest wyświetlona w przeglądarce). Następnie kliknięcie przycisku które ma odczytać wartości powoduje przesłanie formularza do serwera ? gdzie metoda FindControl powinna znaleźć tą kontrolkę i jej zawartość ? (Przypisanie tych wartości do Labela nie jest konieczne, raczej zależy mi na dostępie do tych danych po odesłaniu formularza.) Podejrzewam że gdzieś jest błąd w moim rozumowaniu, proszę o wyjaśnienie, pozdrawiam.

0

Tak jak napisałeś, każde kliknięcie przycisku to wysłanie formularza do serwera.

0

No właśnie a więc, na początku jest formularz z jednym TextBoxem, klikam "Dodaj kontrolke", formularz leci do serwera tam w odpowiedzi na kliknięcie jest wykonywany kod dodający do formularza TextBox2 i nowy formularz jest wysyłany do przeglądarki - tutaj mamy już 2 TextBoxy. Klikam na przycisk, "Odczytaj wartości z kontrolek" formularz jest wysyłany do serwera i tam FindControl szuka kontrolki. Przy drugim kliknięciu w formularzu w przeglądarce są przecież 2 TextBoxy. Wybacz że Cie tak męczę, ale moje rozumowanie mi nie wyjaśnia tej sytuacji ;/

2

Tak, ale nie patrz na to, co wysyłasz do serwera, tylko na to, co serwer zwraca w odpowiedzi. Serwer renderuje stronę i wywołuje zdarzenia w określonej kolejności.

Od początku:

  1. Wchodzisz na stronę po raz pierwszy:
    a) Generowana jest strona z jej statycznymi kontrolkami;
    b) Wykonywane jest zdarzenie Page_Load, więc dodawana jest dynamicznie kontrolka textBox;
  2. Klikasz przycisk "Dodaj kontrolkę":
    a) Generowana jest strona z jej statycznymi kontrolkami;
    b) Wykonywane jest zdarzenie Page_Load, więc dodawana jest dynamicznie kontrolka textBox;
    c) Wykonywane jest zdarzenie dodajKontrolke_Click i dodawana jest dynamicznie kontrolka textBox2.
  3. Klikasz przycisk "Odczytaj wartości":
    a) Generowana jest strona z jej statycznymi kontrolkami;
    b) Wykonywane jest zdarzenie Page_Load, więc dodawana jest dynamicznie kontrolka textBox;
    c) Wykonywane jest zdarzenie odczytajWartosc_Click. Kontrolka textBox jest na stronie, bo Page_Load się wykonał. Kontrolki textBox2 nie ma, bo niby skąd ma być, bo dodajKontrolke_Click wykonywany był przy poprzednim postbacku, a nie teraz.
0

Ok, a po kliknięciu "Odczytaj wartości" a przed wygenerowaniem odpowiedzi przez serwer (który generuje formularz od nowa, według tego co ma w Default.aspx i Page_Load), zawartość formularza nie jest przesyłana na serwer ? Nie potrzebuję aby formularz został ponownie wygenerowany u użytkownika tylko chcę pobrać wartości wprowadzone do dynamicznie utworzonych kontrolek w formie id_kontrolki/wartość. Może mogę to zrobić w inny sposób ?

0
piotrekg1987 napisał(a):

Ok, a po kliknięciu "Odczytaj wartości" a przed wygenerowaniem odpowiedzi przez serwer (który generuje formularz od nowa, według tego co ma w Default.aspx i Page_Load), zawartość formularza nie jest przesyłana na serwer ? Nie potrzebuję aby formularz został ponownie wygenerowany u użytkownika tylko chcę pobrać wartości wprowadzone do dynamicznie utworzonych kontrolek w formie id_kontrolki/wartość. Może mogę to zrobić w inny sposób ?

Poczytaj o AJAXie.

0
piotrekg1987 napisał(a):

Ok, a po kliknięciu "Odczytaj wartości" a przed wygenerowaniem odpowiedzi przez serwer (który generuje formularz od nowa, według tego co ma w Default.aspx i Page_Load), zawartość formularza nie jest przesyłana na serwer ?

Oczywiście, jest przesyłana i dostępna w zdarzeniach strony. Od tego masz zmienną this.Request.Form.

Hrypa napisał(a):

Poczytaj o AJAXie.

Tylko co mu to da?

0

Witam ponownie, dziękuję za wszystkie odpowiedzi. Mam jeszcze pytanie na temat Request.Form. A mianowicie mam już na stronie swoje dynamicznie wygenerowane kontrolki typu TextBox i RadioButton po wysłaniu formularza chcę wydobyć dane z tych kontrolek. Jednak obiekt Form, który wraca w odpowiedzi zawiera elementy w postaci nazwa/wartość i nie mam możliwości odczytu innych atrybutów, które nadałem kontrolce podczas jej generowania. Chodzi mi mianowicie o kwestie dostępu do atrybutów ID, name, czy też np. własnych atrybutów, które można wygenerować dla kontrolki za pomocą metody: Attributes.Add().
Chciałbym móc na poziomie generowania dodawać np. jako ID danej kontrolki - ID pytania którego ma dotyczyć odpowiedź wpisana do TextBoxa, aby podczas obróbki odebranego formularza w prosty sposób skojarzyć pytanie z odpowiedzią. W przeciwnym wypadku będę musiał troszkę pokombinować i przerobić parę linijek kodu :)

Będę wdzięczny za odpowiedź.
Pozdrawiam

1 użytkowników online, w tym zalogowanych: 0, gości: 1