|
Thanks, I'll take a look at the version you mention.
doing it in event handler is too late
If in Page_Load I just do:
<br />
If IsPostback Then<br />
grid.AllowInsert = True<br />
End If<br />
... and I provoke a Postback by clicking a Sort header, it works. It doesn't work if the Postback is caused by clicking a Button control though. I'm not yet sure why this is.
|
|
|
|
|
Hi all,
My code:
<edititemtemplate>
<asp:textbox id="DateTextBox" width="70px" runat="server" text="<%# Bind(" date")="" %>"="" onload="DateTextBox_Load" xmlns:asp="#unknown">
<asp:image id="Image1" runat="server" imageurl="Images/Calendar_scheduleHS.png" xmlns:asp="#unknown">
<cc1:calendarextender id="CalendarExtender1" format="dd.MM.yyyy" runat="server" targetcontrolid="DateTextBox" popupbuttonid="Image1" xmlns:cc1="#unknown">
<asp:requiredfieldvalidator id="DateRequiredFieldValidator" runat="server" controltovalidate="DateTextBox" errormessage="Date is required!" xmlns:asp="#unknown">
couse System.InvalidOperationException "Extender controls may not be registered before PreRender."
StackTrace:
at System.Web.UI.ScriptControlManager.RegisterExtenderControl[TExtenderControl](TExtenderControl extenderControl, Control targetControl)
at System.Web.UI.ScriptManager.RegisterExtenderControl[TExtenderControl](TExtenderControl extenderControl, Control targetControl)
at System.Web.UI.ExtenderControl.RegisterWithScriptManager()
at System.Web.UI.ExtenderControl.OnPreRender(EventArgs e)
at AjaxControlToolkit.ExtenderControlBase.OnPreRender(EventArgs e) in D:\tools\ASP_Ajax\AjaxControlToolkit\ExtenderBase\ExtenderControlBase.cs:line 367
at System.Web.UI.Control.PreRenderRecursiveInternal()
at System.Web.UI.Control.PreRenderRecursiveInternal()
at System.Web.UI.Control.PreRenderRecursiveInternal()
at System.Web.UI.Control.PreRenderRecursiveInternal()
at System.Web.UI.Control.PreRenderRecursiveInternal()
at System.Web.UI.Control.PreRenderRecursiveInternal()
at System.Web.UI.Control.PreRenderRecursiveInternal()
at System.Web.UI.Control.PreRenderRecursiveInternal()
at System.Web.UI.Control.PreRenderRecursiveInternal()
at System.Web.UI.Control.PreRenderRecursiveInternal()
at System.Web.UI.Control.PreRenderRecursiveInternal()
at System.Web.UI.Page.ProcessRequestMain(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint)
Can someone help me?
|
|
|
|
|
I have converted the C# to VB for anyone interested... Enjoy.
Imports System
Imports System.Collections.Generic
Imports System.ComponentModel
Imports System.Text
Imports System.Web
Imports System.Web.UI
Imports System.Web.UI.WebControls
Imports System.Drawing.Design
Imports System.Collections.Specialized
Imports System.Data
Namespace InsGrid
<DefaultEvent("SelectedIndexChanged")> _
<SupportsEventValidation()> _
<Designer("System.Web.UI.Design.WebControls.GridViewDesigner, System.Design, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a")> _
<ControlValueProperty("SelectedValue")> _
<ToolboxData("<{0}:InsertableGrid runat=server></{0}:InsertableGrid>")> _
Public Class InsertableGrid
Inherits GridView
<Category("Action")> _
Public Event RowInserted(ByVal sender As Object, ByVal e As GridViewUpdatedEventArgs)
<Category("Action")> _
Public Event RowInserting(ByVal sender As Object, ByVal e As GridViewUpdateEventArgs)
<Browsable(True), DefaultValue("true"), Category("Behavior")> _
Public Property AllowInsert() As Boolean
Get
Return CBool(IIf(ViewState("_InsOk") Is Nothing, True, CBool(ViewState("_InsOk"))))
End Get
Set(ByVal Value As Boolean)
ViewState("_InsOk") = Value
End Set
End Property
<Browsable(True), DefaultValue("false"), Category("Behavior")> _
Public Property SuppressReadOnlyColumns() As Boolean
Get
Return CBool(IIf(ViewState("_SupRO") Is Nothing, False, CBool(ViewState("_SupRO"))))
End Get
Set(ByVal Value As Boolean)
ViewState("_SupRO") = Value
End Set
End Property
Private _InsertValues As IOrderedDictionary = Nothing
Protected Property InsertValues() As IOrderedDictionary
Get
Return _InsertValues
End Get
Set(ByVal Value As IOrderedDictionary)
_InsertValues = Value
End Set
End Property
Protected gvFooterRow As GridViewRow = Nothing
Public Overrides ReadOnly Property FooterRow() As GridViewRow
Get
Return CType(IIf(gvFooterRow Is Nothing, MyBase.FooterRow, gvFooterRow), GridViewRow)
End Get
End Property
<Browsable(True)> _
Public Overrides ReadOnly Property Columns() As DataControlFieldCollection
Get
Return MyBase.Columns
End Get
End Property
Protected Overrides Function CreateChildControls(ByVal dataSource As System.Collections.IEnumerable, ByVal dataBinding As Boolean) As Integer
Dim ret As Integer = MyBase.CreateChildControls(dataSource, dataBinding)
If Columns.Count = 0 Or DesignMode Or Not AllowInsert Then
Return ret
End If
ShowFooter = True
Dim flds() As DataControlField = New DataControlField(Columns.Count - 1) {}
Columns.CopyTo(flds, 0)
If ret = 0 Then
Controls.Clear()
Dim t As Table = New Table()
Controls.Add(t)
Dim r As GridViewRow = CreateRow(-1, -1, DataControlRowType.Header, DataControlRowState.Normal)
Me.InitializeRow(r, flds)
t.Rows.Add(r)
gvFooterRow = CreateRow(-1, -1, DataControlRowType.Footer, DataControlRowState.Insert)
Me.InitializeRow(gvFooterRow, flds)
t.Rows.Add(gvFooterRow)
Else
gvFooterRow = MyBase.FooterRow
End If
FooterRow.RowState = DataControlRowState.Insert
FooterRow.Visible = (MyBase.EditIndex < 0)
For i As Integer = 0 To Columns.Count - 1
Dim cell As DataControlFieldCell = CType(FooterRow.Cells(i), DataControlFieldCell)
Dim fld As DataControlField = Columns(i)
Dim NotReadOnly As Boolean = True
If (Me.SuppressReadOnlyColumns) Then
If (Columns(i).GetType().Name.EndsWith("BoundField")) Then
NotReadOnly = Not (CType(Columns(i), BoundField)).ReadOnly
ElseIf (Columns(i).GetType().Name.EndsWith("CheckBoxField")) Then
NotReadOnly = Not (CType(Columns(i), CheckBoxField)).ReadOnly
ElseIf (Columns(i).GetType().Name.EndsWith("ImageField")) Then
NotReadOnly = Not (CType(Columns(i), ImageField)).ReadOnly
End If
End If
fld.InsertVisible = (fld.Visible And NotReadOnly)
If TypeOf fld Is CommandField Then
Dim cf As CommandField = CType(fld, CommandField)
Dim ins As CommandField = New CommandField()
ins.ButtonType = cf.ButtonType
ins.InsertImageUrl = cf.InsertImageUrl
ins.InsertText = cf.InsertText
ins.CancelImageUrl = cf.CancelImageUrl
ins.CancelText = cf.CancelText
ins.ValidationGroup = cf.ValidationGroup
ins.CausesValidation = cf.CausesValidation
ins.InsertVisible = True
ins.ShowInsertButton = True
ins.Initialize(False, Me)
ins.InitializeCell(cell, DataControlCellType.DataCell, DataControlRowState.Insert, -1)
ElseIf cell.Controls.Count = 0 Then
fld.Initialize(MyBase.AllowSorting, Me)
If NotReadOnly Then
fld.InitializeCell( _
cell, _
DataControlCellType.DataCell, _
CType(DataControlRowState.Edit + DataControlRowState.Insert, DataControlRowState), _
-1)
End If
End If
Next
Dim dict As OrderedDictionary = New OrderedDictionary()
MyBase.ExtractRowValues(dict, FooterRow, True, True)
Dim tbl As DataTable = New DataTable()
Dim row As DataRow = tbl.Rows.Add()
For Each k As String In dict.Keys
tbl.Columns.Add(New DataColumn(k))
row(k) = dict(k)
Next
FooterRow.DataItem = New DataView(tbl).Item(0)
Dim args1 As GridViewRowEventArgs = New GridViewRowEventArgs(FooterRow)
Me.OnRowCreated(args1)
FooterRow.DataBind()
Me.OnRowDataBound(args1)
FooterRow.DataItem = Nothing
For Each f As DataControlField In Columns
f.Initialize(Me.AllowSorting, Me)
Next
Return ret
End Function
Protected Overridable Sub OnRowInserting(ByVal e As GridViewUpdateEventArgs)
RaiseEvent RowInserting(Me, e)
End Sub
Protected Overridable Sub OnRowInserted(ByVal e As GridViewUpdatedEventArgs)
RaiseEvent RowInserted(Me, e)
End Sub
Protected Overrides Sub OnRowCommand(ByVal e As GridViewCommandEventArgs)
If "-1".Equals(e.CommandArgument) And "Cancel".CompareTo(e.CommandName) = 0 Then
Page.Trace.Warn("Canceling insert...")
Return
End If
If "-1".Equals(e.CommandArgument) And "Insert".Equals(e.CommandName) Then
Dim args As GridViewUpdateEventArgs = New GridViewUpdateEventArgs(-1)
MyBase.ExtractRowValues(args.NewValues, FooterRow, True, True)
InsertValues = args.NewValues
Dim allEmpty As Boolean = True
For Each v As Object In InsertValues.Values
If v IsNot Nothing AndAlso System.Convert.ToString(v).Trim().Length > 0 Then
allEmpty = False
Exit For
End If
Next
If allEmpty Then
Return
End If
Me.OnRowInserting(args)
If args.Cancel Then
Return
End If
Dim view As DataSourceView = Me.GetData()
view.Insert(args.NewValues, New System.Web.UI.DataSourceViewOperationCallback(AddressOf CallBack))
Return
End If
MyBase.OnRowCommand(e)
End Sub
Private Function CallBack(ByVal affectedRows As Integer, ByVal ex As Exception) As Boolean
Dim evt As GridViewUpdatedEventArgs = New GridViewUpdatedEventArgs(affectedRows, ex)
evt.NewValues.Clear()
For Each v As String In InsertValues.Keys
evt.NewValues.Add(v, InsertValues(v))
Next
Me.OnRowInserted(evt)
If ex Is Nothing And Not evt.ExceptionHandled Then
Return False
End If
MyBase.RequiresDataBinding = True
Return True
End Function
End Class
End Namespace
modified on Tuesday, February 3, 2009 9:50 AM
|
|
|
|
|
Man, awesome job!
I found just one problem: the code does not take AutoGenerated buttons under consideration - they cause off-by-one error and the grid does not render the way it should. Here is my solution (tested). Modify InsertableGrid.cs file as follows:
lines 60-62
was:
DataControlField[] flds = new DataControlField[Columns.Count];
Columns.CopyTo(flds, 0);
if (ret == 0) {
correction:
if (ret == 0) {
bool hasAutoCommandField = base.AutoGenerateDeleteButton || base.AutoGenerateEditButton || base.AutoGenerateSelectButton;
int autoCommandFieldOffset = hasAutoCommandField ? 1 : 0;
DataControlField[] flds = new DataControlField[Columns.Count + autoCommandFieldOffset];
if (hasAutoCommandField) flds[0] = new CommandField();
Columns.CopyTo(flds, autoCommandFieldOffset);
lines 79-81:
was:
for (int i = 0; i < Columns.Count; i++) {
DataControlFieldCell cell = (DataControlFieldCell)FooterRow.Cells[i];
DataControlField fld = Columns[i];
correction:
for (int i = 0; i < FooterRow.Cells.Count; i++) {
DataControlFieldCell cell = (DataControlFieldCell)FooterRow.Cells[i];
DataControlField fld = cell.ContainingField;
Cheers,
Thomas Jastrzebski
|
|
|
|
|
Very nice solution!
Question:
If inserting to DB caused an exception, it can be handled in InsertableGrid1_RowInserted.
But even if (GridViewUpdatedEventArgs)eventArgs.KeepInEditMode is set to true, the old values that caused the error are cleared.
This is annoying if a lot of data has been entered before Insert and the action fails on only one of them.
Is it possible to have similar behavior as setting (GridViewUpdatedEventArgs)eventArgs.KeepInEditMode in normal edit mode?
Dani
|
|
|
|
|
|
Hi, thanks again for a great gridview! If anyone else is getting this error (using a databound DropDownList in your EditItemTemplate), try:
Add this to the InsertableGrid code:
private void PrepareDropDowns(bool addBlank)<br />
{<br />
foreach (TableCell t in FooterRow.Cells)<br />
{<br />
foreach (Control c in t.Controls)<br />
{<br />
DropDownList ddl = c as DropDownList;<br />
<br />
if (ddl != null && !ddl.DataSourceID.Equals(""))<br />
{<br />
if (addBlank)<br />
{<br />
ddl.AppendDataBoundItems = true;<br />
ddl.Items.Insert(0, "");<br />
}<br />
else<br />
{<br />
ddl.SelectedIndex = 1;<br />
ddl.Items.RemoveAt(0);<br />
}<br />
}<br />
}<br />
}<br />
}
Swap your CreateChildControls routine to this:
protected override int CreateChildControls(System.Collections.IEnumerable dataSource, bool dataBinding)<br />
{<br />
int ret = base.CreateChildControls(dataSource, dataBinding);<br />
if (Columns.Count == 0 || DesignMode || !AllowInsert)<br />
return ret;<br />
ShowFooter = true;<br />
DataControlField[] flds = new DataControlField[Columns.Count];<br />
Columns.CopyTo(flds, 0);<br />
if (ret == 0)<br />
{<br />
Controls.Clear();<br />
Table t = new Table();<br />
Controls.Add(t);<br />
<br />
GridViewRow r = CreateRow(-1, -1, DataControlRowType.Header, DataControlRowState.Normal);<br />
this.InitializeRow(r, flds);<br />
t.Rows.Add(r);<br />
<br />
gvFooterRow = CreateRow(-1, -1, DataControlRowType.Footer, DataControlRowState.Insert);<br />
this.InitializeRow(gvFooterRow, flds);<br />
t.Rows.Add(gvFooterRow);<br />
}<br />
else<br />
gvFooterRow = base.FooterRow;<br />
FooterRow.RowState = DataControlRowState.Insert;<br />
FooterRow.Visible = (base.EditIndex < 0);<br />
for (int i = 0; i < Columns.Count; i++)<br />
{<br />
DataControlFieldCell cell = (DataControlFieldCell)FooterRow.Cells[i];<br />
DataControlField fld = Columns[i];<br />
fld.InsertVisible = fld.Visible;<br />
if (fld is CommandField)<br />
{<br />
CommandField cf = (CommandField)fld;<br />
CommandField ins = new CommandField();<br />
ins.ButtonType = cf.ButtonType;<br />
ins.InsertImageUrl = cf.InsertImageUrl;<br />
ins.InsertText = cf.InsertText;<br />
ins.CancelImageUrl = cf.CancelImageUrl;<br />
ins.CancelText = cf.CancelText;<br />
ins.InsertVisible = true;<br />
ins.ShowInsertButton = true;<br />
ins.Initialize(false, this);<br />
ins.InitializeCell(cell, DataControlCellType.DataCell, DataControlRowState.Insert, -1);<br />
}<br />
else if (cell.Controls.Count == 0)<br />
{
fld.Initialize(base.AllowSorting, this);<br />
fld.InitializeCell(cell, DataControlCellType.DataCell, DataControlRowState.Edit | DataControlRowState.Insert, -1);<br />
PrepareDropDowns(true);<br />
}<br />
}<br />
OrderedDictionary dict = new OrderedDictionary();<br />
base.ExtractRowValues(dict, FooterRow, true, true);<br />
DataTable tbl = new DataTable();<br />
DataRow row = tbl.Rows.Add();<br />
foreach (string k in dict.Keys)<br />
{<br />
tbl.Columns.Add(new DataColumn(k));<br />
row[k] = dict[k];<br />
}<br />
FooterRow.DataItem = new DataView(tbl)[0];<br />
GridViewRowEventArgs args1 = new GridViewRowEventArgs(FooterRow);<br />
this.OnRowCreated(args1);<br />
FooterRow.DataBind();<br />
this.OnRowDataBound(args1);<br />
FooterRow.DataItem = null;<br />
foreach (DataControlField f in Columns)<br />
f.Initialize(this.AllowSorting, this);<br />
PrepareDropDowns(false);<br />
return ret;<br />
}
Cheers
|
|
|
|
|
Hello there!
For some reason this is not working for me.. any ideas? I've just copy/paste your code with no success...
Best Regards
|
|
|
|
|
|
I think the problem here is when I'm trying to create an insert line, I'm trying to bind it to the data. The reason it was done is so I can fill up the insert line with default values. During the binding, list control tries to select empty data in the drop down list box. There are couple of ways to solve it:
- Create OnRowCreated event handler and initialize insert data in there (see article for details on how to do it).
- Remove portion that tries to bind footer row to the data in the CreateChildControls
|
|
|
|
|
hi every one
we can a lot of way for showing data in gridview.
1) the first one is using datasource whitout writing any code.
2) another one is using dataset,datareader,... and writing code.
I whant to khow that :
what is different between using wizard and writing code ?
when we can use the first one ?
thanks alot
|
|
|
|
|
I tried using this as a drop-in replacement for asp:GridView -- just compile, add assembly, add assembly reference, and change tag type -- and it fails.
The CreateChildControls override fails to handle asp:HyperLink inside of ItemTemplate inside of asp:TemplateField.
I'm not sure whether this code breaks a naming container, or if its casts to DataControlField don't handle asp:HyperLinks, or what -- but, anyway, it appears that it is not ready to replace asp:GridView.
|
|
|
|
|
This is almost exactly what I need... except that when I tried it out it ignores the SkinID attribute. Did I do something wrong?
|
|
|
|
|
Hi,
cool control. I have it up and on my page but when I try to add to my DataSet's dataTable, it won't insert correctly. I'm using an ObjectDataSource to bind them and using the wizard to configure the SELECT and INSERT, but its still not working correctly, i just get an empty table back. When i check the RowInserting event, the data is there to send but nothign is happing. any help?
thanks!
|
|
|
|
|
So after much pulling out of hair and gnashing of teeth, I think I found a way to use the InsertVisible property to JUST control whether or not the field is displayed in insert mode. Well, except for in a TemplateField.
As a reference, italic means existing code, bold means new code, and strikethrough means that it was replaced by the bold code immediately proceeding it.
I forgot something important
Comment out this line:
fld.InsertVisible = fld.Visible;
First, make these changes:
else if (cell.Controls.Count == 0)
{
fld.Initialize(base.AllowSorting, this);
fld.InitializeCell(cell, DataControlCellType.DataCell, DataControlRowState.Edit | DataControlRowState.Insert, -1);
fld.InitializeCell(cell, DataControlCellType.DataCell, fld.InsertVisible ? DataControlRowState.Edit | DataControlRowState.Insert : DataControlRowState.Normal, -1);
}
What this does is initialize a cell to Insert mode if InsertVisible is true, or to Normal mode if it's false.
The next change to make:
base.ExtractRowValues(dict, FooterRow, true, true);
foreach (DataControlField f in Columns)
{
BoundField b = f as BoundField;
if (b != null && !dict.Contains(b.DataField))
dict.Insert(0, b.DataField, null);
}
Because of how ExtractRowValues works, if a field is marked InsertVisible="False", the field doesn't make it into that dictionary. However, the dictionary needs to contain the field so the row can databind. Important note: I only handled for BoundFields here, you'll need to do the same thing for any other type of fields you're using. TemplateFields don't have a DataField property, obviously, so I'm not sure how you'd handle those.
The last change you'll need to make:
base.ExtractRowValues(args.NewValues, FooterRow, true, true);
InsertValues = args.NewValues;
foreach (DataControlField f in Columns)
{
BoundField b = f as BoundField;
if (b != null && !b.InsertVisible && InsertValues.Contains((string)b.DataField))
InsertValues.Remove((string)b.DataField);
}
This is the opposite of the last problem. I'm having a brain fart at the moment so I can't remember why it happens, but when inserting, you don't want parameters created for fields marked InsertVisible="False". This takes them out.
Now, this whole thing could be done better if the ExtractRowValues (actually, the part that contains the code that breaks when InsertVisible is set is in the individual field type classes in a function called ExtractValuesFromCell I believe), but I didn't feel like doing that, and this was pretty easy.
Hope this helps.
Chris
|
|
|
|
|
This code is absolutely amazing! It saved me a lot of work, thanks for the posting.
I like the fact that the footer row gets created automatically by looking at the edit row. However, I need to be able to have different fields in the footer row with extra buttons. I tried creating the FooterTemplate and it renders correctly. When I click on the insert link however, it does not insert the row and it does not fire the RowInserting event.
Thanks
|
|
|
|
|
Hello
I have tried in vain to add your insertion footer code to my subclassed gridview. There are multiple errors eg. the footer is not show, columns go missing and cannot be databound...
In trying to find the issue I see that you have exactly 0 comments in InsertableGrid.cs.
Admittedly, c# is easy to understand but usually you add comments to give other programmers a hint on what you are trying to do -- the big picture.
Normally I evaluate code quality by the (precense) and quality of the comments, among other things. While you have no comments at all the code seems pretty well worked. However it still won't play nicely with my existing app...
Could you please add comments in the future?
Best regards
Andreas Warberg
|
|
|
|
|
i'm using a BLL as my data source and in the insert row of the grid all the text boxes are disabled and i can't input the data? any taughts on this? thanks.
|
|
|
|
|
<asp:templatefield headertext="Name" sortexpression="Name">
<itemtemplate>
<asp:label id="lblCategory1" width="250px" text="<%# Eval("Name") %>" runat="server">
this code works fine when i set AllowInsert to False if set to true i get this error
DataBinding: 'System.Data.DataRowView' does not contain a property with the name 'Name'.
Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.
Exception Details: System.Web.HttpException: DataBinding: 'System.Data.DataRowView' does not contain a property with the name 'Name'.
Any thoughts on this problem? other than this issue this is a great article and after i fix this issue everything should work great for me!
|
|
|
|
|
I want to use this control to insert into a table that uses a primary key where indentity specification is turned on. when using the normal grid view, i can set the readonly property to true for this bound column to prevent it being edited.
is there a way to display these fieldsas 'non-editable' within the inset row?
|
|
|
|
|
I was having some problem using a Calendar and binding it to a DateTime value.
After 2 little changes on your grid I managed to make it work.
Maybe it will help people who are having the same problem...
I replaced your code in 2 places :
on line 143
foreach (string v in InsertValues.Values)<br />
if ((allEmpty = (v == null || v.Trim().Length == 0)) == false)<br />
break;
replaced by
for (int i = 0; i < InsertValues.Count; i++)<br />
{<br />
if ((allEmpty = (InsertValues[i].ToString() == null || InsertValues[i].ToString().Trim().Length == 0)) == false)<br />
break;<br />
}
and on line 106
tbl.Columns.Add(new DataColumn(k));
replaced by
tbl.Columns.Add(k, typeof(System.Object));
|
|
|
|
|
I have a bound DropDown control in an Edit Template field. The souce of the DropDown is an 'ObjectDataSource'.
When selectec value from DropDown and press button save, the "args.NewValues" is empty
Thanks.
Massimo
|
|
|
|
|
Massimo,
I have exactly the same problem: drop down + objectdatasource in a footer template.
I seems it commes from ExtractRowValues(args.NewValues, this.FooterRow, true, true).
Did you find a solution ?
Thanks for your reply.
Jean-Luc
|
|
|
|
|
Same problem. Has anybody found a solution to the args.NewValues being empty when using a dropdown in an Edit Template field using an ObjectDataSource?
|
|
|
|
|
Same problem here Any ideas anyone?
|
|
|
|
|