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





Я нашел этот маленький самородок в документации в разделе 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
Я делаю то же самое для динамического добавления столбцов в код позади, однако проблема в том, что если я добавляю более 20 столбцов динамически, каждый цикл замедляет работу всей страницы, была ли у вас такая же проблема?