ASP ListView - Eval() as formatted number, Bind() as unformatted? - asp.net

I have an ASP ListView, and have a very simple requirement to display numbers as formatted w/ a comma (12,123), while they need to bind to the database without formatting (12123). I am using a standard setup - ListView with a datasource attached, using Bind().
I converted from some older code, so I'm not using ASP.NET controls, just form inputs...but I don't think it matters for this:
<asp:SqlDataSource ID="MySqlDataSource" runat="server"
ConnectionString='<%$ ConnectionStrings:ConnectionString1 %>'
SelectCommand="SELECT NUMSTR FROM MY_TABLE WHERE ID = #ID"
UpdateCommand= "UPDATE MY_TABLE SET NUMSTR = #NUMSTR WHERE ID = #ID">
</asp:SqlDataSource>
<asp:ListView ID="MyListView" runat="server" DataSourceID="MySqlDataSource">
<LayoutTemplate>
<div id="itemplaceholder" runat="server"></div>
</LayoutTemplate>
<ItemTemplate>
<input type="text" name="NUMSTR" ID="NUMSTR"
runat="server" value='<%#Bind("NUMSTR")%>' />
<asp:Button ID="UpdateButton" runat="server" Text="Update" Commandname="Update" />
</ItemTemplate>
</asp:ListView>
In the example above, NUMSTR is a number, but stored as a string in a SqlServer 2008 database. I'm also using the ItemTemplate as read and edit templates, to save on duplicate HTML. In the example, I only get the unformatted number. If I convert the field to an integer (via the SELECT) and use a format string like Bind("NUMSTR", "{0:###,###}"), it writes the formatted number to the database, and then fails when it tries to read it again (can't convert with the comma in there).
Is there any elegant/simple solution to this? It's so easy to get the two-way binding going, and I would think there has to be a way to easily format things as well...
Oh, and I'm trying to avoid the standard ItemTemplate and EditItemTemplate approach, just for sheer amount of markup required for that.
Thanks!

As seen in my comment above, I ended up just stripping commas from the NewValues collection in the ListView ItemUpdating event, and adding in commas on the ItemDataBound event.
If there are other ways to do this in a clean way, I'm interested!
Code in VB.NET, comment syntax made to be compatible with stackoverflow's highlighting:
Protected Sub myListView_OnItemDataBound(ByVal sender As Object, ByVal e As ListViewItemEventArgs) Handles myListView.ItemDataBound
Dim intNumber As Integer
For Each c As Control In e.Item.Controls
If TypeOf c Is TextBox Then /*ASP textbox controls*/
Dim numberText As TextBox
numberText = DirectCast(c, TextBox)
If Integer.TryParse(numberText.Text, intNumber) Then /*If the text can be parsed, format it*/
numberText.Text = String.Format("{0:###,##0}", intNumber)
End If
Next
End Sub
Protected Sub myListView_OnItemUpdating(ByVal sender As Object, ByVal e As ListViewUpdateEventArgs) Handles myListView.ItemUpdating
Dim cleanNumber As String
Dim intNumber As Integer
For Each key As String In e.NewValues.Keys
cleanNumber = e.NewValues(key).ToString().Replace(",", Nothing) /*Remove all commas*/
If Integer.TryParse(cleanNumber, intNumber) Then /*If the text can be parsed, format it*/
e.NewValues(key) = intNumber.ToString()
End If
Next
End Sub

Cannot you use <%# Bind("expression"[, "format"]) %> style?
So in your case, devise a format string (I think it should be "#,###") and ASP.NET would be able to format the string both on input and output.

Related

ASP.NET bound hyperlink as SQL Server stored procedure parameter?

