简体   繁体   English

将动态列添加到ASP.NET Gridview

[英]Adding dynamic columns to an ASP.NET Gridview

I'm having a problem dynamically adding columns to a GridView. 我在向GridView动态添加列时遇到问题。 I need to change the layout -- ie the included columns -- based on the value in a DropDownList. 我需要根据DropDownList中的值更改布局 - 即包含的列。 When the user changes the selection in this list, I need to remove all but the first column and dynamically add additional columns based on the selection. 当用户更改此列表中的选择时,我需要删除除第一列之外的所有列,并根据选择动态添加其他列。

I have only one column defined in my markup -- column 0, a template column, in which I declare a Select link and another application specific LinkButton. 我的标记中只定义了一列 - 第0列,一个模板列,其中我声明了一个Select链接和另一个特定于应用程序的LinkBut​​ton。 That column needs to always be there. 该专栏需要始终存在。 When the ListBoxSelection is made, I remove all but the first column and then re-add the desired columns (in this sample, I've simplified it to always add a "Title" column). 在创建ListBoxSelection时,我删除除第一列之外的所有列,然后重新添加所需的列(在此示例中,我将其简化为始终添加“标题”列)。 Here is a portion of the 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;
    }
}

The first time this code runs through, I see both the static column and the dynamically added "Title" column. 这段代码第一次运行时,我看到静态列和动态添加的“标题”列。 However, the next time a selection is made, the first column is generated empty (nothing in it). 但是,下次进行选择时,第一列将生成为空(其中没有任何内容)。 I see the title column, and I see the first column to its left -- but there's nothing generated within it. 我看到了标题栏,我看到它左边的第一列 - 但是里面没有生成任何内容。 In the debugger, I can see that gvPrimaryListView does indeed still have two columns and the first one (index 0) is indeed a template column. 在调试器中,我可以看到gvPrimaryListView确实仍然有两列,第一列(索引0)确实是一个模板列。 In fact, the column even retains it's width which is set as 165px in the markup below (for debugging purposes). 事实上,该列甚至保留了它的宽度,在下面的标记中设置为165px(用于调试目的)。

Any ideas? 有任何想法吗?

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

Just some additional information. 只是一些额外的信息。

It has nothing to do with the fact that it is the first column but everything to do with the fact that it is a TemplateField. 它与第一列无关,而是与它是一个TemplateField这一事实无关。 If I put a normal column to the left (in the markup) and shift the TemplateField column to the right, the first column renders fine, and the (now second) TemplateField column disappears. 如果我将一个普通列放在左侧(在标记中)并将TemplateField列向右移动,则第一列呈现正常,而(现在第二列)TemplateField列将消失。

Another strange thing -- the problem doesn't happen the first postback -- OR THE SECOND -- but it starts on the third postback and then continues for subsequent postbacks. 另一个奇怪的事情 - 问题不会发生在第一次回发 - 或第二次 - 但它从第三次回发开始,然后继续进行后续回发。 I'm stumped. 我很难过。

I recently conquered silmilar issues with dynamic columns in gridviews, perhaps this will help. 我最近在gridviews中用动态列征服了silmilar问题,也许这会有所帮助。

First turn the viewstate off 首先关闭视图状态
Second add the columns programatically in a function fired in the oninit event 其次,在oninit事件中触发的函数中以编程方式添加列
Lastly I used the following helper class to cause the checkboxes to instantiate when the RowDataBound event kicked off. 最后,我使用以下帮助程序类使复选框在RowDataBound事件启动时实例化。 Yes some of it is hard coded. 是的,其中一些是硬编码的。

Heck here is all the code. 这里是所有代码。 Have at it :) Warrenty as is, blah blah blah... 拥有它:) Warrenty原样,等等等等......

Finally since I am just getting my feet wet DotNet any tips would be appreciated [IE don't rip me too much :) ]. 最后,因为我只是让我的脚湿了DotNet任何提示将不胜感激[IE不要扯我太多:)]。 And yes 'borrowed' the initial code from the web somewhere, sorry I cant remember off the top of my head :( 是的'借用'来自网络的初始代码,对不起,我不记得我的头顶:(

-- Fire this off in protected override void OnInit - 在受保护的覆盖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
}

