How can I solve the problem on Create Multi-Series Chart - vb.net

I tried to made a chart through Vb.Net, it seems not work. Here is my Coding:
Private Sub btn_Rate_Click(sender As Object, e As EventArgs) Handles btn_Rate.Click
Label37.Visible = True
Label39.Visible = True
Label48.Visible = True
Label49.Visible = False
Label51.Visible = False
Label52.Visible = False
Label37.Text = "The Chart of " & Label38.Text & " by Rate of Incidents "
Label48.Text = "From" & " " & Label42.Text & " " & "to" & " " & Label45.Text
If DropDownList13.SelectedIndex = -1 Then
Label39.Text = Label40.Text
Else
Label39.Text = Label40.Text & Label41.Text
End If
btn_Print.Visible = True
Dim dtRate As DataTable = New DataTable("dtRates")
For Each cell As TableCell In gvRate.HeaderRow.Cells
dtRate.Columns.Add(cell.Text.Trim())
Next
For Each row As GridViewRow In gvRate.Rows
dtRate.Rows.Add()
For i As Integer = 0 To row.Cells.Count = -1
dtRate.Rows(row.RowIndex)(i) = row.Cells(i).Text.Trim()
Next
Next
Dim departments As List(Of String) = New List(Of String)()
departments = (From p In dtRate.AsEnumerable() Select p.Field(Of String)("Department")).Distinct().ToList()
If RateChart.Series.Count() = 1 Then
RateChart.Series.Remove(RateChart.Series(0))
End If
For Each department As String In departments
Dim UserDepartment = department
Dim X As Integer()
X = (From p In dtRate.AsEnumerable()
Where p.Field(Of String)("Department") = UserDepartment
Order By p.Field(Of String)("Period_Shown")
Select Convert.ToInt32(p.Field(Of String)("Period_Shown"))).ToArray()
Dim Y As Decimal()
Y = (From p In dtRate.AsEnumerable()
Where p.Field(Of String)("Department") = UserDepartment
Order By p.Field(Of String)("Period_Shown")
Select Convert.ToDecimal(p.Field(Of String)("Rate_Int"))).ToArray()
RateChart.Series.Add(New Series(department))
RateChart.Series(department).IsValueShownAsLabel = True
RateChart.Series(department).BorderWidth = 3
RateChart.Series(department).ChartType = SeriesChartType.Line
RateChart.Series(department).Points.DataBindXY(X, Y)
Next
RateChart.Legends(0).Enabled = True
End Sub
There had two error Message. The one is In Line24
Where p.Field(Of String)("department") = department
Error Meaage:
Using the iteration variable "department" in a query expression may have unexpected results. Instead, create a local variable within the loop and assign it the value of the iteration variable.
The other is In Line41
RateChart.Legends(0).Enabled = True
Error Meaage:
Index was out of range. Must be non-negative and less than the size of the collection.Parameter name: index

For the second error; Line 41: The error says "The index is out of range". That means you were trying to index an object with a value that was not valid.

Related

VB.NET copy data from treevew to datagridview