I've currently got a gridview that is populated with summary data. I've created a hyperlink from a bound field labeled ticket_num. What I'm wanting to accomplish is to click the hyperlink and have that call a stored procedure. I need to pass that text of that hyperlink into the stored procedure in SQL Server. So the flow is something like this...
User clicks link
The text of that hyperlink is passed into a parameter for the SQL Server stored procedure to use
Call the stored procedure and display results on new page
Any ideas? The stored procedure is created, connection into the server via ASP.NET is created. Everything works thus far but I can't figure this piece of it out.
Well, since you want some code to execute, then LITTLE reason exists to use a hyper-link, since that not what you need nor want.
You don't mention if you prefer to "hide" the URL and hyper link anyway? (often for security, this is not all such a bad idea).
so, if you need to hide/not show/don't want the value in the grid, then our simple button click can get the row id of the grid (also hidden), and then get the value, and then pass to sql server.
Or, you can add to the button the URL or value to the button - just use command argument. That way you don't have to hit the database again based say on row PK id.
So, here is a simple grid. I have the URL as a row on the grid, but lets also shove/put it into the button for you to see how this works.
So, simple grid:
<asp:GridView ID="GridView1" runat="server" AutoGenerateColumns="False"
DataKeyNames="ID" CssClass="table" >
<Columns>
<asp:BoundField DataField="Fighter" HeaderText="Fighter" />
<asp:BoundField DataField="Engine" HeaderText="Engine" />
<asp:BoundField DataField="Thrust" HeaderText="Thrust" />
<asp:BoundField DataField="Description" HeaderText="Description" />
<asp:TemplateField HeaderText="Preview">
<ItemTemplate>
<asp:Image ID="Image2" runat="server" ImageUrl = '<%# Eval("ImagePath") %>' Width="140" />
</ItemTemplate>
</asp:TemplateField>
<asp:TemplateField HeaderText="View">
<ItemTemplate>
<asp:Button ID="cmdView" runat="server" Text="View" CssClass="btn"
CommandArgument = '<%# Eval("ImagePath") %>' OnClick="cmdView_Click" />
</ItemTemplate>
</asp:TemplateField>
</Columns>
</asp:GridView>
And our code to load up the above:
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
If Not IsPostBack Then
LoadGridF()
End If
End Sub
Sub LoadGridF()
Using conn As New SqlConnection(My.Settings.TEST4)
Using cmdSQL As New SqlCommand("SELECT * FROM Fighters", conn)
conn.Open()
Dim rstData = New DataTable
rstData.Load(cmdSQL.ExecuteReader)
GridView1.DataSource = rstData
GridView1.DataBind()
End Using
End Using
End Sub
And now we have this:
Note how we have a PLAIN JANE button - after all, it not really a hyper link we need, is it?
So, for our button click event, we have this:
Protected Sub cmdView_Click(sender As Object, e As EventArgs)
Dim btn As Button = sender
Dim gRow As GridViewRow = btn.NamingContainer
Debug.Print("Row index click = " & gRow.RowIndex)
' get database row PK id
Dim iPK As Integer = GridView1.DataKeys(gRow.RowIndex).Item("ID")
' now we have database row PK - walk the dog, do payroll processing
' do ANYTHING we want like get the database row etc.
Debug.Print("Data base row PK id = " & iPK)
Debug.Print("Command button arugment = " & btn.CommandArgument)
End Sub
And output is then:
So, you can see, we did not need (or want a hyper link), but a simple button, and that click event can:
get any value from the given grid row
get the database PK id (hidden - never exposed to client side (good security).
get the row click index
get parameters passed to button
And we can use gRows.Cells() to get the other row values (for data fields)
And of course gRow.FindControl("ctrl name") for templated columns.

How to handle nulls with Eval

I can have a record with all filled in fields and then without an SO_ID or SO_Num. I want my eval to be able to handle these and just return a '-' in the grid column when this happens while still returning all other data for that row. I've tried other solutions online and couldn't find one that works.
<dx:GridViewDataColumn FieldName="SO_Num" VisibleIndex="19" runat="server" Caption="Sales Order Number">
<DataItemTemplate>
<a id="clickElement" href="../sales/order/view.aspx?ID=<%# Eval("SO_ID").ToString()%>"><%#Eval("SO_Num").ToString()%></a>
</DataItemTemplate>
</dx:GridViewDataColumn>
You can use a in-line "iif(), and thus this:
<asp:TemplateField HeaderText="City">
<ItemTemplate>
<asp:TextBox ID="txtCity" runat="server"
Text = '<%# IIf(IsDBNull(Eval("City")), "-", Eval("City")) %>'
></asp:TextBox>
</ItemTemplate>
</asp:TemplateField>
So, in above, if say Eval("City") is null, then we display a "-", else we display Eval("City")
Edit: display of values and some button to click and navgate are DIFFERENT!!
As I pointed out, if you need a Eval() in the GridView, and want to convert a null say into a "-", then do that (but, I fail to see why I would want to display some "-" in the GV. Why do that?? Seems rather strange to me?
However, if you have a button on the GV row, and you want to click on that button to jump or navigate to some other page? Fail to see how such a button click and navigate has ANY REALATIONSHIP to what we display? Why are the two concepts connected? I fail to see any sensible logic here?
If you want to drop in a pane jane button, or even a link button (no difference here), then wire up a click event for that given button you drop in.
so, say in our GV, we drop in a button to view a given row of data. Say this GV with hotels, and a button.
<asp:GridView ID="GridView1" runat="server" AutoGenerateColumns="False"
CssClass="table table-hover" Width="50%"
DataKeyNames="ID" >
<Columns>
<asp:BoundField DataField="FirstName" HeaderText="FirstName" />
<asp:BoundField DataField="LastName" HeaderText="LastName" />
<asp:TemplateField HeaderText="Hotel Name">
<ItemTemplate>
<asp:Label ID="txtHotel" runat="server"
Text='<%# Eval("HotelName") %>' >
</asp:Label>
</ItemTemplate>
</asp:TemplateField>
<asp:BoundField DataField="Description" HeaderText="Description" />
<asp:TemplateField HeaderText="View">
<ItemTemplate>
<asp:Button ID="cmdView" runat="server" Text="View" CssClass="btn "
/>
</ItemTemplate>
</asp:TemplateField>
</Columns>
<PagerStyle CssClass="pagenavi" />
</asp:GridView>
so, we just dropped in a plane jane button for operations on that one row.
So, our code to load is this:
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
If Not IsPostBack Then
LoadGrid()
End If
End Sub
Sub LoadGrid()
Using conn As New SqlConnection(My.Settings.TEST4)
Dim strSQL As String =
"SELECT * from tblHotelsA ORDER BY HotelName"
Using cmdSQL As New SqlCommand(strSQL, conn)
conn.Open()
Dim rst As New DataTable
rst.Load(cmdSQL.ExecuteReader)
GridView1.DataSource = rst
GridView1.DataBind()
End Using
End Using
End Sub
And our results are now this:
Ok, so now lets wire up that plane jane button click.
As a normal rule, you can double click on a button to build the click event, or bring up the property sheet, choose events tab/section, and then add the click event. However, since the button is in the GV, then we have to add the click event this way (in the markup).
Type in OnClick=, and when you hit the "=" sign, intel-sense will popup a dialog to create the event.
You get this:
So, we select create new event. Don't seem like anything occurred, but flipping to code behind, we have a click event stub, and our code thus can be this:
Protected Sub cmdView_Click(sender As Object, e As EventArgs)
Dim btn As Button = sender
Dim gRow As GridViewRow = btn.Parent.Parent
Dim intPKID As Integer = GridView1.DataKeys(gRow.RowIndex).Item("ID")
Debug.Print("Row click index = " & gRow.RowIndex)
Debug.Print("Row click database PK id = " & intPKID)
' now do whatever you want with this row information.
' to get values from non templated columns, use cells()
' to get values from tempated columns, use findcontrol
'eg:
' get last name (Boundfield)
Debug.Print("Last name = " & gRow.Cells(1).Text)
' get hotel name - template - "label"
Dim lblHotel As Label = gRow.FindControl("txtHotel")
Debug.Print("Hotel name (label) = " & lblHotel.Text)
End Sub
output:
So, as noted, I fail to see why ANY issue occurs here in regards to some data in a column of the GV being null?
In your case, just navigate based on the button click to anything you want, based on any value you want.
say like this:
<asp:TemplateField HeaderText="View">
<asp:Button ID="cmdView" runat="server" Text="View" CssClass="btn "
OnClick= "cmdView_Click"
CommandArgument = '<%# Eval("SO_ID") %>' />
</asp:TemplateField>
And then in code behind:
Protected Sub cmdView_Click(sender As Object, e As EventArgs)
Dim btn As Button = sender
Dim gRow As GridViewRow = btn.Parent.Parent
Dim intPKID As Integer = GridView1.DataKeys(gRow.RowIndex).Item("ID")
Debug.Print("Row click index = " & gRow.RowIndex)
Debug.Print("Row click database PK id = " & intPKID)
Dim intSOID = btn.CommandArgument
Dim strURLJumpTo = "../sales/order/view.aspx?ID=" & intSOID
Response.Redirect(strURLJumpTo)
So, you are free to cook up any URL navagation you want.
NOTE VERY close how I used the data keys feature of the GV. That allowed me to have, use, get, play with the database PK row id, but NEVER do I have to expose or show or have or mess with the database PK id in the actual GV markup.
This is not only nice, but also is a HUGE deal from a security point of view, since then the user, the browser (client side) thus NEVER has to see, or know or can munge or play with the database PK row id - it is 100% server side managed.
And in fact if you SO_ID or whatever values are not in the GV, or the user does not care? Then I would NOT pass the values in the URL as so called "query parms" of the URL, but would in fact pass the values in session() like say this:
Session("OrderID") = intPKID
Response.Redirect("../sales/order/view.aspx")
Then in the page load event of the target page, I do this:
and NEVER EVER forget to check/use the ispostback in your page load event.
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
If Not IsPostBack Then
ViewState("OrderID") = Session("OrderID")
Now, in that code behind, and that page, you use this to get/use/have the orederid
dim intOrderID = ViewState("OrderID")
So, on page load (first page, ispostback = false), you transfer the session() value to the ViewState. And you do this since the user might have more then one copy of the browser running - and thsu using session() to pass the value is ok, but session() is global to the ONE user, where as ViewState is per page. So, that's why we transfer to ViewState on page load, since FROM THAT POINT onwards in that page, and code behind, we use ViewState. If we used session() in the code behind, then it is global, and if more then one copy of the browser is running or even multiple tables, they will all have the same session() value.
So, say you click on a house to view or buy?
Well, then they might open another tab - display same GV, and click on a different row. If we use session, you now display two pages - but both have the one and same row PK id value - and if you click buy house, you get the wrong house if your code behind uses session(), but with ViewState, the issue does not exist.
And thus, you can even dump your ugle "id" and parameters out of the URL - they look much nicer, but are also much more secure, and the user thus does not have to see, or know about things such as database row PK junk and stuff.

access selectcommand using codebehind

How can I change my selecommand, and keep it through the remainder of the page (when using pagination, sorting)?
I have a page of checkboxes:
<input type="checkbox" name="checkbox_1" />
<input type="checkbox" name="checkbox_2" />
<input type="checkbox" name="checkbox_3" />
<asp:Button runat="server" Id="CustomButton" text="Create Report" PostBackUrl="report.aspx?"/>
Then on report.aspx I want to generate a standard listview based on the selections in the checkbox.
<asp:ListView runat="server" ID="ReportListView" DataSourceID="ReportListViewSDS">
<LayoutTemplate runat="server">
...<asp:PlaceHolder runat="server" ID="itemPlaceHolder" />...
</LayoutTemplate>
<ItemTemplate>
...
</ItemTemplate>
</asp:ListView>
I want to be able to sort and paginate that listview. This is an idea of what i want in the code behind:
Protected Sub ReportListView_PreRender(ByVal sender As Object, ByVal e As System.EventArgs)
' What's the correct way to reference the listview?
' When I use the below code i get "ReportListView is not declared...."
' ReportListView.SqlCommand = "SELECT " & checkbox1 & ", " & checkbox2 & " WHERE..."
End Sub
I'm not sure if I'm even going in the right direction with this, any help is appreciated. Will the changes i make to the sql command in the PreRender function hold when I have applied pagination or sorting to the listview?
If I understand your question correctly, you want to open a new page and use the prior page's values in the select statement for the ListView's SqlDataSource on the new page, correct?
First, a few observations:
In your first page, you appear to be intending to call the second page with a query string (PostBackUrl="report.aspx?), but you don't appear to set the query string.
Your PreRender event for the ListView control has the wrong signature. It only takes one argument, EventArgs:
Protected Sub ReportListView_PreRender(ByVal e As EventArgs)
Your ListView appears to be using a SqlDataSource as it's binding source (DataSource="ReportListViewSDS"). More about that below.
There is no SqlCommand property or method for the ListView control.
Since you're binding the ListView to a SqlDataSource, it'd be simplest to set the Select command and the parameters in the markup, like this:
<asp:SqlDataSource ID="ReportListViewSDS" runat="server"
SelectCommand="SELECT checkbox1, checkbox2, checkbox3 FROM <table> WHERE checkbox1 = #parm1 AND checkbox2 = #parm2 AND checkbox3 = #parm3">
<SelectParameters>
<asp:FormParameter FormField="checkbox_1" Name="parm1" />
<asp:FormParameter FormField="checkbox_2" Name="parm2" />
<asp:FormParameter FormField="checkbox_3" Name="parm3" />
</SelectParameters>
</asp:SqlDataSource>
Replace <table> in the SelectCommand with the name of your table. You can adjust the names of the columns you're selecting, as well as the parameters you're using, as desired. I simply used 3 checkboxes as that's what you had in the code you posted.
Also note, NO VALIDATION of the parameters will be done by the SqlDataSource, so if you want to prevent SQL Injection attacks and other security risks, you'll want to do validation in the Selecting event of the SqlDataSource.
More information can be found here:
SqlDataSource Class
FormParameter Class
Actually this was much easier than I thought. Sorry, just a newbie mistake i guess. i ended up simply doing:
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
Dim SqlCommand As String
' create the sql command using the Request.Form vars i wanted.
' ...
' Run the sql command, I can access the listview directly, just like a global variable:
ReportListView.SelectCommand = SqlCommand
ReportListView.DataBind()
End Sub
And that seemed to do it. Actually very easy.

Using now() in a <asp:textbox>

Can anyone help. I am using a formview in VS 2005. I have different elements in my form databound to a database and I am performing an INSERT SQL statement. No problem. The problem is that I am trying to enter the current date into the SQL statement and I am having a problem.
I can add <%now()%> to the "Text" property of the asp:Textbox. But when I do, then I can't bind the textbox to a specific database column.
How do I do both????
I can do this:
<asp:TextBox ID="TextBox2" runat="server" Text='<%# Bind("Initiate_Date") %>' ></asp:TextBox>
Or this:
<asp:TextBox ID="TextBox2" runat="server" Text='<%# now() %>' ></asp:TextBox>
But I don't know how to do both.
Leave it bound to Initiate_Date in the aspx but in your code behind set it to Now() when you need to....
Handle the ItemUpdating method of your formview. There you can cancel the event and write your own db handling code by getting the values from the formview, or you can just change the data item in the formview and inject your chages (and let the formview handle teh database)
http://msdn.microsoft.com/en-us/library/system.web.ui.webcontrols.formview.itemupdating.aspx
I needed to get the now() value into the selectcommand of my sqldatasource. There was no need for a textbox.
This is what I ultimately did.
On the code behind I created a session variable and set Now() to the value.
> Protected Sub Page_Load(ByVal sender
> As Object, ByVal e As
> System.EventArgs) Handles Me.Load
> Dim RightNow As Date = Now()
> Dim RightNowShort As Date = RightNow.ToShortDateString
>
> Session("RightNow") = RightNowShort
> End Sub
On the aspx page I use the variable in a sessionparameter tag in the InsertParameters tag of my SqlDataSource.
<InsertParameters>
<asp:sessionparameter sessionfield="RightNow" type="String" name="RightNow" />
</InsertParameters>
Thanks.

Loading a value on the insert command of a detailsview

In a detailsview, how can I prepopulate one of the textboxes on the insertcommand (When the user clicks insert and the view is insert).
I think this would work for codebehind:
Dim txtBox As TextBox = FormView1.FindControl("txtbox")
txtbox.Text = "Whatever I want"
Is this right? What do I need in the aspx (not as sure)? Also, I'm assuming the server-side code will go in the itemcommand or insertcreating event.
I have typed this in VB.NET but I am using C# (I can do both so on a language agnostic forum I might type the problem in another language). I am also using a SqlDataSource, with my parameters and insert/delete/edit commands all created.
I am trying to generate a random GUID (using the GUID object), which will be prepopulated in the textbox.
Also, is the postbackurl property of a button not another way of preserving form state?
Thanks
I would update the field in the DetailsView to a TemplateField:
<asp:TemplateField>
<InsertItemTemplate>
<asp:TextBox ID="txtField" runat="server" Text='<%# Bind("GUID") %>'/>
</InsertItemTemplate>
<ItemTemplate>
<asp:Label ID="lblField" runat="server" Text='<%# Bind("GUID") %>'/>
</ItemTemplate>
</asp:TemplateField>
Then you have two options:
generate your GUID and insert into
your datasource. This may have to be done with SQL since you mentioned using SqlDataSource
remove the binding and access the controls from code in the
DataBound event of your DetailsView
Private Sub dv_DataBound(ByVal sender As Object, ByVal e As EventArgs) Handles dv.DataBound
dim txt as Textbox = dv.FindControl("txtField")
txt.Text = GenerateGUID()
End Sub
I'm guessing you need to use one of detailsview events.
Hook up to ItemCommand, ModeChanging or ModeChanged events and fill your value there.
I am doing something like this as well. I am hiding the DetailsView and showing it when the user clicks a button.
dvDetails.ChangeMode(DetailsViewMode.Insert)
pnlDetailMenu.Visible = True
Dim ColumnTextBox As TextBox
ColumnTextBox = dvDetails.Rows(0).Cells(1).Controls(0)
If Not ColumnTextBox Is Nothing Then
ColumnTextBox.Text = "Initial Value"
End If

Resources