通过以下地址在代码项目中添加动态列到网格视图(ASP)的最佳解决方案:请查看: http//www.codeproject.com/Articles/13461/how-to-create-columns-dynamically-in -a-网格视图

diningphilanderer.myopenid.com has a similar approach to what I would recommend. diningphilanderer.myopenid.com与我的推荐方法类似。

The problem is that you have to rebind the grid each time a postback occurs and consequently you have to rebuild the columns. 问题是每次发生回发时都必须重新绑定网格,因此必须重建列。 I like to have a method called BindGrid() that first clears the Columns GridView1.Columns.Clear(); 我喜欢有一个名为BindGrid()的方法,它首先清除列GridView1.Columns.Clear(); then adds them programatically, then sets the datasource and calls databind. 然后以编程方式添加它们,然后设置数据源并调用数据绑定。 Make sure you have viewstate disabled for the grid and you have autogeneratecolumns = false; 确保您为网格禁用了viewstate,并且您有autogeneratecolumns = false;

I found this earlier today: TemplateField in a GridView doesn't have its ViewState restored when BoundFields are inserted . 我今天早些时候发现了这一点: 当插入BoundFields时,GridView中的TemplateField没有恢复其ViewState

Looks like a bug that Microsoft doesn't plan on fixing, so you'll have to try one of the solutions above. 看起来像Microsoft不打算修复的错误,因此您必须尝试上述解决方案之一。 I'm having the same problem -- I have some DataBoundFields and some TemplateFields, and after a postback, the TemplateField based columns lose their controls and data. 我遇到了同样的问题 - 我有一些DataBoundFields和一些TemplateFields,并且在回发后,基于TemplateField的列会丢失它们的控件和数据。

I have written a short article on the similar topic that deal with dynamically populating GridView column based on the columns selected by the user in the CheckBoxList control. 我写了一篇关于类似主题的简短文章,该文章基于用户在CheckBoxList控件中选择的列来处理动态填充GridView列。 Hope this will help to those looking for simple demonstration How to generate GridView columns dynamically based on user selection? 希望这对那些寻求简单演示的人有所帮助如何根据用户选择动态生成GridView列? .

    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);
            }
        }
    }

I found this little nugget in the documentation, under the DataControlFieldCollection Class. 我在文档中的DataControlFieldCollection类下找到了这个小块。

If you are using the GridView or DetailsView control, the DataControlField objects that are automatically created (for example, when the AutoGenerateColumns property is true) are not stored in the publicly accessible fields collection. 如果您使用的是GridView或DetailsView控件,则自动创建的DataControlField对象(例如,当AutoGenerateColumns属性为true时)不会存储在可公开访问的字段集合中。 You can only access and manipulate DataControlField objects that are not automatically generated. 您只能访问和操作未自动生成的DataControlField对象。

I guess the answer is to do all of your column manipulation in code, and then your approach should work fine. 我想答案是在代码中进行所有列操作,然后你的方法应该可以正常工作。

Instead of dynamically adding columns, could you define them at the outset and hide/show them as needed (either with Visible="false" or setting the CssClass of the control/header/footer to a class with "display: none;")? 您可以在开始时定义它们,并根据需要隐藏/显示它们(使用Visible =“false”或将控件/页眉/页脚的CssClass设置为带有“display:none;”的类,而不是动态添加列。 ? I use this method in some of my code, including template columns, with no issues. 我在我的一些代码中使用此方法,包括模板列,没有任何问题。

Sorry, Decker. 对不起,德克尔。 I missed a few key points obviously.. :) 我显然错过了几个关键点.. :)

If this is still a problem for you, I wonder if it makes a difference what you have in your item template? 如果这仍然是一个问题,我想知道你的项目模板中有什么不同? If you just put some text in there, then refresh the page a few times, does the text appear on the first load, then not on the second? 如果您只是在其中放入一些文本,那么刷新页面几次,文本是否出现在第一次加载,然后不出现在第二次?

Also, when the problem arises is there any html markup at all in the cells or are they completely empty? 此外,当问题出现时,单元格中是否存在任何html标记,或者它们是否完全为空?

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM