[C#/.NET] Dynamische Controls en Postback troubles

Pagina: 1
Acties:

  • Skinny
  • Registratie: Januari 2000
  • Laatst online: 22-03 20:57
Over mijn probleem (of soortgelijk in iedergeval) is veel te vinden via google en diverse .net fora, maar ik kom er maar niet uit waar ik de denkfout maak. Wat is er aan de hand ?

Simpel gezegd heb ik bij de eerste keer laden van de pagina een DataList gevuld met gegevens (Companies). Als ik nu een item hieruit selecteer creer ik een DataGrid (Policies) op basis van het gekozen item. Uit de tweede lijst wordt ook een item gekozen op basis waarvan een Repeater (Customers) wordt weergegeven.

Deze Repeater bevat weer DataGrids (jaja), maar dit werkt allemaal goed dus wees gerust. Deze laatste DataGrids bouw ik via code op, omdat de vorm hiervan nog niet van te voren vastligt.

code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
DataGrid dg = new DataGrid();
dg.AutoGenerateColumns = false;

// Events
dg.ItemDataBound += new DataGridItemEventHandler(dg_ItemDataBound);
dg.ItemCommand +=new DataGridCommandEventHandler(dg_ItemCommand);

bc = new BoundColumn();
bc.DataField = "ABALPH";
dg.Columns.Add(bc);

..meer velden..

dg.DataSource = datasource;
dg.DataBind();

parentcontrol.Controls.Add(dg);


Op het eerste gezicht werkt dit dus allemaal goed. Echter voeg ik in een van de kolommen een LinkButton (of Button) toe waarmee ik bepaalde akties wil laten uitvoeren op de regel waarachter deze button staat. Bij DataGrid die ik van te voren ontwerp gebruik ik altijd het ItemCommand event en daarom heb ik die hier ook aangemaakt (zie boven). Bij het klikken op deze knoppen wordt er wel een PostBack uitgevoerd, maar ik de aangegeven functies worden met geen mogelijkheid uitgevoerd.

Tevens verdwijnt de hele DataGrid ook na deze postback. Dit "probleem" met dynamisch gegenereerde controls is bekend, maar ik kom maar niet op de juiste oplossing.

Misschien iemand een tipje ?

[edit]Na het plaatsen, zie ik dat het een aardig warrig verhaal is geworden.. 8)7 , mocht je het niet snappen...of niet helemaal.. vraag het dan even ;)

[ Voor 5% gewijzigd door Skinny op 05-12-2003 17:35 ]

SIZE does matter.
"You're go at throttle up!"


Verwijderd

edit:
...nee onzin...

[ Voor 123% gewijzigd door Verwijderd op 05-12-2003 17:37 ]


  • EfBe
  • Registratie: Januari 2000
  • Niet online
Data bewaren (in viewstate bv), datagrid kent de data na postback niet meer. Postback event komt aan bij datagrid, niet bij button. je moet dus middels de eventhandler van de grid kijken welke knop is geklikt. Hierover is een quickstart gemaakt op www.asp.net.

Creator of: LLBLGen Pro | Camera mods for games
Photography portfolio: https://fransbouma.com


Verwijderd

Het probleem van in code-behind toegevoegde controls is dat ze geen onderdeel uitmaken van de pagina zoals normaal toegevoegde controls (in de aspx file) dat doen.
Om ze te laten gedragen als normale controls is het zaak om ze te herstellen voor de ViewState hersteld wordt. De meeste events zijn namelijk afhankelijk van ViewState.
De beste plaats om dit te doen is in de LoadViewState page event. Deze vuurt na de PageInit en voor de PageLoad. Voeg op deze plaats je dynamische gegenereerde controls opnieuw toe en ze zullen normaal verschijnen en events triggeren.

  • whoami
  • Registratie: December 2000
  • Laatst online: 15:14
Heb je een CommandName aan die button of linkbutton gegeven?
Kijk je dan in je ItemCommand welke CommandName aan de DataGridCommandEventArgs wordt meegegeven, en voer je dan de gepaste aktie uit?

code:
1
2
bc = new BoundColumn();
bc.CommandName = "blaat";


En dan in je ItemCommand:
code:
1
2
3
4
if( e.CommandName == "blaat" )
{
   // do stuff.
}

[ Voor 32% gewijzigd door whoami op 05-12-2003 18:23 ]

https://fgheysels.github.io/


  • gorgi_19
  • Registratie: Mei 2002
  • Laatst online: 18:08

gorgi_19

Kruimeltjes zijn weer op :9

@markm

Waarom voeg je ze niet toe bij je init-event?

En het binden kan je imho beter niet in het init event doen, maar in het load-event.

Visual Basic .NET:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
Imports System.Web.UI
Imports System.Web.UI.WebControls