I want to copy some/all data from treeview to the datagridview
For example: if I select one node of a department or a company node, when I click a button I want all employees of that node to be copied to the datagridview with checking for repeatation.
Someone Please explain to me the mechanism of this.
This is the code I tried to write but I had difficulties to make it
For i = 0 To TreeView1.Nodes.Count - 1
'MsgBox(i & " " & TreeView1.Nodes(i).Text)
Dim node As TreeNode = TreeView1.Nodes(i)
For j = 0 To node.Nodes.Count - 1
'MsgBox(j & " " & node.Nodes(j).Text)
Dim subnode As TreeNode = node.Nodes(j)
For z = 0 To subnode.Nodes.Count - 1
'MsgBox(z & " " & subnode.Nodes(z).Text)
Dim Usubnode As TreeNode = subnode.Nodes(j)
DGV.Rows(z).Cells(0).Value = subnode.Nodes(z).Name.ToString
DGV.Rows(z).Cells(1).Value = subnode.Nodes(z).Text.ToString
Next '' z
Next ''j
Next '' i
'''''''''''''''''''''''''''''''''''''''''''
the treeview was populated this way:
For Each dr As DataRow In dt.Rows
Dim coName = dr("CompName").ToString()
Dim coNodeId = "co" & dr("ID").ToString()
''''find or create the company node
Dim nodes = TreeView2.Nodes.Find(coNodeId, True)
Dim coNode As TreeNode
If nodes.Length = False Then ''''didn't find: create and add
coNode = New TreeNode() {.Name = coNodeId, .Text = coName}
TreeView2.Nodes.Add(coNode)
Else ''''did find
coNode = nodes(0)
End If
Dim depName = dr("depName").ToString()
Dim depNodeId = "dep" & dr("depNum").ToString()
''''find or create the dep node under the co node
nodes = coNode.Nodes.Find(depNodeId, True)
Dim depNode As TreeNode
If nodes.Length = 0 Then
depNode = New TreeNode() {.Name = depNodeId, .Text = depName}
coNode.Nodes.Add(depNode)
Else
depNode = nodes(0)
End If
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
''''create the emp node
Dim EmpName = dr("EmpName").ToString()
Dim empNodeId = "emp" & dr("EmpNum").ToString()
''''find or create the emp node under the dep node
nodes = depNode.Nodes.Find(empNodeId, True)
Dim empNode As TreeNode
If nodes.Length = 0 Then
empNode = New TreeNode() {.Name = empNodeId, .Text = EmpName}
depNode.Nodes.Add(empNode)
Else
empNode = nodes(0)
End If
Next
You populated the tree from a datatable. Bind the grid to the same table, through a BindingSource. While you are adding nodes, store a string in the Tag of the node to dictate a BindingSource filter:
depNode = New TreeNode() {.Name = depNodeId, .Text = depName, .Tag = $"[depNum] = '{dr("depNum")}'"}
Put a different filter for each kind of node(co/dep/emp)
Add a handler to the treeview's AfterSelect event (https://learn.microsoft.com/en-us/dotnet/framework/winforms/controls/how-to-determine-which-treeview-node-was-clicked-windows-forms) and set the Filter property of the BindingSource to e.Node.Tag.ToString()

DataTable.Delete() Removing rows before Accept Changes

I'm having an issue where I mark several rows for deletion within a loop, but when it gets to a certain point in the loop, the rows actually start to be removed.
As you can see what I'm basically doing is checking if the row needs to be deleted and if so, add it to a new table and delete it.
The problem is this works for the first 60 ish rows, then all of a sudden the rows appear to actually be removed and it eventually throws a row with that index doesn't exist error (at 65).
The original table is a list of contacts with firstname, lastname, email and company, with 70 records.
I tried to cut the list in half, but then the issue started happening at around row 23.
dtSelectCompany = dt_data.Clone
Dim s_company As String
Dim b_add As Boolean
Dim dtCompanies As Data.DataTable
For i = 0 To dt_data.Rows.Count - 1
b_add = False
s_company = dt_data.Rows(i).Item(columnsDictionary("company")).ToString
If s_company = "" Then : b_add = True
Else
dtCompanies = crm_functions.getCompaniesByName(s_company.Replace(" ", "%"))
If dtCompanies.Rows.Count > 1 Then : b_add = True
ElseIf dtCompanies.Rows.Count = 1 Then
dt_data.Rows(i).Item(columnsDictionary("company")) = dtCompanies.Rows(0).Item("id")
Else : b_add = True
End If
End If
If b_add Then
Dim temp_row As Data.DataRow = dtSelectCompany.NewRow
temp_row.ItemArray = dt_data.Rows(i).ItemArray.Clone()
temp_row.Item("fullName") = temp_row.Item(columnsDictionary("firstname")) & " " & temp_row.Item(columnsDictionary("lastname"))
dtSelectCompany.Rows.Add(temp_row)
dt_data.Rows(i).Delete()
End If
Next
Instead of a counter, use a datarow, for example:
dtSelectCompany = dt_data.Clone
Dim s_company As String
Dim b_add As Boolean
Dim dtCompanies As Data.DataTable
Dim MyDataRow as DataRow
For Each MyDataRow IN dt_data.Rows
b_add = False
s_company = MyDataRow("company").ToString
If s_company = "" Then : b_add = True
Else
' not sure what crm_functions is, so left this alone
dtCompanies = crm_functions.getCompaniesByName(s_company.Replace(" ", "%"))
If dtCompanies.Rows.Count > 1 Then : b_add = True
ElseIf dtCompanies.Rows.Count = 1 Then
MyDataRow("company")) = dtCompanies.Rows(0).Item("id")
Else : b_add = True
End If
End If
If b_add Then
Dim temp_row As Data.DataRow = dtSelectCompany.NewRow
temp_row = MyDataRow
temp_row.Item("fullName") = temp_row.Item(columnsDictionary("firstname")) & " " & temp_row.Item(columnsDictionary("lastname"))
dtSelectCompany.Rows.Add(temp_row)
MyDataRow.Delete()
End If
Next
Wrote this off the top of my head, so .....

