[ASP.Net C# 2.0] Datagrid events vuren niet

Pagina: 1
Acties:

  • Twilight Burn
  • Registratie: Juni 2000
  • Laatst online: 16-02 23:04
Ik ben bezig met het maken van een datagrid, waar ik een soort van woordenboek in kan zetten. Bij een gegeven "stringId" kan ik dan per taal een vertaling opslaan in de database.

Omdat de talen niet vast staan moet ik de kolommen hiervoor dynamisch aanmaken

Het op het scherm zetten van deze datagrid lukt perfect, echter als ik op een van de knoppen klik, gebeurd er helemaal niets, de event handler wordt nooit bereikt, ook niet als ik een Exception forceer bij de ItemCommand eventhandler.

De (belangrijke) code van de pagina:
C#:
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
protected override void OnInit(EventArgs e) {
    base.OnInit(e);
    
    dtgStringTable.ItemCommand += new DataGridCommandEventHandler(dtgStringTable_ButtonClick);
}

protected override void OnPreRender(EventArgs e) {
    base.OnPreRender(e);
    
    //Create datagrid language columns
    for (int i = 0; i < dictionary.Languages.Length; i++) {
        string curLang = dictionary.Languages[i].LanguageId.ToUpper();
        
        TemplateColumn dgColumn = new TemplateColumn();
    
        dgColumn.HeaderTemplate   = new StringTableTemplate(ListItemType.Header,   curLang);
        dgColumn.FooterTemplate   = new StringTableTemplate(ListItemType.Footer,   curLang);
        dgColumn.ItemTemplate     = new StringTableTemplate(ListItemType.Item,     curLang);
        dgColumn.EditItemTemplate = new StringTableTemplate(ListItemType.EditItem, curLang);
        
        dtgStringTable.Columns.AddAt(i + 1, dgColumn);
    }
    FillDataGrid();
}
    
private void FillDataGrid() {
    DataTable dt = new DataTable();
    
    //Create datatable columns
    dt.Columns.Add("stringId", typeof(string));
    foreach (Language curLang in dictionary.Languages) {
        dt.Columns.Add("trans" + curLang.LanguageId.ToUpper(), typeof(string));
    }
    
    //Fill datatable
    foreach (string[] curLine in dictionary.GetStringTable()) {
        dt.Rows.Add(curLine);
    }
    
    dtgStringTable.DataSource = dt;
    dtgStringTable.DataBind();  
}

private void dtgStringTable_ButtonClick(object sender, DataGridCommandEventArgs e) {
    throw new Exception("Test:" + e.CommandName); //Event test!         
    switch (e.CommandName.ToLower()) {
        case "add": {
            
        } break;
            
        case "edit": {
            dtgStringTable.EditItemIndex = e.Item.ItemIndex;
        } break;
            
        case "save": {
            dtgStringTable.EditItemIndex = -1;
        } break;
            
        case "cancel": {
            dtgStringTable.EditItemIndex = -1;
        } break;
            
        case "delete": {
            
        } break;
            
        default:
            throw new Exception(e.CommandName + "-" + e.CommandArgument + "-" + e.CommandSource.ToString());
    }
}

De datagrid code van de pagina:
XML:
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
<asp:DataGrid id="dtgStringTable" runat="server" CssClass="table_layout" GridLines="None" ShowHeader="True" ShowFooter="True" AutoGenerateColumns="False">
    <ItemStyle CssClass="table_row_normal"></ItemStyle>
    <AlternatingItemStyle CssClass="table_row_alternate"></AlternatingItemStyle>
    <HeaderStyle CssClass="table_header"></HeaderStyle>
    <FooterStyle CssClass="table_footer"></FooterStyle>
    <Columns>
        <asp:TemplateColumn runat="server">
            <HeaderTemplate>
                String ID
            </HeaderTemplate>
            <ItemTemplate>
                <asp:label style="float:left;width: 200px;" runat="server" Text='<%# DataBinder.Eval(Container.DataItem, "stringId") %>' />
            </ItemTemplate>
        </asp:TemplateColumn>

        <asp:TemplateColumn>
            <ItemTemplate>
                <asp:ImageButton runat="Server" CssClass="table_imagebutton" ImageUrl="images/edit_edit.png" CommandName="Edit" />
                <asp:ImageButton runat="Server" CssClass="table_imagebutton" ImageUrl="images/edit_delete.png" CommandName="Delete" />
            </ItemTemplate>
            <EditItemTemplate>
                <asp:ImageButton runat="Server" CssClass="table_imagebutton" ImageUrl="images/edit_save.png" CommandName="Save" />
                <asp:ImageButton runat="Server" CssClass="table_imagebutton" ImageUrl="images/edit_cancel.png" CommandName="Cancel" />
            </EditItemTemplate>
            <FooterTemplate>
                <asp:ImageButton runat="Server" CssClass="table_imagebutton" ImageUrl="images/edit_add.png" CommandName="Add"/>
            </FooterTemplate>
        </asp:TemplateColumn>
    </Columns>
</asp:DataGrid>


Als ik de opzet van de datagrid code vergelijk met wat ik al eens heb gemaakt in .Net 1.1, dan zie ik eigenlijk geen verschil - maar dit is de eerste keer dat ik kolommen dynamisch aanmaak.

Verwijderd

Je voegt de kolommen pas in de PreRender toe. Deze kolommen hebben effect op de control hierarchy van de datagrid. Als je naar de life cycle van ASP.NET kijkt zul je zien dat na de Load (en voor PreRender) postback events geraised (dienen te) worden. Kort door de bocht wordt het raisen van events mogelijk gemaakt door het feit dat de Page aan bepaalde controls de mogelijkheid geeft om adhv wat post informatie een event te raisen.

Doordat je de kolommen pas in PreRender toevoegt, zal de gehele control hierarchy (inclusief de controls voor je data - gebaseerd op de ViewState) niet opnieuw opgebouwd worden (of in ieder geval niet op tijd). De control die eigelijk het postback event zou moeten raisen is daarom nog niet geinstantieerd voor PreRender.

Ik denk dat als je de huidige code verplaatst naar OnLoad, en het zetten van de DataSource alleen in het geval van een PostBack (of indien geforceerd ivm een wijziging oid) doet. Dit laatste mag in PreRender gebeuren (en is in sommige situaties aan te bevelen - maar dat terzijde).

Update: zie voor meer informatie http://www.wilcob.com/Wil...callyCreatedControls.aspx

[ Voor 6% gewijzigd door Verwijderd op 12-12-2005 22:07 ]


  • Not Pingu
  • Registratie: November 2001
  • Laatst online: 01-04 20:36

Not Pingu

Dumbass ex machina

C#:
1
2
3
4
5
6
7
8
protected override void OnPreRender(EventArgs e) {
    base.OnPreRender(e);
    
    if(!IsPostBack}
    {
        //Create datagrid language columns 
    }
}


Je maakt de templates nl. steeds opnieuw aan, waardoor de controls daarbinnen vernietigd en weer opgebouwd worden tijdens de postback. De oude control die het event afvuurde bestaat nl. niet meer en dus zal de event handler niet meer uitgevoerd worden.

[edit] never mind, oplossing al gevonden zie ik :)