' ***********************************************************************************
'
' VoorbeelDatagridUserControl Class
'
' Een voorbeeldclass hoe een datagrid dynamisch toe te voegen aan een usercontrol.
' Het principe geldt eventueel ook als je hem wilt toevoegen aan je Page class;
' vervang hiervoor UserControl door System.Web.UI.Page
'
' ***********************************************************************************'

Public Class VoorbeelDatagridUserControl
    Inherits UserControl

    ' ***********************************************************************************
    '
    ' InitPage method
    '
    ' Hier vindt de initialisatie van de controls plaats. We voegen alleen de controls
    ' toe aan de controlcollection, we doen er verder niets mee, anders vernaggelen we
    ' onze postback misschien.
    ' Verder mag je niet bij een postback opeens controls gaan toevoegen; anders snapt
    ' je viewstate er niets meer van en krijg je viewstate errors.
    ' Bij een postback moet de controlcollection exact dezelfde zijn als de vorige keer.
    '
    ' ***********************************************************************************'

    Private Sub InitPage(ByVal sender As Object, ByVal e As EventArgs) Handles MyBase.Init

        Dim __datagrid As DataGrid = New DataGrid
        __datagrid.ID = "TestDataGrid"
        __datagrid.DataKeyField = "PersonID"

        __datagrid.Columns.Add(Me.GetNameBoundColumn)

        AddHandler __datagrid.DeleteCommand, AddressOf Me.HandleDeleteCommand
        AddHandler __datagrid.ItemCommand, AddressOf Me.HandleItemCommand

        Me.Controls.Add(__datagrid)

    End Sub

    ' ***********************************************************************************
    '
    ' LoadPage method
    '
    ' Hier geven we de control een waarde. Aangezien ik een bloedhekel heb aan geneste
    ' if's, spring ik er al vroeg uit. Bij een postback willen we niet opnieuw
    ' laden, anders heeft een edit, update, insert, etc. geen enkele zin, omdat we onze
    ' net ingevulde waarden weer overschrijven.
    '
    ' ***********************************************************************************'

    Private Sub LoadPage(ByVal sender As Object, ByVal e As EventArgs) Handles MyBase.Load

        If Page.IsPostBack Then Exit Sub

        Me.LoadDatagrid()

    End Sub

    ' ***********************************************************************************
    '
    ' HandleDeleteCommand method
    '
    ' Een voorbeeldhandler van de datagrid. Aangezien een datagrid de sender is, casten
    ' we deze weer terug naar de datagrid, zodat de code evt. ook bruikbaar is indien
    ' een actie uitgevoerd moet worden op meerdere datagrids.
    ' Imho hoort er nog 1 extra actie bij, namelijk een redirect naar zichzelf. De reden?
    ' Anders kan je hele vreemde effecten krijgen als mensen op F5 drukken en op deze 
    ' manier kan je het een beetje voorkomen.
    '
    ' ***********************************************************************************'

    Private Sub HandleDeleteCommand(ByVal sender As Object, ByVal e As DataGridCommandEventArgs)

        Dim __datagrid As DataGrid = CType(sender, DataGrid)
        Dim __itemToDelete As Integer = Convert.ToInt32(__datagrid.DataKeys(e.Item.ItemIndex))

        ' Voer deleteactie uit

        Response.Redirect(Request.RawUrl)

    End Sub

    ' ***********************************************************************************
    '
    ' HandleItemCommand method
    '
    ' Een voorbeeldhandler van de datagrid. Aangezien een datagrid de sender is, casten
    ' we deze weer terug naar de datagrid, zodat de code evt. ook bruikbaar is indien
    ' een actie uitgevoerd moet worden op meerdere datagrids.
    '
    ' ***********************************************************************************'

    Private Sub HandleItemCommand(ByVal sender As Object, ByVal e As DataGridCommandEventArgs)

        Dim __datagrid As DataGrid = CType(sender, DataGrid)

        ' etc.

    End Sub

    ' ***********************************************************************************
    '
    ' LoadDatagrid method
    '
    ' Het feitelijk laden van de datagrid. Aangezien na iedere update de datagrid opnieuw
    ' gebind moet worden, krijgt deze een aparte method.
    '
    ' ***********************************************************************************'

    Private Sub LoadDatagrid()

        Dim __datagrid As DataGrid = CType(Me.FindControl("TestDataGrid"), DataGrid)
        __datagrid.DataSource = New ArrayList ' Hier zet je normaliter je datasource neer
        __datagrid.DataBind()

    End Sub

    ' ***********************************************************************************
    '
    ' GetNameBoundColumn method
    '
    ' Voor de overzichtelijk even een kleine aparte functie geschreven, zodat m'n InitPage
    ' niet een enorme bende wordt en wordt overheerst door het aanmaken van een control.
    '
    ' ***********************************************************************************'

    Private Function GetNameBoundColumn() As BoundColumn

        Dim __bc As BoundColumn = New BoundColumn
        __bc.DataField = "name"
        __bc.ItemStyle.CssClass = "cellitem"

        Return __bc

    End Function