How to display information of a line chart by moving the mouse pointer over the chart area

I have a desktop app in visual studio 2015 that has a line chart. I want to get the value of the (x,y) point when I move the mouse inside the chart area so I don't have to put every label because it will be flooded with labels and it would be a mess. The idea is to make a dot (or some kind of vertical and horizontal intersected line) to follow the X-position of the mouse pointer, but change the Y-position according to the Y-axis value.
The image below shows in a better way (I hope) what I'm looking for. You can see that the mouse pointer is in the chart area and there is a blue point (which I circled in red) that is in the X position of the mouse pointer and in the Y position of the Y-axis of the graph. Notice that it does not matter if it is above or under the line, the blue dot is in the line.
Now, the values of X and Y are shown above (in the inside the red rectangle). It would be nice to have those values in near the blue dot, like an annotation or something like that.
EDIT:
I managed to make some progress, but other problems appeared. I can put an annotation in the ChartArea2 (which is intended) but two things happens:
1) The annotation appears either I click the ChartArea1 or ChartArea2.
2) It creates two annotations instead of one.
The code I'm using is as follows.
To create the chart with the chart areas and the series:
Private Sub ComboBox1_SelectedIndexChanged(sender As Object, e As EventArgs) Handles ComboBox1.SelectedIndexChanged
Dim cargo As String = ComboBox1.Text
Dim fi As Date = DateTimePicker1.Text
Dim ff As Date = DateTimePicker2.Text
Dim tabla As New DataTable
Dim fechas As New DataTable
Dim k As Integer
Dim green As Integer
Dim posX, posY, width, height As Single
Dim numberOfAreas As Integer = 2
green = RGB(118, 183, 3)
cn.ConnectionString = ""
Try
tabla.Clear()
fechas.Clear()
Dim sql = ("select p.nombre, s.servicio_nombre, dp.etapa, dp.carga, fecha_inicial, fecha_final, extract (epoch from fecha_final - fecha_inicial)/86400 as duracion
from desarrolloproyecto dp natural join cargo c natural join proyecto p natural join servicio s
where nombre_cargo = '" & cargo & "' AND fecha_inicial BETWEEN '" & fi & "' AND '" & ff & "'
OR
nombre_cargo = '" & cargo & "' AND fecha_final BETWEEN '" & fi & "' AND '" & ff & "'
order by p.nombre desc")
Dim sqlf = ("select t1.fechas, t1.carga from
(select (generate_series(fecha_inicial, fecha_final, '1 day'::interval))::date AS fechas, sum(dp.carga) as carga
FROM desarrolloproyecto dp NATURAL JOIN cargo c NATURAL JOIN proyecto p NATURAL JOIN servicio s
where nombre_cargo = '" & cargo & "'
group by fechas
order by fechas)t1
where t1.fechas BETWEEN '" & fi & "' AND '" & ff & "'
order by t1.fechas")
Dim da As New Npgsql.NpgsqlDataAdapter(sql, cn)
Dim daf As New Npgsql.NpgsqlDataAdapter(sqlf, cn)
cn.Open()
da.Fill(tabla)
daf.Fill(fechas)
With Chart1
.Series.Clear()
.Series.Add("fechas")
.Series.Add(cargo)
.Series.Add("fech")
.Series(cargo).ChartArea = "ChartArea1"
.Series("fechas").ChartArea = "ChartArea1"
.Series("fech").ChartArea = "ChartArea2"
.Legends("Legend1").Alignment = StringAlignment.Center
.Legends("Legend1").Docking = Docking.Bottom
.Series("fechas").IsVisibleInLegend = False
.Series("fech").IsVisibleInLegend = False
.Series("fechas").Color = Color.Transparent
.Series(cargo).Color = Color.FromArgb(118, 183, 3)
.Series(cargo).CustomProperties = "DrawingStyle = Cylinder"
.Series("fech").Color = Color.FromArgb(118, 183, 3)
.Series("fech").ChartType = SeriesChartType.Line
.Series("fechas").ChartType = SeriesChartType.StackedBar
.Series(cargo).ChartType = SeriesChartType.StackedBar
.ChartAreas("ChartArea1").Position = New ElementPosition(0, 0, 95, 70)
.ChartAreas("ChartArea1").CursorX.IsUserEnabled = True
.ChartAreas("ChartArea1").CursorX.IsUserSelectionEnabled = True
.ChartAreas("ChartArea1").AxisX.ScaleView.Zoomable = True
.ChartAreas("ChartArea1").AxisX.ScrollBar.IsPositionedInside = False
.ChartAreas("ChartArea1").CursorY.IsUserEnabled = True
.ChartAreas("ChartArea1").CursorY.IsUserSelectionEnabled = True
.ChartAreas("ChartArea1").AxisY.ScaleView.Zoomable = True
.ChartAreas("ChartArea1").AxisY.ScrollBar.IsPositionedInside = False
.ChartAreas("ChartArea1").AxisY.Minimum = fi.ToOADate 'valores minimos y maximos de ejes x e y
.ChartAreas("ChartArea1").AxisY.Maximum = ff.ToOADate
.ChartAreas("ChartArea1").AxisY.IntervalType = DateTimeIntervalType.Auto
.ChartAreas("ChartArea1").AxisX.IntervalOffset = 0
.ChartAreas("ChartArea1").AxisX.Interval = 1
.ChartAreas("ChartArea2").Position = New ElementPosition(0, 70, 95, 25)
.ChartAreas("ChartArea2").CursorX.IsUserEnabled = True
.ChartAreas("ChartArea2").CursorY.IsUserEnabled = True
.ChartAreas("ChartArea2").AxisX.Minimum = fi.ToOADate
.ChartAreas("ChartArea2").AxisX.Maximum = ff.ToOADate
.ChartAreas("ChartArea2").AxisX.IntervalType = DateTimeIntervalType.Auto
.Series("fech").SmartLabelStyle.Enabled = True
For k = 0 To tabla.Rows.Count - 1
.Series("fechas").Points.AddXY(tabla.Rows(k).Item(0) & "-" & tabla.Rows(k).Item(1) & "-E" & tabla.Rows(k).Item(2), tabla.Rows(k).Item(4))
.Series(cargo).Points.AddY(tabla.Rows(k).Item(6))
Next
For k = 0 To fechas.Rows.Count - 1
.Series("fech").Points.AddXY(fechas.Rows(k).Item(0), fechas.Rows(k).Item(1))
If k = 0 Then
posY = fechas.Rows(0).Item(1)
posX = fechas.Rows(0).Item(1)
Else
posY = Min(posY, fechas.Rows(k).Item(1))
posX = Max(posX, fechas.Rows(k).Item(1))
End If
Next
.ChartAreas("ChartArea2").AxisY.Minimum = posY - 25
.ChartAreas("ChartArea2").AxisY.Maximum = posX + 25
End With
Label1.Focus()
Catch ex As Exception
MessageBox.Show(ex.Message)
End Try
cn.Close()
End Sub
Now, for the annotation part it goes as follows:
Private Sub Chart1_CursorPositionChanging(sender As Object, e As CursorEventArgs) Handles Chart1.CursorPositionChanging
Dim a As CalloutAnnotation = New CalloutAnnotation()
a.AnchorDataPoint = Chart1.Series("fech").Points(0)
a.Text = "Select this Annotation Object\nand move the Anchor point"
Chart1.Annotations.Add(a)
End Sub
So I guess I have 3 questions now.
1) How can I make the annotations appear only when I move the mouser over the ChartArea2? (The idea is not to click the ChartArea2.
2) How can I make only to appear one annotation?
3) How can I make the annotation move when I move the mouse pointer along the X-axis? I would like that the annotation coordenates were x = mouse pointer value and y = y-axis value.