[ Voor 52% gewijzigd door Not Pingu op 12-12-2005 22:36 ]

Certified smart block developer op de agile darkchain stack. PM voor info.


  • Twilight Burn
  • Registratie: Juni 2000
  • Laatst online: 16-02 23:04
Na het verplaatsen van de code naar OnLoad werkt het idd. Bedankt!

Verwijderd

In de OnPreRender mage je geen nieuwe objecten meer toevoegen aan de pagina, etc.

Verwijderd

Verwijderd schreef op dinsdag 13 december 2005 @ 00:44:
In de OnPreRender mage je geen nieuwe objecten meer toevoegen aan de pagina, etc.
Dat mag op zich gewoon, mits ze bij de volgende request maar op tijd opnieuw toegevoegd worden. Dit zal dan vaak mbv ViewState geregeld kunnen worden.

Wanneer je een control als een DataGrid of Repeater databind, zullen er in principe altijd controls toegevoegd worden. Dit mag gewoon in PreRender gedaan worden, omdat deze controls op basis van ViewState (mits ViewState is enabled...) de controls opnieuw kunnen toevoegen (en op tijd).

Het kan nuttig zijn om in PreRender te binden indien er een bepaalde vlag gezet is. Een dergelijke flag zou dan gezet worden indien het om geen postback gaat, of als er een mutatie van data heeft plaatsgevonden, etc. Door in PreRender te re-binden ipv. bvb een eventhandler voorkom je dat je vaker re-bind, en kan re-binden op basis van alle informatie die je hebt.

  • Twilight Burn
  • Registratie: Juni 2000
  • Laatst online: 16-02 23:04
