15

我在将列动态添加到 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 列向右移动,则第一列呈现良好,并且(现在是第二个)TemplateField 列消失。

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

4

9 回答 9

5

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

首先关闭视图状态
其次以编程方式在 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
}
于 2008-10-30T16:18:37.460 回答
3

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

于 2013-02-01T08:28:47.493 回答
1

我写了一篇关于基于用户在 CheckBoxList 控件中选择的列动态填充 GridView 列的类似主题的短文。希望这对那些寻找简单演示的人有所帮助如何根据用户选择动态生成 GridView 列?.

于 2011-06-12T02:50:12.533 回答
1

我今天早些时候发现了这一点:GridView 中的 TemplateField 在插入 BoundFields 时没有恢复其 ViewState

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

于 2008-12-10T19:09:54.280 回答
1

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

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

于 2008-10-30T16:26:58.007 回答
1
    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);
            }
        }
    }
于 2012-01-25T15:50:45.570 回答
0

I found this little nugget in the documentation, under the DataControlFieldCollection Class.

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. You can only access and manipulate DataControlField objects that are not automatically generated.

I guess the answer is to do all of your column manipulation in code, and then your approach should work fine.

于 2008-10-24T21:08:08.420 回答
0

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

于 2008-10-29T15:26:13.947 回答
0

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

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

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

于 2008-10-30T15:52:11.680 回答