Ajouter des colonnes dynamiques à un ASP.NET Gridview
j'ai un problème pour ajouter dynamiquement des colonnes à un GridView. Je dois modifier la mise en page -- c'est-à-dire les colonnes incluses -- en fonction de la valeur dans une DropDownList. Lorsque l'utilisateur change la sélection dans cette liste, je dois supprimer toutes sauf la première colonne et ajouter dynamiquement des colonnes supplémentaires basées sur la sélection.
Je n'ai qu'une colonne définie dans mon markup -- colonne 0, une colonne modèle, dans laquelle je déclare un lien Select et un autre lien spécifique à l'application LinkButton. Cette colonne doit toujours être là. Lorsque la sélection ListBox est faite, je supprime toutes les colonnes sauf la première et je rajoute ensuite les colonnes désirées (dans cet exemple, je l'ai simplifié pour toujours ajouter une colonne "Titre"). Voici une partie du code:
RemoveVariableColumnsFromGrid();
BoundField b = new BoundField();
b.DataField = "Title";
this.gvPrimaryListView.Columns.Add(b);
this.gvPrimaryListView.DataBind();
private void RemoveVariableColumnsFromGrid() {
int ColCount = this.gvPrimaryListView.Columns.Count;
//Leave column 0 -- our select and view template column
while (ColCount > 1) {
this.gvPrimaryListView.Columns.RemoveAt(ColCount - 1);
--ColCount;
}
}
la première fois que ce code passe, je vois à la fois la colonne statique et la colonne dynamiquement ajoutée "titre". Cependant, la prochaine fois qu'une sélection est faite, la première colonne est généré vide (rien). Je vois le titre la colonne, et je vois la première colonne, à gauche -- mais il n'y a rien généré à l'intérieur. Dans le débogueur, je peux voir que gvPrimaryListView a en effet encore deux colonnes et la première (index 0) est en effet une colonne Modèle. En fait, la colonne conserve même sa largeur qui est fixée à 165px dans le markup ci-dessous (pour les besoins du débogage).
des idées?
<asp:GridView ID="gvPrimaryListView" runat="server" Width="100%" AutoGenerateColumns="false"
DataKeyNames="Document_ID" EnableViewState="true" DataSourceID="odsPrimaryDataSource"
AllowPaging="true" AllowSorting="true" PageSize="10" OnPageIndexChanging="activeListView_PageIndexChanging"
AutoGenerateSelectButton="False" OnSelectedIndexChanged="activeListView_SelectedIndexChanged"
Visible="true" OnRowDataBound="CtlDocList_RowDataBound" Font-Size="8pt" Font-Names="Helvetica">
<Columns>
<asp:TemplateField ShowHeader="false">
<ItemTemplate>
<asp:LinkButton EnableTheming="false" ID="CtlSelectDocRowBtn" runat="server" Text="Select"
CommandName="Select" CssClass="gridbutton" OnClick="RowSelectBtn_Click" />
<asp:ImageButton EnableTheming="false" ID="DocViewBtn" runat="server" ImageUrl="../../images/ViewDoc3.png"
CssClass="gridbutton" CommandName="Select" OnClick="DocViewBtn_Click" />
</ItemTemplate>
<ItemStyle Width="165px" />
</asp:TemplateField>
</Columns>
<EmptyDataTemplate>
<asp:Label ID="Label6" runat="server" Text="No rows found." SkinID="LabelHeader"></asp:Label>
</EmptyDataTemplate>
</asp:GridView>
Juste quelques informations supplémentaires.
Il n'a rien à voir avec le fait que c'est la première colonne, mais tout à voir avec le fait que c'est un TemplateField. Si je mets une colonne normale à gauche (dans le markup) et que je déplace la colonne TemplateField à droite, la première colonne rend fine, et la (maintenant deuxième) colonne TemplateField disparaît.
autre chose étrange -- le problème n'arrive pas au premier postback -- ou au SECOND -- mais il commence au troisième postback et continue ensuite pour les postbacks suivants. Je suis perplexe.
9 réponses
j'ai récemment conquis les questions de silmilar avec des colonnes dynamiques dans gridviews, peut-être que cela aidera.
premier tour de la viewstate off
Deuxième ajoutez les colonnes par programmation dans une fonction tiré dans le oninit événement
Enfin, j'ai utilisé la classe helper suivante pour provoquer l'instanciation des cases à cocher lors du démarrage de L'événement RowDataBound. Oui, certains de il est codé en dur.
Diable ici, c'est tout le code. Avoir à elle :) Garantie que est, bla blah blah...
enfin puisque je suis juste obtenir mes pieds humide DotNet tous les conseils seraient appréciés [C'est-à-dire ne me déchirez pas trop :) ]. Et oui 'emprunté' le code initial du web quelque part, désolé Je ne peux pas me rappeler du haut de ma tête: (
-- Feu ce en protected override void OnInit
private void GridViewProject_AddColumns()
{
DataSet dsDataSet = new DataSet();
TemplateField templateField = null;
try
{
StoredProcedure sp = new StoredProcedure("ExpenseReportItemType_GetList", "INTRANETWEBDB", Context.User.Identity.Name);
dsDataSet = sp.GetDataSet();
if (sp.RC != 0 && sp.RC != 3000)
{
labelMessage.Text = sp.ErrorMessage;
}
int iIndex = 0;
int iCount = dsDataSet.Tables[0].Rows.Count;
string strCategoryID = "";
string strCategoryName = "";
iStaticColumnCount = GridViewProject.Columns.Count;
// Insert all columns immediatly to the left of the LAST column
while (iIndex < iCount)
{
strCategoryName = dsDataSet.Tables[0].Rows[iIndex]["CategoryName"].ToString();
strCategoryID = dsDataSet.Tables[0].Rows[iIndex]["CategoryID"].ToString();
templateField = new TemplateField();
templateField.HeaderTemplate = new GridViewTemplateExternal(DataControlRowType.Header, strCategoryName, strCategoryID);
templateField.ItemTemplate = new GridViewTemplateExternal(DataControlRowType.DataRow, strCategoryName, strCategoryID);
templateField.FooterTemplate = new GridViewTemplateExternal(DataControlRowType.Footer, strCategoryName, strCategoryID);
// Have to decriment iStaticColumnCount to insert dynamic columns BEFORE the edit row
GridViewProject.Columns.Insert((iIndex + (iStaticColumnCount-1)), templateField);
iIndex++;
}
iFinalColumnCount = GridViewProject.Columns.Count;
iERPEditColumnIndex = (iFinalColumnCount - 1); // iIndex is zero based, Count is not
}
catch (Exception exception)
{
labelMessage.Text = exception.Message;
}
}
-- Helper Class
public class GridViewTemplateExternal : System.Web.UI.ITemplate
{
#region Fields
public DataControlRowType DataRowType;
private string strCategoryID;
private string strColumnName;
#endregion
#region Constructor
public GridViewTemplateExternal(DataControlRowType type, string ColumnName, string CategoryID)
{
DataRowType = type; // Header, DataRow,
strColumnName = ColumnName; // Header name
strCategoryID = CategoryID;
}
#endregion
#region Methods
public void InstantiateIn(System.Web.UI.Control container)
{
switch (DataRowType)
{
case DataControlRowType.Header:
// build the header for this column
Label labelHeader = new Label();
labelHeader.Text = "<b>" + strColumnName + "</b>";
// All CheckBoxes "Look Up" to the header row for this information
labelHeader.Attributes["ERICategoryID"] = strCategoryID;
labelHeader.Style["writing-mode"] = "tb-rl";
labelHeader.Style["filter"] = "flipv fliph";
container.Controls.Add(labelHeader);
break;
case DataControlRowType.DataRow:
CheckBox checkboxAllowedRow = new CheckBox();
checkboxAllowedRow.Enabled = false;
checkboxAllowedRow.DataBinding += new EventHandler(this.CheckBox_DataBinding);
container.Controls.Add(checkboxAllowedRow);
break;
case DataControlRowType.Footer:
// No data handling for the footer addition row
CheckBox checkboxAllowedFooter = new CheckBox();
container.Controls.Add(checkboxAllowedFooter);
break;
default:
break;
}
}
public void CheckBox_DataBinding(Object sender, EventArgs e)
{
CheckBox checkboxAllowed = (CheckBox)sender;// get the control that raised this event
GridViewRow row = (GridViewRow)checkboxAllowed.NamingContainer;// get the containing row
string RawValue = DataBinder.Eval(row.DataItem, strColumnName).ToString();
if (RawValue.ToUpper() == "TRUE")
{
checkboxAllowed.Checked = true;
}
else
{
checkboxAllowed.Checked = false;
}
}
#endregion
}
meilleure solution pour ajouter une colonne dynamique à la vue de grille (ASP) placée sur le projet de code par adresse ci-dessous : s'il vous plaît vérifier : http://www.codeproject.com/Articles/13461/how-to-create-columns-dynamically-in-a-grid-view
diningphilanderer.myopenid.com a une approche similaire à ce que je recommanderais.
le problème est que vous devez relancer la grille chaque fois qu'un postback se produit et par conséquent vous devez reconstruire les colonnes. J'aime avoir une méthode appelée Bindrid() qui efface d'abord les colonnes GridView1.Colonne.Clear (); puis les ajoute de manière programmatique, puis définit la source de données et la base de données des appels. Assurez-vous que vous avez viewstate désactivé pour le réseau et vous avez autogeneratecolumns = false;
j'ai trouvé cela plus tôt aujourd'hui: TemplateField dans un GridView ne dispose pas de son ViewState restauré lors de la BoundFields sont insérés.
ressemble à un bug que Microsoft ne prévoit pas de corriger, donc vous devrez essayer l'une des solutions ci-dessus. J'ai le même problème -- j'ai quelques DataBoundFields et quelques TemplateFields, et après un postback, les colonnes basées sur TemplateField perdent leurs contrôles et leurs données.
j'ai écrit un court article sur le sujet similaire qui traite de la population dynamique de la colonne GridView basée sur les colonnes sélectionnées par l'utilisateur dans le contrôle CheckBoxList. Espérons que cela aidera à ceux qui cherchent la démonstration simple comment générer dynamiquement des colonnes GridView en fonction de la sélection de l'utilisateur?.
void Page_PreRenderComplete(object sender, EventArgs e)
{
// TemplateField reorder bug: if there is a TemplateField based column (or derived therefrom), GridView may blank out
// the column (plus possibly others) during any postback, if the user has moved it from its original markup position.
// This is probably a viewstate bug, as it happens only if a TemplateField based column has been moved. The workaround is
// to force a databind before each response. See https://connect.microsoft.com/VisualStudio/feedback/details/104994/templatefield-in-a-gridview-doesnt-have-its-viewstate-restored-when-boundfields-are-inserted
//
// This problem is also happening for grid views inside a TabPanel, even if the TemplateField based columns have not
// been moved. Also do a databind in that case.
//
// We also force a databind right after the user has submitted the column chooser dialog.
// (This is because the user could have moved TemplateField based column(s) but ColChooserHasMovedTemplateFields()
// returns false -- ie when the user has moved all TemplateField based columns back to their original positions.
if ((!_DataBindingDone && (ColChooserHasMovedTemplateFields() || _InTabPanel)) || _ColChooserPanelSubmitted || _ColChooserPanelCancelled)
DataBind();
// There is a problem with the GridView in case of custom paging (which is true here) that if we are on the last page,
// and we delete all row(s) of that page, GridView is not aware of the deletion during the subsequent data binding,
// will ask the ODS for the last page of data, and will display a blank. By PreRenderComplete, it will somehow have
// realized that its PageIndex, PageCount, etc. are too big and updated them properly, but this is too late
// as the data binding has already occurred with oudated page variables. So, if we were on the last page just before
// the last data binding (_LastPageIndex == _LastPageCount - 1) and PageIndex was decremented after the data binding,
// we know this scenario has happened and we redo the data binding. See http://scottonwriting.net/sowblog/archive/2006/05/30/163173.aspx
// for a discussion of the problem when the GridView uses the ODS to delete data. The discussion also applies when we
// delete data directly through ClassBuilder objects.
if (_LastPageIndex == _LastPageCount - 1 && PageIndex < _LastPageIndex)
DataBind();
if (EnableColChooser)
{
if (!_IsColChooserApplied)
ApplyColChooser(null, false, false);
else
{
// The purpose of calling ApplyColChooser() here is to order the column headers properly. The GridView
// at this point will have reverted the column headers to their original order regardless of ViewState,
// so we need to apply our own ordering. (This is not true of data cells, so we don't have to apply
// ordering to them, as reflected by the parameters of the call.)
// If we have already processed column reordering upon the column chooser panel being submitted,
// don't repeat the operation.
if (!_ColChooserPanelSubmitted)
ApplyColChooser(null, false, true);
}
}
}
j'ai trouvé cette petite pépite dans la documentation, sous la classe DataControlFieldCollection.
si vous utilisez la commande GridView ou DetailsView, les objets DataControlField qui sont créés automatiquement (par exemple, lorsque la propriété AutoGenerateColumns est true) ne sont pas stockés dans la collection de champs accessibles au public. Vous ne pouvez accéder et manipuler que les objets DataControlField qui ne sont pas générés automatiquement.
je suppose la réponse est de faire toute votre manipulation de colonne dans le code, et alors votre approche devrait fonctionner très bien.
au lieu d'ajouter dynamiquement des colonnes, pourriez-vous les définir dès le début et les masquer/les afficher au besoin (soit avec Visible="false" ou paramétrer la classe CSS du contrôle/en-tête/pied de page à une classe avec "display: none;")? J'utilise cette méthode dans certains de mes codes, y compris les colonnes modèles, sans problèmes.
Désolé, Decker. J'ai évidemment oublié quelques points clés.. :)
Si c'est encore un problème pour vous, je me demande si cela fait une différence que vous avez dans votre modèle d'élément? Si vous mettez juste un peu de texte là - dedans, puis rafraîchissez la page quelques fois, est-ce que le texte apparaît sur le premier chargement, puis pas sur le second?
Aussi, lorsque le problème se pose, est-il des balises html dans les cellules ou sont-ils complètement vide?