Monday, September 29, 2008

FormView - FindControl causes DataBind when in EditMode!

I just had the misfortune of discovering a “feature” of the FormView: If you try to do a a FormView.FindControl while in EditMode, it causes a DataBind that very instant.  This was perplexing b/c right after I did the FindControl, I was trying to change the mode to Insert and couldn’t figure out why the ObjectDataSource was all-of-a-sudden firing a Select?  This happened after a small tweak to working code mind-you, so it’s one of those “oh-shoot, what just happened to completely break the form I’ve been working on for 5 days” moments!  I guess that’s what I get for going Codeless.

 

 

Monday, September 22, 2008

Using GridView Footer to Insert Record even if Empty

(Updated 10/29 to include ASP.Net example

A great thanks to Matt Berseth for his post on subclassing the GridView to show footers even when the GridView has no records. I used a C# to VB.Net converter to convert his excellent example and the net result is the code below (and includes the tweak/fix for persisting the Footer on PostBack). A few pecularities I noticed about using the Footer.

  • I could not use two-way binding with my ObjectDataSource in the footer so I used the traditional code-behind approach with FindControl (insert does not use the DataSource at all)
  • to find your controls you have to use MyGridView.Footer.FindControl(“aTextBox”)
  • to hide/show the footer I added buttons to the header and footer that fire the GridView.RowCommand (note, the Insert also uses this technique)

Public Class MyGridView
Inherits GridView

Protected Overloads Overrides Function CreateChildControls(ByVal dataSource As System.Collections.IEnumerable, ByVal dataBinding As Boolean) As Integer
Dim numRows As Integer = MyBase.CreateChildControls(dataSource, dataBinding)

'no data rows created, create empty table if enabled
If numRows = 0 AndAlso ShowWhenEmpty Then
'create table
Dim table As New Table()
table.ID = Me.ID

'convert the exisiting columns into an array and initialize
Dim fields As DataControlField() = New DataControlField(Me.Columns.Count - 1) {}
Me.Columns.CopyTo(fields, 0)

If Me.ShowHeader Then
'create a new header row
Dim headerRow As GridViewRow = MyBase.CreateRow(-1, -1, DataControlRowType.Header, DataControlRowState.Normal)

Me.InitializeRow(headerRow, fields)
table.Rows.Add(headerRow)
End If

'create the empty row
Dim emptyRow As New GridViewRow(-1, -1, DataControlRowType.EmptyDataRow, DataControlRowState.Normal)

Dim cell As New TableCell()
cell.ColumnSpan = Me.Columns.Count
cell.Width = Unit.Percentage(100)
If Not [String].IsNullOrEmpty(EmptyDataText) Then
cell.Controls.Add(New LiteralControl(EmptyDataText))
End If

If Me.EmptyDataTemplate IsNot Nothing Then
EmptyDataTemplate.InstantiateIn(cell)
End If

emptyRow.Cells.Add(cell)
table.Rows.Add(emptyRow)

If Me.ShowFooter Then
'create footer row
'Dim footerRow As GridViewRow = MyBase.CreateRow(-1, -1, DataControlRowType.Footer, DataControlRowState.Normal)
_footerRow = MyBase.CreateRow(-1, -1, DataControlRowType.Footer, DataControlRowState.Normal)

Me.InitializeRow(footerRow, fields)
table.Rows.Add(footerRow)
End If

Me.Controls.Clear()
Me.Controls.Add(table)
End If

Return numRows
End Function


<Category("Behaviour")> _
<Themeable(True)> _
<Bindable(BindableSupport.No)> _
Public Property ShowWhenEmpty() As Boolean
Get
If ViewState("ShowWhenEmpty") Is Nothing Then
ViewState("ShowWhenEmpty") = False
End If

Return CBool(ViewState("ShowWhenEmpty"))
End Get
Set(ByVal value As Boolean)
ViewState("ShowWhenEmpty") = value
End Set
End Property

Protected _footerRow As GridViewRow = Nothing
Public Overloads Overrides ReadOnly Property FooterRow() As GridViewRow
Get
Return IIf(_footerRow Is Nothing, MyBase.FooterRow, _footerRow)
End Get
End Property


End Class



<asp:ValidationSummary ID="vsumDocErrors" runat="server" />
<cc1:MyGridView ID="gvwDocs" runat="server" AutoGenerateColumns="False" DataSourceID="dsDocs"
DataKeyNames="ID" ShowWhenEmpty="true">
<EmptyDataTemplate>
No related data elements</EmptyDataTemplate>
<Columns>
<asp:TemplateField>
<headertemplate>
<asp:ImageButton runat="server" CommandName="Add" AlternateText="Add New Item" ImageUrl="../images/add.png"
OnPreRender="AddButton_PreRender"/>
</headertemplate>
<itemtemplate>
<asp:ImageButton runat="server" CommandName="Select" AlternateText="View Item" ImageUrl="../images/MGlass.png" />
<asp:ImageButton ID="btnDelDoc" runat="server" CommandName="Delete" AlternateText="Delete Item"
ImageUrl="../images/cross.png"
visible="<%# (UserCanEdit) AndAlso (gvwDocs.ShowFooter = False) %>"
OnClientClick='<%# DataBinder.Eval(Container.DataItem, "Description", "return confirm(""Are you sure you want to delete document: [{0}] ?"");") %>' />
</itemtemplate>
<footertemplate>
<asp:ImageButton ID="btnFooterCancel" runat="server" CommandName="CancelAdd" AlternateText="Cancel Add" CausesValidation="false"
ImageUrl="../images/Cancel.png"
OnClientClick="doCheck = false;" />
<asp:ImageButton ID="ImageButton1" runat="server" CommandName="Insert" CommandArguement="Insert" AlternateText="Save Item"
ImageUrl="../images/disk.png"
OnClientClick="doCheck = false;" />
</footertemplate>
</asp:TemplateField>
<asp:TemplateField HeaderText="Document Name">
<itemtemplate>
<asp:Label runat="server" Text='<%# CType(Container.DataItem, MyProj.Lib.Document).Description %>'></asp:Label>
</itemtemplate>
<footertemplate>
<div><asp:Label runat="server" AssociatedControlID="inDocument" CssClass="Label" Style="width:120px">Select File</asp:Label>
<asp:RequiredFieldValidator runat="server" Display="Dynamic" ErrorMessage="File has not been selected" controlToValidate="inDocument">*</asp:RequiredFieldValidator></div>
<asp:FileUpload runat="server" id="inDocument" Style="width:500px;font-size:x-small;" />
<asp:RegularExpressionValidator id="vldDocument" runat="server" OnLoad="Setup_Doc_Validator" ControlToValidate="inDocument" />
<div><asp:Label runat="server" AssociatedControlID="inDocName" CssClass="Label" Style="width:120px">Friendly Name</asp:Label>
<asp:TextBox runat="server" id="inDocName" Columns="40" Style="font-size:x-small" />(optional)</div>

</footertemplate>
</asp:TemplateField>
<asp:BoundField DataField="CreatedBy" HeaderText="Created By" />
</Columns>
</cc1:MyGridView>
<asp:ObjectDataSource ID="dsDocs" runat="server" SelectMethod="GetDocumentsByAgency"
TypeName="MyProj.Lib.DocumentDAL" DeleteMethod="Delete" DataObjectTypeName="MyProj.Lib.Document">
<SelectParameters>
<asp:QueryStringParameter Name="intAgencyID" QueryStringField="PID" Type="Int32" />
</SelectParameters>
</asp:ObjectDataSource>


Thursday, September 18, 2008

Newsflash: IIf is NOT a ternary operator

I guess this is not exactly news, but IIf does not provide short circuiting.  Both the True and False paths are evaluated.  I’m sure I ran into this before and just forgot about it, but I wanted to post this so hopefully I will remember not to use IIf as a null check for an object b/c it will pass/use the Null despite the result of the boolean evaluation:

Dim agencyId As Integer = _
    IIf(agencyCode IsNot Nothing, SomeMethodThatNoLikeNull(agencyCode), 0)

 

Friday, September 12, 2008

Bind a GridView DataSource Inside a FormView

I have a FormView that uses an ObjectDataSource to retrieve a business object that contains a collection of children objects. I wanted to display the children in a GridView and it turns out to be very easy to do using Eval:


<asp:GridView ID="GridView1" runat="server" Visible='<%# Eval("HasChildren") %>'
AutoGenerateColumns="False" DataSource='<%# Eval("ChildrenApplications") %>'>
<Columns>
<asp:BoundField DataField="EAAppNum" HeaderText="EA ID" SortExpression="EaIdNegative" />
<asp:HyperLinkField DataNavigateUrlFormatString="ApplicationForm.aspx?pid={0}" DataNavigateUrlFields="ID"
DataTextField="Name" HeaderText="Name" SortExpression="Name" />
</Columns>
</asp:GridView>

Overloading Stored Procedures - Oracle Stored Procedure Calling Another Procedure

I had a procedure that I wanted to reuse, and extend…my first thought was why not overload it. It took awhile to get to the syntax correct, but it turns out that there is no syntax…you just call the procedure. Keep in mind my procedures are in separate packages and this returns multiple resultsets (see VB.Net code below):

PROCEDURE GET_APP
(
IN_APP_ID IN NUMBER := 0,
OUT_CUR OUT MY_PACK.PROJECT_CUR,
OUT_CUR2 OUT MY_PACK.PROJECT_CUR
)
IS
BEGIN
MY_OTHER_PACK.GET_APP(IN_APP_ID, OUT_CUR);
OPEN OUT_CUR2 FOR
SELECT * FROM APPS where P_APP_ID = IN_APP_ID;
END GET_APP;


dr = comm.ExecuteReader()
newList = LoadFromDataReader(dr)
If dr.NextResult() Then
childList = LoadFromDataReader(dr)
newList(0).ChildrenApps = childList
End If

Thursday, September 11, 2008

Going codeless - Conditionally hide a control in a formview using databinding

I am a big fan of “codeless” code.  I’ve been relying on FormView controls quite a bit lately.  In one form I needed to show a textbox when the form is in Insert mode, but hide for Edit (I only use one template, EditItemTemplate, in my FormView).  Playing around with WPF had me thinking of ways to bind to properties that already existed rather than create a wrapper property, so I tried binding the TextBox.Visible property to the FormView.CurrentMode property and it WORKED!  I find this much cleaner than adding a FormView.FindControl(“inAgencyId”) in the codebehind.

<asp:TextBox
ID="inAgencyId"
runat="server"
Text="<%# Bind("AgencyCode") %>"
Visible="<%# (fvwComp1.CurrentMode = FormViewMode.Insert) %>"
/>

 

Thursday, September 4, 2008

Overriding PerformDataBinding on Custom ServerControls

I ran into something cool today.  If you Override PerformDataBinding you have access to the DataSource that populated your ListControl.  In my case, I extended RadioButtonList and connected it to an ObjectDataSource that retrieves a List(Of MyObjects) and in the debugger, there was my list of MyObjects in the dataSource collection!

 

        Protected Overrides Sub PerformDataBinding(ByVal dataSource As System.Collections.IEnumerable)

            MyBase.PerformDataBinding(dataSource)

        End Sub

 

I guess this would be a good time to mention that I was able to handle the ArgumentOutOfRangeException that occurs when you bind a RadioButtonList and the ListItem doesn’t exist by Overriding OnDataBinding:

 

        Protected Overrides Sub OnDataBinding(ByVal e As EventArgs)

            Try

                MyBase.OnDataBinding(e)

            Catch ex As ArgumentOutOfRangeException

                Me.ClearSelection()

            End Try

        End Sub