Добавление динамических столбцов в таблицу ASP.NET

У меня проблема с динамическим добавлением столбцов в GridView. Мне нужно изменить макет, то есть включенные столбцы, в зависимости от значения в DropDownList. Когда пользователь изменяет выбор в этом списке, мне нужно удалить все столбцы, кроме первого, и динамически добавлять дополнительные столбцы в зависимости от выбора.

У меня есть только один столбец, определенный в моей разметке - столбец 0, столбец шаблона, в котором я объявляю ссылку Select и еще одно приложение LinkButton. Этот столбец должен всегда быть рядом. После создания 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) действительно является столбцом шаблона. Фактически, столбец даже сохраняет свою ширину, которая установлена ​​как 165 пикселей в разметке ниже (для целей отладки).

Есть идеи?

<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 исчезает.

Еще одна странная вещь - проблема не возникает при первой обратной передаче - ИЛИ ВТОРОЙ - но она начинается при третьей обратной передаче и затем продолжается для последующих обратных передач. Я в тупике.

Я делаю то же самое для динамического добавления столбцов в код позади, однако проблема в том, что если я добавляю более 20 столбцов динамически, каждый цикл замедляет работу всей страницы, была ли у вас такая же проблема?

Princa 25.11.2013 18:33
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
15
1
64 958
9

Ответы 9

Я нашел этот маленький самородок в документации в разделе DataControlFieldCollection Class.

Если вы используете элемент управления GridView или DetailsView, автоматически созданные объекты DataControlField (например, когда свойство AutoGenerateColumns имеет значение true) не сохраняются в коллекции общедоступных полей. Вы можете получать доступ и управлять только объектами DataControlField, которые не создаются автоматически.

Думаю, ответ - выполнять все манипуляции с столбцами в коде, и тогда ваш подход должен работать нормально.

Вместо динамического добавления столбцов, не могли бы вы определить их с самого начала и скрыть / показать их по мере необходимости (либо с помощью Visible = "false", либо установив CssClass элемента управления / верхнего / нижнего колонтитула в класс с "display: none;") ? Я использую этот метод в некоторых частях своего кода, включая столбцы шаблонов, без проблем.

Извини, Декер. Очевидно, я упустил несколько ключевых моментов .. :)

Если это все еще проблема для вас, интересно, имеет ли значение то, что у вас есть в вашем шаблоне предмета? Если вы просто поместите туда какой-то текст, а затем обновите страницу несколько раз, появится ли текст при первой загрузке, а затем не при второй?

Кроме того, когда возникает проблема, есть ли вообще какая-либо разметка html в ячейках или они полностью пусты?

Недавно я преодолел подобные проблемы с динамическими столбцами в представлениях сетки, возможно, это поможет.

Сначала выключите состояние просмотра
Во-вторых, добавьте столбцы программно в функцию, запущенную в событии oninit Наконец, я использовал следующий вспомогательный класс, чтобы установить флажки при запуске события RowDataBound. Да, некоторые из них жестко запрограммированы.

Черт возьми, вот и весь код. Получите это :) Warrenty as is, бла-бла-бла ...

Наконец, так как я только начинаю промочить DotNet, любые советы будут оценены [IE не рвет меня слишком сильно :)]. И да, «позаимствовал» исходный код где-то в сети, извините, я не могу припомнить :(

- Отключить это в защищенном переопределении 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
}

Diningphilanderer.myopenid.com использует подход, аналогичный тому, который я бы порекомендовал.

Проблема в том, что вам нужно повторно привязать сетку каждый раз, когда происходит обратная передача, и, следовательно, вам нужно перестраивать столбцы. Мне нравится использовать метод BindGrid (), который сначала очищает столбцы GridView1.Columns.Clear (); затем добавляет их программно, затем устанавливает источник данных и вызывает привязку данных. Убедитесь, что у вас отключено состояние просмотра для сетки и у вас есть autogeneratecolumns = false;

Я нашел это сегодня ранее: TemplateField в GridView не восстанавливает ViewState при вставке BoundField.

Похоже на ошибку, которую Microsoft не планирует исправлять, поэтому вам придется попробовать одно из вышеперечисленных решений. У меня та же проблема - у меня есть несколько DataBoundFields и несколько TemplateFields, и после обратной передачи столбцы на основе TemplateField теряют свои элементы управления и данные.

Я написал небольшую статью по аналогичной теме, которая касается динамического заполнения столбца GridView на основе столбцов, выбранных пользователем в элементе управления CheckBoxList. Надеюсь, это поможет тем, кто ищет простую демонстрацию Как динамически генерировать столбцы 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);
            }
        }
    }

лучшее решение для добавления динамического столбца в представление сетки (ASP), размещенное в проекте кода по адресу ниже: Пожалуйста, проверьте это : http://www.codeproject.com/Articles/13461/how-to-create-columns-dynamically-in-a-grid-view

Другие вопросы по теме