End Class

[ Voor 229% gewijzigd door gorgi_19 op 05-12-2003 19:11 ]

Digitaal onderwijsmateriaal, leermateriaal voor hbo


Verwijderd

ehm... waarom override je niet gewoon de bestaande events voor deze acties, zo kan je beter gebruik maken van reeds bestaande functionaliteit. Zo kan je voor het instantieren van controls gebruik maken van CreateChildControls() en zodoende de bijbehorende EnsureChildControls() voor gebruik in properties.
Wat de Init/LoadViewState betreft; het is maar net wat je wilt. Het voordeel van LVS is dat deze alleen vuurt na een postback (dus niet bij het initieel laden van de pagina) en je daar ook toegang hebt tot eventuele data die je handmatig aan je ViewState hebt toegevoegd, bijvoorbeeld informatie over de betreffende controls die je geinstantieerd wilt zien.

  • gorgi_19
  • Registratie: Mei 2002
  • Laatst online: 18:08

gorgi_19

Kruimeltjes zijn weer op :9

Verwijderd schreef op 05 december 2003 @ 19:24:
ehm... waarom override je niet gewoon de bestaande events voor deze acties, zo kan je beter gebruik maken van reeds bestaande functionaliteit. Zo kan je voor het instantieren van controls gebruik maken van CreateChildControls() en zodoende de bijbehorende EnsureChildControls() voor gebruik in properties.
Wat de Init/LoadViewState betreft; het is maar net wat je wilt. Het voordeel van LVS is dat deze alleen vuurt na een postback (dus niet bij het initieel laden van de pagina) en je daar ook toegang hebt tot eventuele data die je handmatig aan je ViewState hebt toegevoegd, bijvoorbeeld informatie over de betreffende controls die je geinstantieerd wilt zien.
Ik gebruik toch bestaande events, namelijk het InitEvent. En CreateChildControls, icm EnsureChildControls, heb ik een tijdje gebruikt, maar ik vond het nu niet echt heel prettig werken, om eerlijk te zijn.

Als ik me goed herinner, werd CreateChildControls na Init afgevuurd.

Maar wat is feitelijk het verschil tussen:
Visual Basic .NET:
1
Private Sub InitPage(sender as object, e as eventArgs) handles mybase.Init

of
Visual Basic .NET:
1
Protected Overrides Sub CreateChildControls()


Je krijgt alleen gekke zaken indien je inherit van een custom usercontrol; in jouw geval met createchildcontrols zou je dan geen problemen hebben, in mijn geval worden de controls in beide init-events geladen. Alleen of dat echt een groot probleem is, betwijfel ik. (ik ben deze situatie iig nog nooit tegen gekomen)

Dat tweede, dat je toegang hebt tot de manueel toegevoegd data, aan je viewstate, kan idd een voordeel zijn. :) Alleen het nadeel geef je dan zelf al aan; hij fired alleen in het geval van een postback. En dat is een situatie die je denk ik niet altijd wil hebben; het kan best zijn dat er een datagrid dynamisch wordt gebouwd op basis van Role Based Security.
En dan moet te allen tijde een datagrid toegevoegd worden.

[ Voor 30% gewijzigd door gorgi_19 op 05-12-2003 19:37 ]

Digitaal onderwijsmateriaal, leermateriaal voor hbo


  • EfBe
  • Registratie: Januari 2000
  • Niet online
Verwijderd schreef op 05 december 2003 @ 17:48:
Het probleem van in code-behind toegevoegde controls is dat ze geen onderdeel uitmaken van de pagina zoals normaal toegevoegde controls (in de aspx file) dat doen.
Om ze te laten gedragen als normale controls is het zaak om ze te herstellen voor de ViewState hersteld wordt. De meeste events zijn namelijk afhankelijk van ViewState.
De beste plaats om dit te doen is in de LoadViewState page event. Deze vuurt na de PageInit en voor de PageLoad. Voeg op deze plaats je dynamische gegenereerde controls opnieuw toe en ze zullen normaal verschijnen en events triggeren.
Onzin, de dyn. controls worden net zo goed meegenomen met de logic. Hij doorloopt 2x de post data voor de controls, eens in de init, en 1 keer na de page load.

Creator of: LLBLGen Pro | Camera mods for games
Photography portfolio: https://fransbouma.com


Verwijderd

je hebt gelijk als je zegt dat dyn. controls de tweede pass van SaveViewState meemaken, en zodoende hun state bewaard wordt voor gebruik na de postback. Maar het instantieren van de dyn. controls moet wel degelijk handmatig geschieden voor het herstellen van deze informatie, hetzij in Init, hetzij in LoadViewState.

  • EfBe
  • Registratie: Januari 2000
  • Niet online
Oh bedoelde je dat, in dat geval heb je volkomen gelijk.

Creator of: LLBLGen Pro | Camera mods for games
Photography portfolio: https://fransbouma.com

Pagina: 1