Het aanmaken van de template controls werkt nu, alleen nu ben ik bezig met de mogelijkheid om de strings te wijzigen. Als ik op de edit knop klik, krijg ik mooi een tekstbox voor elke taal te zien, echter als ik dan op de save/cancel knop druk krijg ik de volgende foutmelding:
Invalid postback or callback argument. Event validation is enabled using <pages enableEventValidation="true"/> in configuration or <%@ Page EnableEventValidation="true" %> in a page. For security purposes, this feature verifies that arguments to postback or callback events originate from the server control that originally rendered them. If the data is valid and expected, use the ClientScriptManager.RegisterForEventValidation method in order to register the postback or callback data for validation.
De fout treed alleen op als ik het "ID" property zet van de tekstbox (zie code hieronder) maar die ID code heb ik straks nodig om de tekstbox weer uit te lezen als ik moet saven.

De code is nog hetzelfde als in de startpost, alleen de "PreRender" code staat nu in de "OnLoad" (de code zelf is ongewijzigd) en aan het einde van de buttonClick event roep ik FillDataGrid() nog een keer aan.

De code die ik voor de template gebruik:
C#:
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
private class StringTableTemplate : ITemplate {
    private ListItemType type;
    private string language;
    
    public StringTableTemplate(ListItemType type, string language) {
        this.language = language;
        this.type = type;
    }
    
    public void InstantiateIn(Control container) {
        TableCell curCell = (TableCell)container;
        switch (type) {
            case ListItemType.Header: {
                curCell.Text = language;
            } break;
                
            case ListItemType.Footer: {
                TextBox txt = new TextBox();
                txt.ID = "txtTrans" + language;
                txt.CssClass = "input_text_112";
                container.Controls.Add(txt);
            } break;
        
            case ListItemType.Item: {
                Label lit = new Label();
                lit.Style.Add("float", "left");
                lit.Style.Add("width", "112px");
                container.Controls.Add(lit);
                lit.DataBinding += new EventHandler(OnDataBind);
            } break;
                
            case ListItemType.EditItem: {
                TextBox txt = new TextBox();
                txt.ID = "txtTrans" + language;
                txt.CssClass = "input_text_112";
                container.Controls.Add(txt);
                txt.DataBinding += new EventHandler(OnDataBind);
            } break;
        }
    }
    
    private void OnDataBind(object sender, EventArgs e) {
        switch (type) {
            case ListItemType.Item: {
                Label lit = (Label)sender;
                DataGridItem di = (DataGridItem)lit.NamingContainer;
                lit.Text = Convert.ToString(((DataRowView)di.DataItem).Row["trans" + language]);
            } break;
                
            case ListItemType.EditItem: {
                TextBox txt = (TextBox)sender;
                DataGridItem di = (DataGridItem)txt.NamingContainer;
                txt.Text = Convert.ToString(((DataRowView)di.DataItem).Row["trans" + language]);
            } break;
        }
    }
}

  • Twilight Burn
  • Registratie: Juni 2000
  • Laatst online: 16-02 23:04
(schop)

Ik heb nog even zitten spelen met het geval waar ik geen .ID property zet. Als ik dan de textbox probeer uit te lezen, staat er de tekst in die ik er zelf in zet via de datatable (en dus niet wat ik er in heb getikt in de browser)

Ik heb het vermoeden dat de extra kolommen/data niet (correct) in de viewstate gezet worden (of er weer uitgehaald) en dat daarom de gegevens vergeten worden bij een post-back.
Pagina: 1