multiple SQL queries stored as vb.net variable

I know there are a few other posts about putting sql results into a vb.net variable but I just couldn't wrap my head around how they did it and how I could do it in my project.
So what I'm trying to do is query my database for 4 different values and then display each value in a certain part of the form. I was going to store the each value into a variable and then do an textbox1.text = i
Updated code for 10/5/2014
Private Sub LBmembers_SelectedIndexChanged(sender As Object, e As EventArgs) Handles LBmembers.SelectedIndexChanged
Dim i As String = LBmembers.SelectedItem
Dim dbProvider = "PROVIDER=Microsoft.Jet.OLEDB.4.0;"
Dim dbSource = "Data Source= C:\members.mdb "
Dim SqlQuery As String = "SELECT StartTime, EndTime, ShipCode, CycleTime, WorkPercent, Share FROM tblMembers WHERE Member = #ID;"
Using con = New OleDb.OleDbConnection(dbProvider & dbSource)
Using cmd = New OleDb.OleDbCommand(SqlQuery, con)
con.Open()
cmd.Parameters.AddWithValue("#ID", OleDb.OleDbType.VarWChar).Value = i
Using reader = cmd.ExecuteReader()
If reader.Read() Then
TBtimestart.Text = reader.ToString(0)
TBtimeend.Text = reader.ToString(1)
Dim j = Convert.ToInt32(reader.ToString(2))
TBtimecycle.Text = reader.GetInt32(3).ToString
TBmemberpercent.Text = reader.GetInt32(4)
TBmembershare.Text = reader.GetInt32(5)
If j = 1 Then
RBpro.Checked = True
ElseIf j = 2 Then
RBret.Checked = True
ElseIf j = 3 Then
RBcov.Checked = True
ElseIf j = 4 Then
RBskiff.Checked = True
ElseIf j = 5 Then
RBmack.Checked = True
ElseIf j = 6 Then
RBhulk.Checked = True
Else
RBpro.Checked = False
RBret.Checked = False
RBcov.Checked = False
RBskiff.Checked = False
RBmack.Checked = False
RBhulk.Checked = False
Exit Sub
End If
End If
End Using
con.Close()
End Using
End Using
End Sub
The exception
InvalidCastException was unhandled Specified cast is not valid.
Pops up on the line TBtimecycle.text = reader.GetInt32(3).ToString
The reader is also reading "System.Data.OleDb.OleDbDataReader" when I insert the test code "TBgrossisk.Text = reader.ToString()" into the using statement
If you look carefully to the syntax of the SELECT statement you will see that you can request any column of the table with just one query.
Private Sub ListBox1_SelectedIndexChanged(sender As Object, e As EventArgs) Handles LBmembers.SelectedIndexChanged
if string.IsNullOrWitheSpace(ListBox1.SelectedItem.ToString()) Then
MessageBox.Show("Select an item from the Listbox")
End If
Dim member As String = ListBox1.SelectedItem
Dim dbProvider = "PROVIDER=Microsoft.Jet.OLEDB.4.0;"
Dim dbSource = "Data Source= C:\members.mdb "
Dim SqlQuery As String = "SELECT StartTime, EndTime, ShipCode, CycleTime " & _
"FROM tblMembers WHERE Member = #ID;"
Using con = New OleDb.OleDbConnection(dbProvider & dbSource)
Using cmd = New OleDb.OleDbCommand(SqlQuery, con)
con.Open()
cmd.Parameters.AddWithValue("#ID", OleDb.OleDbType.VarWChar).Value = member
Using reader = cmd.ExecuteReader
if reader.Read() Then
TextBox1.Text = reader.GetString(0)
TextBox2.Text = reader.GetString(1)
Dim j = reader.GetInt32(2)
If j = 1 Then
Radio1.Checked = True
ElseIf j = 2 Then
Radio2.Checked = True
ElseIf j = 3 Then
Radio3.Checked = True
ElseIf j = 4 Then
Radio4.Checked = True
ElseIf j = 5 Then
Radio5.Checked = True
ElseIf j = 6 Then
Radio6.Checked = True
Else
Exit Sub
End If
TextBox4.Text = reader.GetInt32(3).ToString()
Else
MessageBox.Show("The search for '" & member & "' doesn't find any data")
End If
End Using
End Using
End Using
End Sub
Instead of using ExecuteScalar that returns just the first column of the first row retrieved you could use an OleDbDataReader returned by the method ExecuteReader. This object allows to access the various column in the current record using an index (or the column name). Read more about OleDbDataReader in MSDN

