Asked by:
GridView would not allow user to add row

Question
-
User1216627406 posted
Greetings everyone.
Hope everyone is staying safe.
I have several GridView control IDs, GridView1 through Gridview8.
Users can enter data into any of the GridView controls and if needed, click button to add a new row.
This works fine.
The issue is that assume that a user enters data into three rows of GridView1 but no data entered for the rest of the GridView controls, when rendered, the form shows three rows for the rest of the controls.
In other words, if any control has one row of data, the rest of the controls show one row of data. If any control has two or more rows of data, the rest of the controls, even though have no data, will show as many rows as the row of the control that has data.
To keep things short, I am posting the following screenshot that shows three control IDs.
As you can see from the above screenshot, GridView2 has three rows of data and GridView1 and GridView3 also shows three rows of data even though the rows are empty.
We need only one row of data for any GridView control that is empty.
To fix this, I added some code to RowDataBound. as shown below:
'//Markup:
<asp:GridView ID="Gridview1" runat="server" AutoGenerateColumns="false" OnRowDataBound="Gridview1_RowDataBound"> <Columns> <asp:BoundField DataField="RowNumber" Visible="false" HeaderText="Row Number" /> <asp:TemplateField HeaderText="Name"> <HeaderStyle HorizontalAlign="Left" /> <ItemTemplate> <asp:TextBox ID="txtsourcename" Text='<%# Eval("sourcename") %>' placeholder="Name...(e.g, Jane Doe)" runat="server" Style="width: 375px;" AutoPostBack="true" class="form-control textClass"></asp:TextBox><br /> <asp:CheckBox ID="grid1Details" ClientIDMode="Static" runat="server" Checked="false" AutoPostBack="true" /><span style="color: #ff0000">*Check this box if N/A</span> </ItemTemplate> </asp:TemplateField> <asp:TemplateField HeaderText="Address"> <ItemStyle HorizontalAlign="Left"></ItemStyle> <ItemTemplate> <asp:TextBox ID="txtsourceaddress" Text='<%# Eval("sourceaddress") %>' placeholder="Address..." runat="server" Style="width: 375px;" class="form-control textClass"></asp:TextBox><br /> <br /> </ItemTemplate> </asp:TemplateField> <asp:TemplateField HeaderText=""> <ItemTemplate> <asp:Button ID="ButtonAdd" runat="server" Text="Add another row if needed" CssClass="grvAddButton" /><br /> <br /> <br> </ItemTemplate> </asp:TemplateField> <asp:TemplateField HeaderText=""> <ItemTemplate> <asp:Button ID="sourceDelete" runat="server" Text="Delete" CommandName="Delete" CssClass="grvDelButton" OnClientClick="return confirm('Are you sure you want to remove this row?')" /> </ItemTemplate> </asp:TemplateField> </Columns> </asp:GridView>
'//VB
Protected Sub Gridview1_RowDataBound(sender As Object, e As GridViewRowEventArgs) Handles Gridview1.RowDataBound If e.Row.RowType = DataControlRowType.DataRow Then 'BEGIN: Show only one row of data if row is empty If e.Row.RowIndex > 0 Then Dim sourceName As String = (TryCast(e.Row.FindControl("txtsourcename"), TextBox)).Text Dim txtSourceAddress As String = (TryCast(e.Row.FindControl("txtsourceaddress"), TextBox)).Text If String.IsNullOrEmpty(sourceName) AndAlso String.IsNullOrEmpty(txtSourceAddress) Then e.Row.Visible = False Else e.Row.Visible = True End If End If 'END: Show only one row of data if row is empty If e.Row.RowIndex = -1 Then DirectCast(e.Row.FindControl("sourceDelete"), Button).Visible = True Else If Convert.ToInt32(ViewState("rowIndex" & 0)) = e.Row.RowIndex Then DirectCast(e.Row.FindControl("sourceDelete"), Button).Visible = False End If End If End If End Sub
This works except that now, if user needs to add an additional row and clicks the 'Add another if needed' button, the additional row is not being added.
Any ideas how to fix this?
Many thanks in advance
Wednesday, May 6, 2020 1:52 PM
All replies
-
User475983607 posted
Data bound controls reflect the number of rows found in the bound data source. It's not clear how the 3 data sources are generated but most likely finding where the data sources are populated is the key to fixing this issue. Fix the data source and the GridViews will follow.
You approach toggles the visibility of the GridView rows. Basically you're the UI not the business logic behind generating the data source. That's why the code does not function as expected.
I recommend simplifying the design. Stop caching data in ViewState and use the DB tables. Move the user inputs outside the GridViews. When the user clicks "Add another row if needed", save the inputs into a table. Bind the table results to a GridView. No you can use all the of the standard GridView features; delete, edit, update. Plus you'll have less code to maintain and you are not burdened with managing ViewState.
Wednesday, May 6, 2020 2:42 PM -
User1216627406 posted
I think you are oversimplifying the problem, unfortunately.
Although, I did not use this link to build it but it follows the same design and logic.
I am using the DB tables as well as ViewState to store the loops.
The controls I alluded to are populated from the database but like I said in my original post, because not all controls are populated with data (from the database), they are obviously empty as they should be except that they are mimicking the behavior of controls with data.
The controls with empty rows display however many rows that the controls with data display.
Ok, thanks for your help.
Wednesday, May 6, 2020 4:24 PM -
User475983607 posted
I would approach the problem as shown below. The concept is pretty simple. Two input fields and a button go with a GridView. Clicking the button inserts the row into the in the GridView. From here you can add the delete, edit, update to GridView. Also this can be turned into a UserControl rather easily.
Public Class IncomeSource Public Property IncomeSourceId As Integer Public Property Name As String End Class Public Enum IncomeSourceType Employee = 1 Spouse = 2 IntersetDividens = 3 End Enum
<%@ Page Title="" Language="vb" AutoEventWireup="false" MasterPageFile="~/Site.Master" CodeBehind="default.aspx.vb" Inherits="WebFormsVBDemo._default1" %> <asp:Content ID="Content1" ContentPlaceHolderID="MainContent" runat="server"> <div> <asp:TextBox ID="EmployeeIncomeName" runat="server"></asp:TextBox> <asp:TextBox ID="EmployeeIncomeAddress" runat="server"></asp:TextBox> <asp:Button ID="InsertEmployeeIncome" runat="server" Text="Add another row if needed" OnClick="InsertEmployeeIncome_Click" /> </div> <div> <asp:GridView ID="EmployeeIncomeGridView" runat="server"></asp:GridView> </div> </asp:Content>
Imports System.Data.SqlClient Public Class _default1 Inherits System.Web.UI.Page Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load If Not Page.IsPostBack Then BindEmployeeIncomeGridView() End If End Sub Protected Sub InsertEmployeeIncome_Click(sender As Object, e As EventArgs) InsertIncomeType(EmployeeIncomeName.Text, EmployeeIncomeAddress.Text, IncomeSourceType.Employee) BindEmployeeIncomeGridView() EmployeeIncomeName.Text = String.Empty EmployeeIncomeAddress.Text = String.Empty End Sub Protected Sub BindEmployeeIncomeGridView() EmployeeIncomeGridView.DataSource = GetIncomeByType(IncomeSourceType.Employee) EmployeeIncomeGridView.DataBind() End Sub Protected Function GetIncomeByType(incomeTypeId As Integer) As List(Of IncomeSource) Dim results As List(Of IncomeSource) = New List(Of IncomeSource) Dim connectionString As String = ConfigurationManager.ConnectionStrings("DefaultConnection").ConnectionString Dim queryString As String = "SELECT IncomeSourceId, [Name], [Address], IncomeTypeId FROM dbo.IncomeSource WHERE IncomeTypeId = @IncomeTypeId" Using connection As SqlConnection = New SqlConnection(connectionString) Dim Command As SqlCommand = New SqlCommand(queryString, connection) Command.Parameters.AddWithValue("@IncomeTypeId", incomeTypeId) connection.Open() Dim reader As SqlDataReader = Command.ExecuteReader() While reader.Read() Dim item = New IncomeSource item.IncomeSourceId = reader.GetInt32(0) item.Name = reader.GetString(1) results.Add(item) End While reader.Close() End Using Return results End Function Protected Sub InsertIncomeType(Name As String, Address As String, IncomeType As Integer) Dim connectionString As String = ConfigurationManager.ConnectionStrings("DefaultConnection").ConnectionString Dim queryString As String = "INSERT INTO dbo.IncomeSource ([Name], [Address], IncomeTypeId) VALUES(@Name, @Address, @IncomeType)" Using connection As SqlConnection = New SqlConnection(connectionString) Dim Command As SqlCommand = New SqlCommand(queryString, connection) Command.Parameters.AddWithValue("@Name", Name) Command.Parameters.AddWithValue("@Address", Address) Command.Parameters.AddWithValue("@IncomeType", IncomeType) connection.Open() Command.ExecuteNonQuery() connection.Close() End Using End Sub End Class
Wednesday, May 6, 2020 7:25 PM -
User-1330468790 posted
Hi simflex,
I think the problem might be located in the button "Add another row if needed" since you use this button to add new row.
According to the URL you posted, this button is associated with an event handler "AddNewRowToGrid" which will add a new row to the GridView when a Button is clicked. The point is that your problem is related to the new added row.
Could you please share the more codes to allow me to find more details?
I understand that the reason why you have this design is that you want to update the database using these grid view controls. The values will be temporarily stored in the ViewState and will be stored in the database when you submit all of the grid views. (There might be a "submit-change" button or similar stuff in the page).
If so, this design is reasonable as long as you keep the values correctly and don't lose them.
Best regards,
Sean
Thursday, May 7, 2020 8:42 AM -
User1216627406 posted
Thank you very much Sean.
I was confident the code was designed correctly as is. No change is necessary.
Everything was working great until I modified the RowDataBound method.
With this code below, everything was working great:
Protected Sub Gridview1_RowDataBound(sender As Object, e As GridViewRowEventArgs) Handles Gridview1.RowDataBound If e.Row.RowType = DataControlRowType.DataRow Then If e.Row.RowIndex = -1 Then DirectCast(e.Row.FindControl("sourceDelete"), Button).Visible = True Else If Convert.ToInt32(ViewState("rowIndex" & 0)) = e.Row.RowIndex Then DirectCast(e.Row.FindControl("sourceDelete"), Button).Visible = False End If End If End If End Sub
It was after I added the below code just to ensure empty rows don't display that I started having problem adding new row:
If e.Row.RowIndex > 0 Then Dim sourceName As String = (TryCast(e.Row.FindControl("txtsourcename"), TextBox)).Text Dim txtSourceAddress As String = (TryCast(e.Row.FindControl("txtsourceaddress"), TextBox)).Text If String.IsNullOrEmpty(sourceName) AndAlso String.IsNullOrEmpty(txtSourceAddress) Then e.Row.Visible = False Else e.Row.Visible = True End If End If
And per your request, here is the AddNewRow method:
Protected Sub ButtonAdd_Click(sender As Object, e As EventArgs) AddNewRowToGrid() End Sub Private Sub AddNewRowToGrid() Dim rowIndex As Integer = 0 If ViewState("CurrentTable") IsNot Nothing Then Dim dtCurrentTable As DataTable = DirectCast(ViewState("CurrentTable"), DataTable) Dim drCurrentRow As DataRow = Nothing If dtCurrentTable.Rows.Count > 0 Then For i As Integer = 1 To dtCurrentTable.Rows.Count 'extract the TextBox values Dim txtsoname As TextBox = DirectCast(Gridview1.Rows(rowIndex).Cells(1).FindControl("txtsourcename"), TextBox) Dim txtsoaddress As TextBox = DirectCast(Gridview1.Rows(rowIndex).Cells(2).FindControl("txtsourceaddress"), TextBox) drCurrentRow = dtCurrentTable.NewRow() drCurrentRow("RowNumber") = i + 1 dtCurrentTable.Rows(i - 1)("sourcename") = txtsoname.Text dtCurrentTable.Rows(i - 1)("sourceaddress") = txtsoaddress.Text rowIndex += 1 Next dtCurrentTable.Rows.Add(drCurrentRow) ViewState("CurrentTable") = dtCurrentTable Gridview1.DataSource = dtCurrentTable Gridview1.DataBind() End If Else Response.Write("ViewState is null") End If 'Set Previous Data on Postbacks SetPreviousData() End Sub
Thank you very much.
Thursday, May 7, 2020 1:21 PM -
User475983607 posted
simflex
Everything was working great until I modified the RowDataBound method.Can you clarify? I though the changes were made to RowDataBound event because the design was not working as expected. I thought the problem was when one GridView contained 3 rows, the other GridViews show 3 empty rows. The fix sets the GridView row visibility to false when the inputs are empty but fix has side affects too.
Data bound controls like the GridView display the same number of records as the data source. The fact the GridViews share the same number of rows indicates a bug somewhere in the data source logic. I assume each grid should have a variable number of rows independent of the other grids. Setting the visibility seems more like a hack than fixing the problem.
The AddNewRwoToGrid() method shows the GridView as a data source a DataTable.. That's not a good idea. A GridView is a UI element not a data source. The DataTable(s) should be the data source and drive the GridView.
I think the code changes to toggle row visibility compounds the problem. The Web Forms framework does not render server controls when Visibility attribute is set to false. This updates ViewState which might cause downstream issues. For example, a hypothetical. Let say a GridView is populated with 5 records during the Page_load event. Every post back from that point on will show 5 GridView records. This behavior is out-of-the-box Web Forms state management. Let's say another event sets the visibility on the last 2 GridView rows to false. Now every post back will have 3 GridView records. If there is a dependency on ViewState, then this can be a problem.
What could be happening is there is bug elsewhere in the code related to the GridView data sources. Toggling visibility makes everything worse because the the system of record is the GridView. It's like a circular update reference.
One thing you can try is setting the row CSS display to none rather than setting visibility. The row still exists in the markup but it's not displayed. I'm not convinced {display:none;} this will fix the actual problem. I think you'll continue to run into issues until you figure why the GridViews share the same number of rows. Fix that and you can move on.
Thursday, May 7, 2020 7:52 PM -
User1216627406 posted
mgebhard, can I clarify?
Sure of course.
But let start with the premise that One thing you can try is setting the row CSS display to none rather than setting visibility.
I think this holds true and would work if we are dealing with just one GridView control like GridView1.
In my original post, I indicated that I was working with there are 7 GridView controls, GridView1 through GridView7.
Each gives the user the ability to add new rows dynamically.
So, I do not believe there is a bug in the original code. There maybe one since the modification.
Anyway, the app has been working now since 2016.
Currently, the way it works is that you enter your userID and the system checks whether that was first time you are accessing the system.
If it is, then it shows blank form so you can complete entire.
If you had used the form the previous year(s), it populates your personal information like name, title, email, and that userID you just entered, asks you to verify the accuracy of those personal info and then complete rest of form.
If you completed the form this year, it tells you that you that you had already submitted the form and can only submit the form once a year.
This year, they are about to start completing the form again for 2020 and I have been asked to modify the app so that once you enter your ID, and you have completed this form the previous year, to populate all the information you entered from previous year so you don't have to enter them again unless you wish to make changes to them.
That's what led to the changes I made and that's why I am having problems now.
To answer your question that only rows will data can only be displayed, yes this is true if you are dealing with just one GridView control eg GridView1 and since I have 7 of them, and to populate the entire form, I have to do joins, that's what is causing the empty rows to display on controls where there is no data on the database.
I have run the stored proc over to several people and I was told it was done correctly.
I can post it here again because if there is to be any bug, it has to come from it.
Thursday, May 7, 2020 8:46 PM -
User475983607 posted
But let start with the premise that One thing you can try is setting the row CSS display to none rather than setting visibility.
I think this holds true and would work if we are dealing with just one GridView control like GridView1.
In my original post, I indicated that I was working with there are 7 GridView controls, GridView1 through GridView7.
I don't understand your response. Why can't you replace the row visibility logic with setting the CSS? If the Visibility logic works then I do not understand why setting the display property is any different.
Secondly, the empty GridView rows are expected and this page has behaved this way since 2016? I though the empty row were unwanted?
I have run the stored proc over to several people and I was told it was done correctly.
I can post it here again because if there is to be any bug, it has to come from it.
What proc? Can you share the code?
Thursday, May 7, 2020 10:01 PM -
User1216627406 posted
I don't understand your response. Why can't you replace the row visibility logic with setting the CSS? If the Visibility logic works then I do not understand why setting the display property is any different.
Yea, you have a good point there. This issue though is not the visibility is not being set to hidden. The issue is that when I click to add a new row, it does not add one/
In other words, even though setting the visibility to none works, when attempting to click the button to add a new row, it is not doing that.
So, I can try your suggestion but not sure if that solves the click button problem.
Secondly, the empty GridView rows are expected and this page has behaved this way since 2016? I though the empty row were unwanted?
No, what I meant was that the page was working perfectly until I made the change.
In other words, normally, you get one empty row by default and you could add additional rows if needed and that row would be added. It has been working that way until I made the change in the event click method that I posted today. Then it stopped adding the additional rows.
I will post the stored proc shortly. It is on my other laptop.
Here is the stored procedure:
I am sure you understand what it is doing but for vanity, it checks three things:
1, when a user attempts to submit a form, has this user submitted the form this year already? 0 for no, 1 yes
2, did this user submit this form last year? 0 for no, 1 for yes
3, did this year submit this form previous years (not this year, not last year but any year before last year? 0 for no, 1 for years.
On my VB, if thisYear = 1, then the user submitted this form already; cannot submit again. Let the user know this with a message.
This works great.
If user submitted this form last year or year before last, grab the record s/he submitted and populate the gridview form.
This is where the issue comes in.
If the user did the form last year or the year before and his/her record populates the gridview and one of the controls has more than one row, then the rest have more than one row whether empty or not.
ALTER PROCEDURE [dbo].[uspGetRecs] @empID nvarchar(50) AS begin SELECT s.sourcename, s.sourceaddress,d.dateCreated,sp.spousename, sp.spouseaddress, r.reimbursementName,r.reimbursementAddress, h.HonorariaName,h.HonorariaAddress, h.HonorariaName as honoraria,h.HonorariaAddress,h.HonorariaIncome,g.giftName,g.giftAddress,dv.dividentName,dv.dividentAddress, o.orgName,o.orgAddress,cr.creditorName, cr.creditorAddress, CASE WHEN d.dateCreated < prev_yr_jan01 THEN 1 ELSE 0 END AS pastYears, CASE WHEN d.dateCreated >= prev_yr_jan01 AND d.dateCreated < curr_yr_jan01 THEN 1 ELSE 0 END as previousYear, CASE WHEN d.dateCreated >= curr_yr_jan01 THEN 1 ELSE 0 END as thisYear FROM Employees e CROSS APPLY ( SELECT DATEADD(YEAR, DATEDIFF(YEAR, 0, GETDATE()), 0) AS curr_yr_jan01, DATEADD(YEAR, DATEDIFF(YEAR, 0, GETDATE()) - 1, 0) AS prev_yr_jan01 ) AS yr_start_dates INNER JOIN SourceDetails s ON e.EmployeeID = s.EmployeeID INNER JOIN SpouseDetails sp ON e.EmployeeID = sp.employeeID INNER JOIN DividentDetails dv ON e.EmployeeID = dv.EmployeeID INNER JOIN ReimbursementDetails r ON e.EmployeeID = r.employeeID INNER JOIN Honoraria h ON e.EmployeeID = h .EmployeeID INNER JOIN GiftDetails g ON e.EmployeeID = g.employeeID INNER JOIN dateDetails d ON e.EmployeeID = d.employeeID INNER JOIN org o ON e.employeeID = o.employeeID INNER JOIN creditorDetails cr ON e.employeeID = cr.employeeID WHERE e.EmpID=@empID ORDER By d.dateCreated DESC end
As you can see, 8 tables are being joined by employeeID since that is the common key amongst them.
Sometimes, I wonder if I just do this check one table at a time instead of joining them, would that have made any difference?
Reason I have not tried this theory is not sure how to use the stored proc on my VB
Thanks
Friday, May 8, 2020 2:57 AM -
User-1330468790 posted
Hi simflex,
Thanks for your further codes.
However, I think the codes should be working as what you expected except one point as below codes in yellow background.
simflex
Protected Sub ButtonAdd_Click(sender As Object, e As EventArgs) AddNewRowToGrid() End Sub Private Sub AddNewRowToGrid() Dim rowIndex As Integer = 0 If ViewState("CurrentTable") IsNot Nothing Then Dim dtCurrentTable As DataTable = DirectCast(ViewState("CurrentTable"), DataTable) Dim drCurrentRow As DataRow = Nothing If dtCurrentTable.Rows.Count > 0 Then For i As Integer = 1 To dtCurrentTable.Rows.Count 'extract the TextBox values Dim txtsoname As TextBox = DirectCast(Gridview1.Rows(rowIndex).Cells(1).FindControl("txtsourcename"), TextBox) Dim txtsoaddress As TextBox = DirectCast(Gridview1.Rows(rowIndex).Cells(2).FindControl("txtsourceaddress"), TextBox) drCurrentRow = dtCurrentTable.NewRow() drCurrentRow("RowNumber") = i + 1 dtCurrentTable.Rows(i - 1)("sourcename") = txtsoname.Text dtCurrentTable.Rows(i - 1)("sourceaddress") = txtsoaddress.Text rowIndex += 1 Next dtCurrentTable.Rows.Add(drCurrentRow) ViewState("CurrentTable") = dtCurrentTable Gridview1.DataSource = dtCurrentTable Gridview1.DataBind() End If Else Response.Write("ViewState is null") End If 'Set Previous Data on Postbacks SetPreviousData() End Sub
There is a method called "SetPreviousData()" which I think is to populate the values from ViewState to GridView1 so that you cannot hide the row even that text box does not contain any value. The method "SetPreviousData()" works after "RowDataBound" event handler.
I remember that I have dealt with a similar case which uses SetPreviousData to manually set data for grid view control.
I think if it would make problem clearer to see what exactly this method does for GridView1 control.
Another problem will be occurred if you could solve this problem.
That is, you will never get the new row rendered in the page since every new row will have two text boxes without value inside. According to your logic, the row will be hidden if both two text box contain "string.Empty".
Suggestion:
If you don't want to make these 8 grid view working together, you would better to let each of them having a separate in-memory data table (ViewState).
If you add one row for one of them, it will only have extra empty row which will not bother others and you will not need to check the row's value and hide it.
Hope this can help you.
Best regards,
Sean
Friday, May 8, 2020 9:34 AM -
User1216627406 posted
Thanks a lot Sean.
First they have a separate in-memory data table (ViewState).
For instance, you asked for the code for setpreviousdata() method,
Here is two (SetPreviousData() and SetPreviousData2(), etc)
Each representing each grid control.
Private Sub SetPreviousData() Dim rowIndex As Integer = 0 If ViewState("CurrentTable") IsNot Nothing Then Dim dtc As DataTable = DirectCast(ViewState("CurrentTable"), DataTable) If dtc.Rows.Count > 0 Then For i As Integer = 0 To dtc.Rows.Count - 1 Dim txtsoname As TextBox = DirectCast(Gridview1.Rows(rowIndex).Cells(1).FindControl("txtsourcename"), TextBox) Dim txtsoaddress As TextBox = DirectCast(Gridview1.Rows(rowIndex).Cells(2).FindControl("txtsourceaddress"), TextBox) txtsoname.Text = dtc.Rows(i)("sourcename").ToString() txtsoaddress.Text = dtc.Rows(i)("sourceaddress").ToString() rowIndex += 1 Next End If End If End Sub Private Sub SetPreviousData2() Dim rowIndex As Integer = 0 If ViewState("SpouseTable") IsNot Nothing Then Dim dt As DataTable = DirectCast(ViewState("SpouseTable"), DataTable) If dt.Rows.Count > 0 Then For i As Integer = 0 To dt.Rows.Count - 1 Dim txtspname As TextBox = DirectCast(grvspouse.Rows(rowIndex).Cells(1).FindControl("txtspousename"), TextBox) Dim txtspaddress As TextBox = DirectCast(grvspouse.Rows(rowIndex).Cells(2).FindControl("txtspouseaddress"), TextBox) txtspname.Text = dt.Rows(i)("spousename").ToString() txtspaddress.Text = dt.Rows(i)("spouseaddress").ToString() rowIndex += 1 Next End If End If End Sub
Friday, May 8, 2020 12:38 PM -
User475983607 posted
The stored procedure clears up what's happening. The design around the DateDetails table is not normalized which is driving the downstream complexity. A union query can hide complexity from the code behind. Basically we'll move the code behind visibility toggle to SQL. We'll also convert the variable columns to variable rows. Each row will have an identifier which allows easy filtering. Keep in mind, I found a HonorariaIncome column which seems to be specific to the Honoraria table. HonorariaIncome might be an outlier. I'm not sure what it is used for.
Anyway, the SQL is below. I cannot test the SQL because I do not have your table structure or data. I did my best to validate the code without being about to test. With that being said, you might have to tweak the SQL. You'll notice the SQL is in sections and each section queries a different income table. The where clause filters out the empty rows. This is the part that takes over the the code behind visibility toggle. Another change is all the column names are the same. That's just the way a UNION works. Other than changing the GridView bindings it should not cause any issues because you can filter by income.
SELECT s.sourcename [Name], s.sourceaddress [Address], d.dateCreated [DateCreated], 'Source' AS [IncomeSource], 0 AS HonorariaIncome CASE WHEN d.dateCreated < prev_yr_jan01 THEN 1 ELSE 0 END AS pastYears, CASE WHEN d.dateCreated >= prev_yr_jan01 AND d.dateCreated < curr_yr_jan01 THEN 1 ELSE 0 END as previousYear, CASE WHEN d.dateCreated >= curr_yr_jan01 THEN 1 ELSE 0 END as thisYear FROM Employees e CROSS APPLY ( SELECT DATEADD(YEAR, DATEDIFF(YEAR, 0, GETDATE()), 0) AS curr_yr_jan01, DATEADD(YEAR, DATEDIFF(YEAR, 0, GETDATE()) - 1, 0) AS prev_yr_jan01 ) AS yr_start_dates INNER JOIN SourceDetails s ON e.EmployeeID = s.EmployeeID INNER JOIN dateDetails d ON e.EmployeeID = d.employeeID WHERE e.EmpID = @empID AND s.sourcename IS NOT NULL AND s.sourcename <> '' UNION SELECT sp.spousename AS [Name], sp.spouseaddress AS [Address], d.dateCreated AS [DateCreated], 'Souse' AS [IncomeSource], 0 AS HonorariaIncome CASE WHEN d.dateCreated < prev_yr_jan01 THEN 1 ELSE 0 END AS pastYears, CASE WHEN d.dateCreated >= prev_yr_jan01 AND d.dateCreated < curr_yr_jan01 THEN 1 ELSE 0 END as previousYear, CASE WHEN d.dateCreated >= curr_yr_jan01 THEN 1 ELSE 0 END as thisYear FROM Employees e CROSS APPLY ( SELECT DATEADD(YEAR, DATEDIFF(YEAR, 0, GETDATE()), 0) AS curr_yr_jan01, DATEADD(YEAR, DATEDIFF(YEAR, 0, GETDATE()) - 1, 0) AS prev_yr_jan01 ) AS yr_start_dates INNER JOIN SpouseDetails sp ON e.EmployeeID = sp.employeeID INNER JOIN dateDetails d ON e.EmployeeID = d.employeeID WHERE e.EmpID = @empID AND sp.spousename IS NOT NULL AND sp.spousename <> '' UNION SELECT h.HonorariaName AS [Name], h.HonorariaAddress AS [Address], d.dateCreated AS [DateCreated], 'HonorariaI' AS [IncomeSource], h.HonorariaIncome AS HonorariaIncome CASE WHEN d.dateCreated < prev_yr_jan01 THEN 1 ELSE 0 END AS pastYears, CASE WHEN d.dateCreated >= prev_yr_jan01 AND d.dateCreated < curr_yr_jan01 THEN 1 ELSE 0 END as previousYear, CASE WHEN d.dateCreated >= curr_yr_jan01 THEN 1 ELSE 0 END as thisYear FROM Employees e CROSS APPLY ( SELECT DATEADD(YEAR, DATEDIFF(YEAR, 0, GETDATE()), 0) AS curr_yr_jan01, DATEADD(YEAR, DATEDIFF(YEAR, 0, GETDATE()) - 1, 0) AS prev_yr_jan01 ) AS yr_start_dates INNER JOIN Honoraria h ON e.EmployeeID = h .EmployeeID INNER JOIN dateDetails d ON e.EmployeeID = d.employeeID WHERE e.EmpID = @empID
AND h.HonorariaName IS NOT NULL AND h.HonorariaName <> ''Friday, May 8, 2020 1:58 PM -
User1216627406 posted
Great work there sir.
Sorry my response is late again...too many virtual meetings.
I have a question. I think I know the answer to my question but I will play it safe by asking for your help in answering.
As you can see from the query, there are 8 tables involved.
However, your version using UNION query handles only three sections, source, spouse and honoraria.
Is that all I need? I know said I may have to modify the query a bit. Is that what you meant?
You made a good observation about honoraria having income.
Yes, that's true. The rest of the tables (grid controls) require name and address but for some reason, we were told honoraria needs income as well.
I am still trying to understand how dateDetails table is the problem.
That table, as well as the rest of the tables are related by employeeID.
Finally, assume that this works and I able to see now, I no longer need to additional vb code that hides the empty rows, correct?
I didn't know you are very good with sql as well.
Many thanks for your help.
BTW: Is there any relevance to using 'source', 'spouse', and 'honoraria1' aliases?
If yes, how do I incorporate them into my VB?
Friday, May 8, 2020 8:28 PM -
User475983607 posted
As you can see from the query, there are 8 tables involved.
However, your version using UNION query handles only three sections, source, spouse and honoraria.
Is that all I need? I know said I may have to modify the query a bit. Is that what you meant?
My intention is to provide enough code so the pattern is obvious and not overwhelming. I thought you could add the missing tables.
I am still trying to understand how dateDetails table is the problem.
That table, as well as the rest of the tables are related by employeeID.
It's a little more than just the dateDetails. Frankly, I don't have the words to explain without sounding overly critical.
Finally, assume that this works and I able to see now, I no longer need to additional vb code that hides the empty rows, correct?Right. The intention is a "data driven design". I guess in other words, the data is smart and the UI does what it is told.
BTW: Is there any relevance to using 'source', 'spouse', and 'honoraria1' aliases?To filter the result set by type. Each GridView data source is filtered by income type. I made the type up. I'm sure you can give the types better names. An enum is good for this sort of deign; named constants.
If yes, how do I incorporate them into my VB?The "types" become an input parameter to filter the data along with EmpId. The filter can be applied to the cached DataTable.
Friday, May 8, 2020 9:25 PM -
User1216627406 posted
My intention is to provide enough code so the pattern is obvious and not overwhelming. I thought you could add the missing tables.
I think I got it. In other words, continue adding the UNION query till all tables (Grid controls) are accounted for?
It's a little more than just the dateDetails. Frankly, I don't have the words to explain without sounding overly critical.
I have no issues with constructive criticisms at all. Here, you have told me what could be wrong with my code. At the same time, you have provided useful resources include code on how to fix the problem. I am fine with that absolutely. Thank you
The "types" become an input parameter to filter the data along with EmpId. The filter can be applied to the cached DataTable.
Sorry to sound too slow in the uptake but still not clear how to use "types" as input parameter.
Since this is a stored procedure with empID as the only input parameter, do I need to add "types" as input parameter as well?
This is the method that is calling the stored procedure:
//FillGridView() Private Sub FillGridView() Dim control As Control = Nothing Dim conn_str As String = ConfigurationManager.ConnectionStrings("constr").ConnectionString Using conn As SqlConnection = New SqlConnection(ConfigurationManager.ConnectionStrings("ppmtest").ConnectionString) conn.Open() Using sourcecmd As SqlCommand = New SqlCommand() sourcecmd.CommandText = "uspGetRecs" sourcecmd.CommandType = CommandType.StoredProcedure sourcecmd.Parameters.AddWithValue("@empID", txtEmpID.Text.Trim()) sourcecmd.Connection = conn Using ad As SqlDataAdapter = New SqlDataAdapter(sourcecmd) Dim ds As DataTable = New DataTable() ad.Fill(ds) If ds.Rows.Count > 0 Then Gridview1.DataSource = ds Gridview1.DataBind() grvspouse.DataSource = ds grvspouse.DataBind() grvDiv.DataSource = ds grvDiv.DataBind() grvReim.DataSource = ds grvReim.DataBind() grvHon.DataSource = ds grvHon.DataBind() grvGift.DataSource = ds grvGift.DataBind() grvOrg.DataSource = ds grvOrg.DataBind() grvCred.DataSource = ds grvCred.DataBind() Else End If End Using End Using End Using End Sub
Two things with the method, as you can see, it uses empID as parameter. So, if I understand correctly, "types" will be added as second parameter in this method.
You can also see how it is using the 7 GridView controls.
Thank you very much
Friday, May 8, 2020 10:38 PM -
User-1330468790 posted
Hi simflex,
The problem is clear now. Try remove the SetPreviousData() method which works as DataBind() for the gridview control.
You already have done the data binding stuff by calling the method "Gridview1.DataBind()" and you don't need to manually bind the data again.
Best regards,
Sean
Tuesday, May 12, 2020 1:24 AM -
User1216627406 posted
Hi Sean,
If I understand you correctly sir, you are suggesting that removing SetPreviousData() will solve the problem of having several empty rows displayed when there is no data in them, no?
If yes, well unfortunately, commenting it out or straight up remove the method didn't solve the problem.
It certainly didn't solve it in terms of the inability to add another row when you click the "Add new row if needed" button.
I think that mgebhard is on to something with the modification of the stored procedure.
Issue that I have with his modification now is what to do with those aliases like 'source', spouse', 'honoraria1', etc.
Are they variables and if yes, how do I use them with fillSource() method?
Tuesday, May 12, 2020 10:52 PM