代码之家  ›  专栏  ›  技术社区  ›  Howard Pinsley

向ASP.NET Gridview添加动态列

  •  15
  • Howard Pinsley  · 技术社区  · 16 年前

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

    我在标记中只定义了一列——列0,一个模板列,我在其中声明了一个选择链接和另一个特定于应用程序的链接按钮。那个专栏需要一直在那里。当选择ListBoxSelection时,我删除除第一列以外的所有列,然后重新添加所需的列(在本示例中,我将其简化为始终添加“Title”列)。以下是代码的一部分:

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

    这段代码第一次运行时,我看到了静态列和动态添加的“Title”列。但是,下次进行选择时,第一列将生成为空(其中没有任何内容)。我看到了标题列,我看到了左边的第一列——但里面没有生成任何内容。在调试器中,我可以看到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列向右移动,则第一列渲染良好,而(现在是第二列)TemplateField列消失。

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

    9 回复  |  直到 13 年前
        1
  •  5
  •   DiningPhilanderer    16 年前

    我最近克服了GridView中动态列的类似问题,也许这会有所帮助。

    首先关闭viewstate

    最后,我使用以下帮助器类在RowDataBound事件启动时使复选框实例化。是的,有些是硬编码的。

    见鬼,这是所有的代码。试试看:)沃伦蒂就这样,胡说八道。。。

    最后,由于我刚刚在互联网上获得成功,任何提示都将不胜感激。是的,从网上某处“借用”了最初的代码,对不起,我记不得了:(

    --在受保护的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;
            }
        }
    

    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
  •  3
  •   Harry Sarshogh    12 年前

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

        3
  •  1
  •   Marcus King    16 年前

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

        4
  •  1
  •   Heinzi    14 年前

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

    看起来像是一个微软不打算修复的bug,所以你必须尝试上面的一个解决方案。我也遇到了同样的问题——我有一些DataBoundFields和一些TemplateFields,在回发之后,基于TemplateField的列将失去它们的控件和数据。

        6
  •  1
  •   helper    13 年前
        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
  •  0
  •   Ken Pespisa    16 年前

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

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

        8
  •  0
  •   gfrizzle    16 年前

    您是否可以在开始时定义列,并根据需要隐藏/显示它们(使用Visible=“false”或将控件/页眉/页脚的CssClass设置为使用“display:none;”的类,而不是动态添加列?我在一些代码中使用此方法,包括模板列,没有任何问题。

        9
  •  0
  •   Ken Pespisa    16 年前

    对不起,戴克。我显然遗漏了几个关键点:)

    如果这对您来说仍然是一个问题,我想知道这是否会对您在项目模板中的内容产生影响?如果你只是把一些文本放在那里,然后刷新页面几次,文本是在第一次加载时出现的,而不是在第二次加载时出现的吗?