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.
Related
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.
I have a standard Gridview which is populated from a SQLDataSource. The Gridview will always have 17 rows. Can anyone please give me an example of how to manually insert rows into it at designated row spots? For example, insert a new row into rows 3 and 5.
Thanks
Well, it not at all clear why say inserting at row 5, or at row 8 is important here?
Remember, when we used desktop PC - single user, or say used punch cards?
Then order of data entered DID matter. But, we now don't use punched cards, and thus order of such data does not really matter from a database point of view. I mean, if you have multiple users entering data, and I insert at row 5, then what happens if 3 other uses also did the same. Now that record is in the 8th position. So, introduction of a "order" or some "position" NEEDS some further context here - in other words what is the goal here?
when working with a database, the FIRST rule is that data does NOT have order. And if YOU need some kind of order, say like date of entry, or even say order of new records added, then you the developer needs to design that into your software.
Now, having stated the above? Sure, there are class problems or say UI interface types of issues in which you might have say a order of 5 boxes, and you need to offer the user the ability to re-order those 5 items in ANY order you wish.
Ok, so having stated above?
Well, it not clear how the records were entered in the first place. and it not clear HOW the record that exists in the 5th position is supposed to be the 5th record.
Ok, now regardless of the above?
The trick to adding rows, or deleting rows, or inserting data? Do this at the data level, and NOT at the gridview level. This not only separates out the UI layer, and the data layer? It also saves world poverty and boatloads of complex code.
So, lets assume we have a column called "myorder". And if you don't have this column in the table, then you really quite much need to add it, since as I stated, some data order does not exist by magic, but in fact has to be "designed" and "managed" by YOU THE developer!!!
So, lets take a simple gv like this:
<asp:GridView ID="GridView1" runat="server" AutoGenerateColumns="False"
DataKeyNames="ID" CssClass="table borderhide" >
<Columns>
<asp:TemplateField HeaderText="First" ><ItemTemplate>
<asp:TextBox ID="txtFirst" runat="server" Text='<%# Eval("FirstName") %>' Width="80px">
</asp:TextBox>
</ItemTemplate></asp:TemplateField>
<asp:TemplateField HeaderText="Last"><ItemTemplate>
<asp:TextBox ID="txtLast" runat="server" Text='<%# Eval("LastName") %>' Width="80px">
</asp:TextBox>
</ItemTemplate></asp:TemplateField>
<asp:TemplateField HeaderText="Hotel"><ItemTemplate>
<asp:TextBox ID="txtHotel" runat="server" Text='<%# Eval("HotelName") %>'></asp:TextBox>
</ItemTemplate></asp:TemplateField>
<asp:TemplateField HeaderText="Description" ><ItemTemplate>
<asp:TextBox ID="txtDescription" runat="server" Text='<%# Eval("Description") %>'
TextMode="MultiLine" Rows="3" Columns="45"
></asp:TextBox>
</ItemTemplate></asp:TemplateField>
<asp:TemplateField HeaderText="Active" ItemStyle-HorizontalAlign="Center" ><ItemTemplate>
<asp:CheckBox ID="chkActive" runat="server" Checked='<%# Eval("Active") %>' />
</ItemTemplate></asp:TemplateField>
</Columns>
</asp:GridView>
Ok, and our code to load the grid. and as noted, we will persit the data source for the grid, since we need to do those inserts.
So, our code to load:
Dim rstData As New DataTable
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
If Not IsPostBack Then
LoadData()
LoadGrid()
Else
rstData = ViewState("rstData")
End If
End Sub
Sub LoadData()
Using conn As New SqlConnection(My.Settings.TEST4)
Dim strSQL = "SELECT * FROM tblHotelsA ORDER BY MyOrder"
Using cmdSQL As New SqlCommand(strSQL, conn)
conn.Open()
rstData.Load(cmdSQL.ExecuteReader)
End Using
End Using
End Sub
Sub LoadGrid()
rstData.DefaultView.Sort = "MyOrder"
GridView1.DataSource = rstData
GridView1.DataBind()
RowCount.Value = rstData.Rows.Count
ViewState("rstData") = rstData
End Sub
and we now have this:
So, now we have that add button. That button will add the row to the grid, and prompt us for the location (what row to insert at).
so, the button looks like this:
<asp:Button ID="cmdSave" runat="server" Text="Save Changes" style="float:left" CssClass="btn"/>
<asp:Button ID="cmdAddRow" runat="server" Text="+Add New" style="float:right" CssClass="btn"
OnClientClick="return askwhatrow(this);"
/>
<asp:HiddenField ID="WhatRow" runat="server" ClientIDMode="Static"/>
<asp:HiddenField ID="RowCount" runat="server" ClientIDMode="Static"/>
<script>
function askwhatrow(btn) {
MyRowCount = $('#RowCount').val()
strMsg = "There are row 1 to " + MyRowCount + "\n" +
"What row to insert new record?"
strAns = prompt(strMsg)
if (strAns === null) {
return false
}
if ((strAns < 1) || (strAns > MyRowCount) ) {
alert("only 1 to " + MyRowCount + " is allowed")
return false
}
// ok, set the row insert location, and run our server side buttion
$('#WhatRow').val(strAns)
return true
}
</script>
So, when we click on add row, we get this prompt:
I of course entered say 2 (or your 5 for your example).
the js code will prompt the user, but of couse if the user hits cancel, then the button code (code behind server) will not run. The code to add the row of data now looks like this:
Protected Sub cmdAddRow_Click(sender As Object, e As EventArgs) Handles cmdAddRow.Click
' add new row to grid at location choosen by user:
GridToTable() ' save any possbile edits by user
Dim InsertLocation As Integer = WhatRow.Value
For Each OneRow In rstData.Rows
If Int(OneRow("MyOrder")) >= InsertLocation Then
OneRow("MyOrder") += 1
End If
Next
' setup new row - some defaults
Dim NewRow As DataRow = rstData.NewRow
NewRow("MyOrder") = InsertLocation
NewRow("Active") = False
rstData.Rows.Add(NewRow)
LoadGrid()
End Sub
Again, note how VERY easy it is to add that row of data. That's because we add the row into the table, and then simple re-bind the grid to display that new record. In other words, don't try to manipulate the gv, but ONLY manipulate the data!!! (that way we save world poverty and you starving from having to write too much code).
So, after we do the above, we see this:
So, with above grid? You can tab around (almost like excel). You can edit any row, make any changes you want.
We then have that ONE save button. All that does is send gv data back to the table, AND THEN IN ONE operation sends the data back to the database.
The code looks like:
Protected Sub cmdSave_Click(sender As Object, e As EventArgs) Handles cmdSave.Click
GridToTable()
Using conn As New SqlConnection(My.Settings.TEST4)
Dim strSQL = "SELECT * FROM tblHotelsA"
Using cmdSQL As New SqlCommand(strSQL, conn)
conn.Open()
Dim da As New SqlDataAdapter(cmdSQL)
Dim daU As New SqlCommandBuilder(da)
da.Update(rstData)
End Using
End Using
End Sub
Sub GridToTable()
' send all data from gv to local table
For Each gvRow As GridViewRow In GridView1.Rows
Dim pkID As Integer = GridView1.DataKeys(gvRow.RowIndex).Item("ID")
Dim OneRow As DataRow = rstData.Select("ID = " & pkID).FirstOrDefault
OneRow("FirstName") = CType(gvRow.FindControl("txtFirst"), TextBox).Text
OneRow("LastName") = CType(gvRow.FindControl("txtLast"), TextBox).Text
OneRow("HotelName") = CType(gvRow.FindControl("txtHotel"), TextBox).Text
OneRow("Description") = CType(gvRow.FindControl("txtDescription"), TextBox).Text
OneRow("Active") = CType(gvRow.FindControl("chkActive"), CheckBox).Checked
Next
End Sub
So in above, we send all edits, all additions (and if you have or add a delete button to each row, then even deletions are ALL SEND back to the database with the above simple few lines of code. As noted, this was and is possible since we persisted the rstData that drives the GV.
and really nice? Well, for 30+ years, every user on the planet who used Excel, or used any accounting system, or any other computer software? They can bounce around in that grid, make changes. And then with one simple save button send the whole she-bang back to the database.
Please note: I do not necessarily need working code. I just don't know how to word what I am looking for to even find an answer on the web. I guess i'm just asking for a little guidance on what kind of control I would to use to accomplish my goal.
Basically when I go to a job site I will use different amounts and types of inventory. So one line item would consist of
Item Description
Quantity Used
UsedByTech
I'm collecting these via webform text box. I would like to have a button that says "Add" and then allows me to input another line item, and so on.
Then at some point a submit button on the form would gather those line items and input them in to a MSSQL databse.
I'm currently using ASP.Net framwork and webforms.
Can somone please tell me what kind of control would allow this a may be give me some hints about what to search for?
Ok, this is actually quite easy - but we let asp.net do most of the work for us.
So, we assume we have these two tables:
so, we have a table to list out the Job Sites
And then as per above, we have a table to list out the Job items for each site.
Ok, so we display the Job Sites.
Drop in a Gridview - build it using the wizards:
Now, blow out (delete) the data source from the page - don't need it.
Remove the DataSource setting for the Gridview
And lets drop in a plane jane button into that grid.
Thus, this markup - most of it was generated for me
<div style="padding:25px">
<asp:GridView ID="GridView1" runat="server" AutoGenerateColumns="False"
DataKeyNames="ID" CssClass="table" Width="40%">
<Columns>
<asp:BoundField DataField="JobSite" HeaderText="JobSite" />
<asp:BoundField DataField="JobDate" HeaderText="JobDate" DataFormatString="{0:yyyy-MM-dd}" />
<asp:BoundField DataField="Foreman" HeaderText="Foreman" />
<asp:BoundField DataField="Description" HeaderText="Description" />
<asp:TemplateField HeaderText="View">
<ItemTemplate>
<asp:Button ID="cmdView" runat="server" Text="View Job" CssClass="btn"
Onclick="cmdView_Click"/>
</ItemTemplate>
</asp:TemplateField>
</Columns>
</asp:GridView>
</div>
And our code to load this grid is thus 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()
GridView1.DataSource = MyRst("SELECT * from JobSites Order by JobDate")
GridView1.DataBind()
End Sub
Ok, we now have this:
Ok, so now we need to wire up that "view" button.
So, we grab the PK row value - jump to our edit items page;
Protected Sub cmdView_Click(sender As Object, e As EventArgs)
Dim btn As Button = sender
Dim gRow As GridViewRow = btn.Parent.Parent
' get PK database ID
Dim PK As Integer = GridView1.DataKeys(gRow.RowIndex).Item("ID")
' Now jump to our job add job items page
Session("JobID") = PK
Response.Redirect("JobAddItems.aspx")
End Sub
So, now we need to build our 2nd page.
I could use a repeater - but I used DataList - to display the ONE job.
I again used the wizards - then blow out the DataSource setting and item on that page.
We then drop in a gridview for the "many" items (the job items).
I now have this:
<br />
<div style="border:solid;border-color:black;width:20%">
<asp:DataList ID="DataList1" runat="server" DataKeyField="ID">
<ItemTemplate>
JobSite:
<asp:Label ID="JobSiteLabel" runat="server" Text='<%# Eval("JobSite") %>' Font-Size="Larger" Font-Bold="true" />
<br />
JobDate:
<asp:Label ID="JobDateLabel" runat="server" Text='<%# Eval("JobDate", "{0:yyyy-MM-dddd}") %>' />
<br />
Foreman:
<asp:Label ID="ForemanLabel" runat="server" Text='<%# Eval("Foreman") %>' />
<br />
Description:
<asp:Label ID="DescriptionLabel" runat="server" Text='<%# Eval("Description") %>' />
<br />
<br />
</ItemTemplate>
</asp:DataList>
<br />
</div>
<br />
<div style="float:left">
<asp:GridView ID="GridView1" runat="server" AutoGenerateColumns="False"
DataKeyNames="ID" CssClass="table borderhide" Width="30%">
<Columns>
<asp:TemplateField HeaderText="Description">
<ItemTemplate>
<asp:TextBox id="txtDesc" runat="server" Text='<%# Eval("ItemDescription") %>' />
</ItemTemplate>
</asp:TemplateField>
<asp:TemplateField HeaderText="Qty">
<ItemTemplate>
<asp:TextBox id="txtQty" runat="server" Text='<%# Eval("Qty") %>' TextMode="Number" />
</ItemTemplate>
</asp:TemplateField>
<asp:TemplateField HeaderText="Used By">
<ItemTemplate>
<asp:TextBox id="txtUsedBy" runat="server" Text='<%# Eval("UsedBy") %>' />
</ItemTemplate>
</asp:TemplateField>
</Columns>
</asp:GridView>
<div style="float:right">
<asp:Button ID="cmdAdd" runat="server" Text="Add Item" CssClass="btn" style="margin-top:-20px" />
</div>
</div>
<div style="clear:both"></div>
<asp:Button ID="cmdSave" runat="server" Text="Save" CssClass="btn" />
<asp:Button ID="cmdCancel" runat="server" Text="Cancel" CssClass="btn" style="margin-left:40px"/>
</div>
Ok, and the code to load this up is this:
We load up the main record - just for display, and then the GridView of child items.
Dim rstJobItems As New DataTable
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
If Not IsPostBack Then
ViewState("JobID") = Session("JobId")
LoadData()
Else
rstJobItems = ViewState("JobItems")
End If
End Sub
Sub LoadData()
DataList1.DataSource = MyRst("SELECT * from JobSites WHERE ID = " & ViewState("JobID"))
DataList1.DataBind()
rstJobItems = MyRst("SELECT * FROM JobItems where Job_ID = " & ViewState("JobID"))
GridView1.DataSource = rstJobItems
GridView1.DataBind()
ViewState("JobItems") = rstJobItems
End Sub
Ok, so now on the first page, when we click on a row, we jump to the 2nd page, and we see/have this:
So we need to wire up the button to add a new row.
That code looks like this:
Protected Sub cmdAdd_Click(sender As Object, e As EventArgs) Handles cmdAdd.Click
' user might have done some edits
GridToTable()
' create a new row
Dim NewRow As DataRow = rstJobItems.NewRow
NewRow("Job_id") = ViewState("JobID")
NewRow("Qty") = 0
rstJobItems.Rows.Add(NewRow)
GridView1.DataSource = rstJobItems
GridView1.DataBind()
End Sub
So, say in above, I click the add row, then you will see this:
(in fact I clicked it two times).
Ok, so you are free to tab around - edit data in that grid. You can edit/change existing rows, or add a new row and simple tab around and enter data.
So, now lets wire up the Save button. That save button has to allow and deal with editing rows - and also saving edits - all in ONE shot to the database.
The code for save button is thus this:
Protected Sub cmdSave_Click(sender As Object, e As EventArgs) Handles cmdSave.Click
GridToTable()
Using conn As New SqlConnection(My.Settings.TEST4)
Using cmdSQL As New SqlCommand("SELECT * FROM JobItems WHERE ID = 0", conn)
Dim da As New SqlDataAdapter(cmdSQL)
Dim daU As New SqlCommandBuilder(da)
da.Update(rstJobItems)
End Using
End Using
' data add, and any edit now saved - return to our job site listing page
Response.Redirect("JobSites.aspx")
End Sub
So we send grid to table, and then in ONE database update, send the table back to the database.
Of course after we save, we return back to our first page with the grid now read to edit more.
So, the other routine used in above was sending the Grid (rows) back to the table.
That code is this:
Sub GridToTable()
For Each gRow As GridViewRow In GridView1.Rows
Dim OneRow As DataRow = rstJobItems.Rows(gRow.RowIndex)
OneRow("ItemDescription") = CType(gRow.FindControl("txtDesc"), TextBox).Text
OneRow("Qty") = CType(gRow.FindControl("txtQty"), TextBox).Text
OneRow("UsedBy") = CType(gRow.FindControl("txtUsedBy"), TextBox).Text
Next
End Sub
And of course the cancel button? Well, if you add some rows, or just edit the rows, and hit cancel? Well, we don't save - but just return to the previous page.
eg:
Protected Sub cmdCancel_Click(sender As Object, e As EventArgs) Handles cmdCancel.Click
Response.Redirect("JobSites.aspx")
End Sub
And last but not least, I have a "general" helper routine that simple returns a data table - and I used it several times in above. That routine kind of gives me a FoxPro or MS-Access like ability to get a data table with great ease (in place of typing that same code over and over).
That routine was this:
Public Function MyRst(strSQL As String) As DataTable
Dim rstData As New DataTable
Using conn As New SqlConnection(My.Settings.TEST4)
Using cmdSQL As New SqlCommand(strSQL, conn)
conn.Open()
rstData.Load(cmdSQL.ExecuteReader)
End Using
End Using
Return rstData
End Function
So the above is quite much start to finish. It is of course quick code, and done rather fast/quick for stack overflow. But it is full of great ideas, and as noted, I let the wizards generate most of the markup. (but then remove the data source from the GV, and also the Data source item that gets created in the page.
So in summary:
I did not write most of that markup - I let the wizards generate most of it.
Note how SMALL and relative simple the code bits were. we broke up this into bite sized parts - thus each routine is not a lot of code.
We ALSO let .net built the sql update (and insert) commands for us. this eliminated BOAT LOADS of parameters, and BOATLOADS of work to wire that up. And it also MUCH better for performance, since ado.net is smart - for rows not changed - it don't send the updates to sql server.
Note also how ONE simple update method of
da.Update(rstJobItems)
Handles BOTH edits/updates, an inserts with one command. And we probably could/should drop in a delete row button - and the update would also handle that for us too!!!
So, there is a LOT of great ideas in above, but key concepts are:
Leverage as much as you can the wizards, and built in controls.
Leverage the ability of ado.net to update a table of edits in ONE shot back to the database - and as above shows, SAME single update also works for both inserts and updates at the same time.
I call stored procedure and then store given data in this datatable variable
DataTable data = CallToSP();
In this DataTable there are 3 columns -> CustomerId, CustomerName, CustomerSecondaryId;
I am displaying two of them -> CustomerId and CustomerName.
My goal is to make a hidden field in .aspx file and get it's value from CodeBehind.
What's unclear to me is that I do not understand how data is mapped.
Project I'm currently working on uses TelerikUI. It has RadGrid.
RadGridViewCustomer.DataSource = data;
RadGridViewCustomer.DataBind();
foreach (GridDataItem dataItem in RadGridViewCustomer.MasterTableView.Items)
{
PutDataInGridView(dataItem); //At some point in this method, I need to take CustomerSecondaryId
}
I tried to put this in aspx but it says that it's not a known element
<telerik:GridClientSelectColumn UniqueName="CustomerSecondaryId" Visible="false" />
Planned to get data from CodeBehind like this :
string customerSecondaryId = dataItem["CustomerSecondaryId"].Text;
As a result I am getting "  ;" every time for each dataItem.
I do not want to use DataRowCollection. I only want to put data in hidden field and get it's value when needed. Any ideas?
Well I not used the telerick grid, but I am betting that it follows both GridView, and listview.
And VERY common goal is to display some data, but NOT have to expose the PK id from the database.
However, GridView and Listview BOTH have built-in provisions for this need (easy of getting, grabbing the PK row of data, but NOT exposing that PK ID.
The asp.net repeater does NOT have this desired ability, but there are some nice cool tricks to get the PK or row ID from a repeater, and again not having to send the PK id to the client side, nor expose it.
So, lets do a really simple example
SELECT ID, HotelName, City, Province, Description, Active
So, we want to display (send) to say client side the HotelName, City, Province but say we click on a row, and want to get the hotel row PK id? (quite common need).
So, we drop in a grid, and have this markup:
(and lets drop is a plane jane asp.net button for the row click).
(and I actually perfer listview since it allows plane jane asp.net controls for the grid without that silly "template" tag set - but lets just go with GridView.
So, we have this markup:
<div style="width:50%;padding:25px">
<asp:GridView ID="GridView1" runat="server" AutoGenerateColumns="False"
DataKeyNames="ID" CssClass="table">
<Columns>
<asp:BoundField DataField="HotelName" HeaderText="HotelName" ItemStyle-Width="200" />
<asp:BoundField DataField="City" HeaderText="City" />
<asp:BoundField DataField="Province" HeaderText="Province" />
<asp:BoundField DataField="Description" HeaderText="Description" />
<asp:CheckBoxField DataField="Active" HeaderText="Active" />
<asp:TemplateField HeaderText="Select">
<ItemTemplate>
<asp:Button ID="cmdRowFun" runat="server" Text="Row Click" OnClick="cmdRowFun_Click" />
</ItemTemplate>
</asp:TemplateField>
</Columns>
</asp:GridView>
</div>
And this code to fill the grid:
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 cmdSQL As _
New SqlCommand("SELECT ID, HotelName, City, Province, Description, Active FROM tblHotels " &
"ORDER BY HotelName",
New SqlConnection(My.Settings.TEST4))
cmdSQL.Connection.Open()
GridView1.DataSource = cmdSQL.ExecuteReader
GridView1.DataBind()
End Using
End Sub
output:
Ok, so now the row click button:
Protected Sub cmdRowFun_Click(sender As Object, e As EventArgs)
Dim btn As Button = sender
Dim gRow As GridViewRow = btn.Parent.Parent
Dim intPK As Integer
intPK = GridView1.DataKeys(gRow.RowIndex).Item("ID")
Debug.Print("Row clicked = " & gRow.RowIndex)
Debug.Print("PK Value = " & intPK)
Debug.Print("Hotel = " & gRow.Cells(0).Text)
End Sub
Output for a row click:
Row clicked = 1
PK Value = 72
Hotel = Banff Rocky Mountain Resort
So, we get the Grid row clicked. We can now launch a form, or whatever and pull that whole record say for editing a form or whatever.
So, we get the row clicked on.
From that we can get the row index.
From that we get/grab from the control the DataKeys with row index and thus get the PK value.
Note VERY careful the setting called DataKeyNames.
So, we NEVER had to expose the PK client side.
You can certainly include a hidden field, or even make up (fake) attribute of the button, and do this:
<asp:Button ID="cmdRowFun" runat="server" Text="Row Click"
OnClick="cmdRowFun_Click"
MyPK = '<%# Eval("ID") %>'
/>
So, now we get PK with:
Dim btn As Button = sender
Debug.Print("Pk value = " & btn.Attributes.Item("MyPK").ToString)
So, you can add the PK value to buttons, labels or even just a hiddenfield in the grid. And you can even add the attribute to the button I dropped into the grid.
Now, of course in that last example, then you ARE exposing the PK id to the client side browser. I suppose in some cases, this might not matter, but I as a general rule try to avoid outright exposing or having database PK id exposed or even required in the client side markup. It is is a big security hold you should avoid.
You have to check the telerick docs, but I suspect they have some kind of DataKeys collection that is part of their grid control, since both GridView, ListView has this feature - and it designed exactly for your question and problem.
That problem is to display data, but not show the PK value, and in fact not even have to include it in the mark-up that is rendered to the browser client side.
I have 2 GridViews with separate IDs
I need the backend code to update the one being viewed when a button is clicked.
` Protected Sub savestatus(sender As Object, e As EventArgs)
Dim btn As Button = TryCast(sender, Button)
Dim row As GridViewRow = CType(((CType(sender, Button)).NamingContainer), GridViewRow)
Dim rowindex As Integer = row.RowIndex
Dim code As String = GridView1.DataKeys(row.RowIndex).Values(0).ToString()
Dim type As Int32 = GridView1.DataKeys(row.RowIndex).Values(1)
Dim statusid As Integer
Dim checkLocked, checkerror As CheckBox
' For Each row As GridViewRow In GridView1.Rows
checkLocked = CType(GridView1.Rows(rowindex).FindControl("lock"), CheckBox)
checkerror = CType(GridView1.Rows(rowindex).FindControl("error"), CheckBox)
If checkerror.Checked Then ' error
statusid = 2
End If
If checkLocked.Checked Then
statusid = 3
End If`
How do I make the GridView1 a variable depending on which grid view the button is pressed in.
Ok, it would have helped a lot to at least show the button and a few rows of the gridview markup.
There are about 10 ways to do this. (really !!!).
However, in your case, two check boxes, and you need actions to occur when a check box is changed – AND say change the other one!!
Now I am using two check boxes – but it could be a text box or whatever I change.
So, say I have this grid markup
Some columns + TWO un-bound check boxes.
<asp:GridView ID="GridView1" runat="server" AutoGenerateColumns="false">
<Columns>
<asp:BoundField DataField="ID" HeaderText="ID" />
<asp:BoundField DataField="HotelName" HeaderText="HotelName" />
<asp:BoundField DataField="City" HeaderText="City" />
<asp:BoundField DataField="Province" HeaderText="Province" />
<asp:TemplateField HeaderText="Good">
<ItemTemplate>
<asp:CheckBox ID="chkGood" runat="server"
AutoPostBack="true"
OnCheckedChanged="chkGood_CheckedChanged"
MyRowID ='<%# Container.DataItemIndex %>' />
</ItemTemplate>
</asp:TemplateField>
<asp:TemplateField HeaderText="Bad">
<ItemTemplate>
<asp:CheckBox ID="chkBad" runat="server"
AutoPostBack="true"
OnCheckedChanged="chkBad_CheckedChanged"
MyRowID ='<%# Container.DataItemIndex %>' />
</ItemTemplate>
</asp:TemplateField>
</Columns>
</asp:GridView>
Ok, and now the code to load the grid:
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
If IsPostBack = False Then
Using cmdSQL As New SqlCommand("SELECT ID, HotelName, City, Province from tblHotels",
New SqlConnection(My.Settings.Test3))
cmdSQL.Connection.Open()
GridView1.DataSource = cmdSQL.ExecuteReader
GridView1.DataBind()
End Using
End If
End Sub
And thus we have this:
Ok, so far - very simple.
Now note CLOSE at the markup for the two check boxes.
And while dropping a button or whatever on a normal form - you can then double click to JUMP to the code behind event/stub?
Well, for buttons or whatever you drop INSIDE of a grid, you can't double click on the control to create + jump to the code behind stub.
But, WHILE in the markup, you can start typing the event, and you get this:
Note VERY careful how the intel-sense pops up a option to create the event. So click on that option. Nothing seems to happen, but NOW we get a code stub behind.
So, we have this code stub for the chkOk event:
Protected Sub chkGood_CheckedChanged(sender As Object, e As EventArgs)
Dim ckBox As CheckBox = sender
Dim RowID As Integer = ckBox.Attributes.Item("MyRowID")
Dim gvRow As GridViewRow = GridView1.Rows(RowID)
If ckBox.Checked = True Then
' do whatever if true - say un-check the "bad" check box
Dim ckBoxBad As CheckBox = gvRow.FindControl("chkBad")
ckBoxBad.Checked = False
Else
' code here if the user just un-checked the "good" check box
End If
End Sub
Note a few things:
We pick up the button click - then shove it into a checkbox control. This is just a lot easier to get the check box value, and our CUSTOM MyRowID
(and this works if it was a button for example).
We then get the custom made up Attribute we added called "MyRowID"
MyRowID ='<%# Container.DataItemIndex %>'
You can see the expression in the Markup - it passes the current row id. Sometimes, I'll pass other values from the row and you can do this like this:
<asp:CheckBox ID="chkBad" runat="server"
AutoPostBack="true"
OnCheckedChanged="chkBad_CheckedChanged"
MyRowID ='<%# Container.DataItemIndex %>'
MyPKID = '<%# Eval("ID") %>' />
So in above, I pass both RowID and a custom MyPKID (so the Eval() expression can be used to pass any valid data row avaialble at binding time. Its often handy then having to grab and mess with a data row - you JUST grab the button from sender - and you don't care about gridview or anything else to get a few extra values. (just a FYI tip). So for example, I REALLY don't want the PK row id as the first row. So I could remove it and STILL use the above idea to PASS the pk row id - all columns can be used - even if a control is NOT in the grid - as long as the column exists during the data binding process - you can grab it.
So, now we pick up the current GridRow - and we are free to modify whatever we want on that row.
In my simple example, we pick up the OTHER check box - and un-check if it was checked. But we could say update other things on that row.
And I did the same for the chkBad check box. And I have really the same as the first chkBox code stub. Eg this:
Protected Sub chkBad_CheckedChanged(sender As Object, e As EventArgs)
Dim ckBox As CheckBox = sender
Dim RowID As Integer = ckBox.Attributes.Item("MyRowID")
Dim gvRow As GridViewRow = GridView1.Rows(RowID)
If ckBox.Checked = True Then
' user checked the bad box, un-check the good one
Dim ckBoxGood As CheckBox = gvRow.FindControl("chkGood")
ckBoxGood.Checked = False
Else
' code here if the user just un-checked the "bad" check box
End If
End Sub
So in above we just hard right past the GridView bult in events.
So in above, if you check one box and the other is checked - we un-check it. Needless to say, I would use a button list, or a checkbox list, and that above code would of course then not be required. But it still a good example on how to pluck/get the current row. And then get/pluck controls from that row.
Note that for the first 3 rows (the databound), you can NOT use findControl, and they are referenced using the gvRow.Cells(0) (starting at 0 to N columns. So no findcontrol is required for these databound columns or autogenerated ones. They do NOT have a name - you have to use number starting at 0 in the cells collection. Of course for "templated" controls that we added as per above? Then you do in fact use findcontrol as per above.