dynamic table and button creation in asp.net

I have created a dynamic table using for loop condition.In which it has a button while i click a specific button it should open a file.But in my coding it is opening a file button in the last row.
If Not IsPostBack Then
txtlogsdate.Text = FormatDate(Now)
End If
Try
trViewlogs.Visible = True
lbllogs.Visible = False
lbllogname.Visible = True
RT1.Visible = False
pb.Visible = False
Dim d1 As DateTime = txtlogsdate.Text
Dim dd As String = d1.ToString("dd")
Dim mm As String = d1.ToString("MM")
Dim yy As String = d1.ToString("yy")
Dim d2 As String = yy & "" & mm & "" & dd
Dim di As DirectoryInfo = New DirectoryInfo(Server.MapPath("~\logs"))
Dim files As FileInfo() = di.GetFiles("*.log")
Dim tab As New Table()
tab.CellPadding = 0
tab.CellSpacing = 0
tab.BorderStyle = BorderStyle.Double
tab.Attributes.Add("style", "margin-left: 0.5px; width: 800px;")
Dim row As New TableRow()
Dim headerCell1 As New TableHeaderCell()
headerCell1.Text = "Logs"
headerCell1.Attributes.Add("style", "margin-left: 0.5px; height: 20px;")
headerCell1.BackColor = System.Drawing.Color.CornflowerBlue
headerCell1.ForeColor = System.Drawing.Color.White
row.Controls.Add(headerCell1)
tab.Controls.Add(row)
Dim headerCell2 = New TableHeaderCell()
headerCell2.Attributes.Add("style", "margin-left: 0.5px; height: 20px;")
headerCell2.BackColor = System.Drawing.Color.CornflowerBlue
headerCell2.ForeColor = System.Drawing.Color.White
headerCell2.Text = "Download"
row.Controls.Add(headerCell2)
tab.Controls.Add(row)
For i As Integer = 0 To files.Length - 1
Dim a As String = files(i).ToString.Replace("Event-", "")
Dim c As String = a.Substring(0, 6)
Dim sw As String
If d2 = c Then
sw = My.Computer.FileSystem.ReadAllText(_
GetWebSitePhysicalRoot & "\logs\" & files(i).ToString)
lbllogname.Text = files(i).ToString
lbllogname.Visible = False
row = New TableRow()
If i Mod 2 = 0 Then
row.BackColor = System.Drawing.Color.White
Else
row.BackColor = System.Drawing.Color.AliceBlue
End If
Dim cell As New TableCell()
cell.Text = lbllogname.Text
'cell.Width = New Unit("1000px")
cell.HorizontalAlign = HorizontalAlign.Center
row.Controls.Add(cell)
Dim cell2 As New TableCell()
Dim bt As New Button
bt.BorderStyle = BorderStyle.Solid
bt.Text = files(i).ToString
AddHandler bt.Click, AddressOf bt_Click
cell2.HorizontalAlign = HorizontalAlign.Center
cell2.Controls.Add(bt)
row.Controls.Add(cell2)
tab.Controls.Add(row)
Panel1.Controls.Add(tab)
End If
Next i
If lbllogname.Text = "" Then
lbllogname.Text = "No Logs to Display !"
End If
Session("pageurl") = ""
Session("pagecount") = ""
ib.SetInfo("Reports > View Logs", Infobar.InfoTypes.Caption)
Dim mi As Integer = GetQueryStringToInt("menuindex", 1)
If Not IsPostBack Then
leftmenu1.AddItem("View Logs", _
GetWebSiteUrlRoot & "/staff_rpt.aspx?rpt=logs&page=1&menuindex=" & mi)
End If
Catch ex As Exception
WriteLog(LogWriter.EventType.eError, ex.StackTrace.ToString)
End Try
Protected Sub bt_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs
Dim a As String
a = lbllogname.Text
Response.ContentType = "text/plain"
Response.AppendHeader("Content-Disposition", "attachment; filename=" & a)
Response.TransmitFile(Server.MapPath("~/logs/" & a))
Response.End()
End Sub
To use the dynamic table structure you have built in your code, then you need to uniquely name each button in each row; otherwise the button click handler (bt_Click) cannot figure out the correct row to open the file in, because they are all called the same and will use the last one.
Since you want a table structure, then I suggest you use the GridView server control, as it will provide similar output, but provide the ability to use templating to name the controls of each row the same, but allow for you to differentiate individual rows when a click event happens.