【问题标题】:Adding dynamic columns to an ASP.NET Gridview向 ASP.NET Gridview 添加动态列
【发布时间】:2010-09-19 02:16:16
【问题描述】:

我在向 GridView 动态添加列时遇到问题。我需要根据 DropDownList 中的值更改布局——即包含的列。当用户更改此列表中的选择时,我需要删除除第一列之外的所有列,并根据选择动态添加其他列。

我的标记中只定义了一个列——第 0 列,一个模板列,我在其中声明了一个 Select 链接和另一个特定于应用程序的 LinkBut​​ton。该列需要始终存在。当创建 ListBoxSelection 时,我删除除第一列之外的所有列,然后重新添加所需的列(在此示例中,我将其简化为始终添加“标题”列)。以下是部分代码:

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

这段代码第一次运行时,我看到了静态列和动态添加的“标题”列。但是,下次进行选择时,第一列生成为空(其中没有任何内容)。我看到了标题栏,我看到了它左边的第一栏——但里面没有生成任何东西。在调试器中,我可以看到 gvPrimaryListView 确实仍然有两列,第一列(索引 0)确实是一个模板列。事实上,该列甚至保留了它的宽度,在下面的标记中设置为 165px(用于调试目的)。

有什么想法吗?

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

只是一些附加信息。

这与它是第一列这一事实无关,而是与它是一个模板字段这一事实有关。如果我将一个普通列放在左侧(在标记中)并将 TemplateField 列向右移动,第一列渲染得很好,而(现在是第二个)TemplateField 列消失了。

另外一件奇怪的事情——问题不会发生在第一次回发——或第二次——但它从第三次回发开始,然后继续进行后续的回发。我被难住了。

【问题讨论】:

  • 我正在做同样的事情来在后面的代码中动态添加列,但是,问题是如果我动态添加 20+ 列,for each 循环会减慢整个页面的速度,你有没有相同的问题?

标签: asp.net gridview dynamic


【解决方案1】:

我最近解决了网格视图中动态列的类似问题,也许这会有所帮助。

首先关闭视图状态
其次,在 oninit 事件中触发的函数中以编程方式添加列
最后,我使用以下帮助程序类在 RowDataBound 事件开始时使复选框实例化。是的,其中一些是硬编码的。

这里是所有的代码。试试看 :) 按原样保修,等等等等……

最后,由于我刚刚接触 DotNet,任何提示都将不胜感激 [IE 不要太撕我 :) ]。是的,从某处“借用”了网络上的初始代码,对不起,我不记得了:(

-- 在受保护的覆盖无效 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;
        }
    }

-- 辅助类

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
}

【讨论】:

    【解决方案2】:

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

    【讨论】:

      【解决方案3】:

      diningphilanderer.myopenid.com 与我推荐的方法类似。

      问题是每次回发时您都必须重新绑定网格,因此您必须重建列。我喜欢有一个名为 BindGrid() 的方法,它首先清除 Columns GridView1.Columns.Clear();然后以编程方式添加它们,然后设置数据源并调用数据绑定。确保您为网格禁用了视图状态,并且您有 autogeneratecolumns = false;

      【讨论】:

        【解决方案4】:

        我今天早些时候发现了这个:TemplateField in a GridView doesn't have its ViewState restored when BoundFields are inserted

        看起来是 Microsoft 不打算修复的错误,因此您必须尝试上述解决方案之一。我遇到了同样的问题——我有一些 DataBoundFields 和一些 TemplateFields,在回发之后,基于 TemplateField 的列失去了它们的控件和数据。

        【讨论】:

          【解决方案5】:

          我写了一篇关于基于用户在 CheckBoxList 控件中选择的列动态填充 GridView 列的类似主题的短文。希望这对那些寻找简单演示的人有所帮助How to generate GridView columns dynamically based on user selection?

          【讨论】:

            【解决方案6】:
                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);
                        }
                    }
                }
            

            【讨论】:

              【解决方案7】:

              我在文档中的 DataControlFieldCollection 类下找到了这个小块。

              如果您使用的是 GridView 或 DetailsView 控件,则自动创建的 DataControlField 对象(例如,当 AutoGenerateColumns 属性为 true 时)不会存储在可公开访问的字段集合中。您只能访问和操作不是自动生成的 DataControlField 对象。

              我想答案是在代码中完成所有列操作,然后你的方法应该可以正常工作。

              【讨论】:

                【解决方案8】:

                除了动态添加列之外,您是否可以在一开始就定义它们并根据需要隐藏/显示它们(使用 Visible="false" 或将控件/页眉/页脚的 CssClass 设置为具有“显示:无”的类;")?我在一些代码中使用了这种方法,包括模板列,没有问题。

                【讨论】:

                  【解决方案9】:

                  对不起,德克尔。我显然错过了一些关键点.. :)

                  如果这对您来说仍然是个问题,我想知道您在项目模板中的内容是否有所不同?如果您只是在其中放一些文本,然后刷新页面几次,文本是否出现在第一次加载时,而不是在第二次加载时?

                  另外,当问题出现时,单元格中是否有任何 html 标记或者它们完全是空的?

                  【讨论】:

                    猜你喜欢
                    • 2010-10-27
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 2011-09-06
                    • 1970-01-01
                    • 2014-02-20
                    • 1970-01-01
                    • 2012-07-25
                    相关资源
                    最近更新 更多