calling a VB.net Function for SQL recordset - sql

Beginner here
I have the following code which I would like to call using a button called findCustomerBTN
Public Function Execute(ByVal sqlQuery As String) As ADODB.Recordset
If SecuritySSPIchkbx.Checked Then
chk = "TRUE"
Else chk = "FALSE"
End If
builder.DataSource = ServerBox.Text
builder.InitialCatalog = DatabaseBox.Text
builder.UserID = Username.Text
builder.Password = Password.Text
builder.IntegratedSecurity = chk
MessageBox.Show(builder.ConnectionString)
Using sqlConnection1 As New SqlConnection(builder.ConnectionString)
sqlConnection1.Open()
Try
command = New SqlCommand(sqlQuery, sqlConnection1)
adapter.SelectCommand = command
adapter.Fill(ds, "Create DataView")
adapter.Dispose()
command.Dispose()
sqlConnection1.Close()
dv = ds.Tables(0).DefaultView
DataGridView1.DataSource = dv
Catch ex As Exception
MsgBox(ex.ToString)
End Try
End Using
End Function
How do I call this function:
Private Sub findCustomerBTN_Click(sender As Object, e As EventArgs) Handles
sqlquery = "Select * from customers where name = 'Smith'"
call function?
End Sub
I have googled but I can't wrap my head around how a function works any pointers to help me understand would be great thanks

In VB, a function is a block of code that returns a value. Your code does not return a value, and the type of query execution you're carrying out will never return an ADODB.RecordSet - that's an ancient technology remeniscent of the VB6 era, and you're using a much more modern data access strategy (ADO.NET, DataTables and DataAdapter) though it's not the latest and greatest.
To offer a run through of your code, and the other issues it has:
Execute is a pretty bland name - go for something more specific, just incase you run the wrong execute and some poor prisoner ends up in front of the firing squad
Your function takes an sql string as a parameter to run, but then overwrites it with a fixed string, so there isn't much point offering it as a parameter in the first place. I could call Execute("SELECT * FROM kittens") expecting to get some cute data back, and all I get is the same old customer
Avoid calling MessageBox.Show in any code that shoudl reasonably be expected to run quietly and repeatedly, otherwise the user is going to get hella annoyed. If youre putting this here for debugging purposes, learn how the visual studio debugger works instead
Your code runs an sql query and assigns the resulting data table data to the datasource of a grid, so that the grid will show the data. There's absolutely no need for this code to be a function (and in c# it wouldnt even compile because it doesn't return a value
What are functions? What do they do? They take some inuts and return some outputs:
Public Function AddTheseTwo(a as Integer, b as Integer) As Integer
Return a + b
End Function
They are called like this:
Dim sum = AddTheseTwo(2, 3)
I.e. you give the name of the function and the input values, which can be variables, and store the result (usually, because you want to use it). Here's a code of yours that is a sub - a block of code that doesn't return a value
Private Sub findCustomerBTN_Click(sender As Object, e As EventArgs) Handles
Execute("Select * from customers where name = 'Smith'")
End Sub
It's not linked to your button click, because there's nothing afte rthe Handles keyword. It should be something like Handles findCustomerBTN.Click. You can mash that button all day and nothing will happen
It called Execute but didn't store the return value (because it didn't need to, because Execute doesn't return anything, so Execute should have been declared as a sub, not a function)
Edit:
You mentioned you want the function to return a datatable:
Public Function GetDataTable(ByVal sqlQuery As String) As DataTable 'need to Imports System.Data if you haven't already
If SecuritySSPIchkbx.Checked Then
chk = "TRUE"
Else chk = "FALSE"
End If
'better to declare builder in this function, not elsewhere
builder.DataSource = ServerBox.Text
builder.InitialCatalog = DatabaseBox.Text
builder.UserID = Username.Text
builder.Password = Password.Text
builder.IntegratedSecurity = chk
MessageBox.Show(builder.ConnectionString)
Using sqlConnection1 As New SqlConnection(builder.ConnectionString)
sqlConnection1.Open()
Try
'note: better to declare adapter and command in this function too
command = New SqlCommand(sqlQuery, sqlConnection1)
adapter.SelectCommand = command
Dim dt as New DataTable
adapter.Fill(dt)
adapter.Dispose()
command.Dispose()
sqlConnection1.Close()
Return dt
Catch ex As Exception
MsgBox(ex.ToString)
End Try
End Using
Return Nothing 'a function has to return something from all possible code paths, even if it's Nothing :)
End Function
And then you call it like this, perhaps:
Private Sub findCustomerBTN_Click(sender As Object, e As EventArgs) Handles whateverbutton.Click
'you can set a datatable as a datasource, doesn't have to be the datatable.defaultview
myDataGRidView.DataSource = GetDataTable("Select * from customers where name = 'Smith'")
End Sub
I recommend you turn on the options for Strict/Explicit etc, to encourage better coding practices. By default VB is quite loose, letting you use variables that havent been declared (autodeclaring variable names that are a typo of another variable name etc), automatically returning Nothing for you from functions etc - it's these little auto's that will later lead to bugs and frsutrations. Computer programming si a precise art; turn on all options to force yourself to be as precise as possible

You can call Execute function using this code:
Private Sub findCustomerBTN_Click(sender As Object, e As EventArgs) Handles
Execute("Select * from customers where name = 'Smith'")
End Sub
You also have to remove this line from Execute function:
sqlQuery = ("select * from ac_billbook where ref = '900123'")
Please follow Steve suggestion to read a good book about programming in VB.NET.

Related

Pasting new records to a Bound DataGridView

Apologies if this has already been asked. If so, I am unable to find a simple solution. I am trying to allow a user to copy/paste multiple records in a DataGridView (the in memory copy of the data, to be saved later when the user clicks the save button) and cannot find anything that works. It probably is because there is something I do not understand about all of this.
I set up a standard edit form with Visual Studio's drag/table into a form, so it's using a BindingSource control and all the other controls that come with doing that. It works just fine when manually entering something in the new row one by one, so it seems to be set up correctly, but when it comes to adding a record (or multiples) using code, nothing seems to work.
I tried a few things as outline in the code below. Could someone please at least steer me in the right direction? It cannot be that difficult to paste multiple records.
I run this when the user presses Control-V (the clipboard correctly holds the delimited strings):
Private Sub PasteClipboard()
If Clipboard.ContainsText Then
Dim sLines() As String = Clipboard.GetText.Split(vbCrLf)
For Each sLine As String In sLines
Dim Items() As String = sLine.Split(vbTab)
Dim drv As DataRowView = AdjustmentsBindingSource.AddNew()
drv.Item(1) = Items(0)
drv.Item(2) = Items(1)
drv.Item(3) = Items(2)
drv.Item(4) = Items(3)
'Error on next line : Cannot add external objects to this list.
AdjustmentsBindingSource.Add(drv)
Next
End If
End Sub
EDIT
(the bindingsource is bound to a dataadapter, which is bound to a table in an mdb file, if that helps understand)
I adjusted the inner part of the code to this:
If (RowHasData(Items)) Then
Dim drv As DataRowView = AdjustmentsBindingSource.AddNew()
drv.Item("FontName") = Items(0)
drv.Item("FontSize") = Items(1)
drv.Item("LetterCombo") = Items(2)
drv.Item("Adjustment") = Items(3)
drv.Item("HorV") = Items(4)
End If
It kinda works, but it also adds a blank row before the 2 new rows. Not sure where that is coming from, as I have even included your RowHasData() routine...
I would think that “attemp3” SHOULD work, however, it is unclear “what” the AdjustmentsBindingSource’s DataSource is. Is it a List<T> or DataTable?
If I set the BinngSource.DataSource to a DataTable, then attempt 3 appears to work. Below is an example that worked.
Private Sub PasteClipboard2()
If Clipboard.ContainsText Then
Dim sLines() As String = Clipboard.GetText.Split(vbCrLf)
For Each sLine As String In sLines
Dim Items() As String = sLine.Split(vbTab)
If (RowHasData(Items)) Then
Dim drv As DataRowView = AdjustmentsBindingSource.AddNew()
drv.Item("FontName") = Items(0)
drv.Item("FontSize") = Items(1)
drv.Item("LetterCombo") = Items(2)
drv.Item("Adjustment") = Items(3)
drv.Item("HorV") = Items(4)
End If
Next
End If
End Sub
This appears to work in my tests. I added a small function (RowHasData) to avoid malformed strings causing problems. It simply checks the size (at least 5 items) and also checks to make sure a row actually has “some” data. If a row is just empty strings, then it is ignored.
Private Function RowHasData(items As String())
If (items.Count >= 5) Then
For Each item In items
If (item <> "") Then Return True
Next
End If
Return False
End Function
I am guessing it would be just as easy to add the new rows “directly” to the BindingSource’s DataSource. In the example below, the code is adding the row “directly” to the DataTable that is used as a DataSource to the BindingSource. I am confident you could do the same thing with a List<T> by simply adding a new object to the list. Below is a complete example using a BindingSource and a DataTable. This simply adds the rows to the bottom of the table.
Dim gridTable1 As DataTable
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
PasteClipboard()
End Sub
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
gridTable1 = GetTable()
FillTable(gridTable1)
AdjustmentsBindingSource.DataSource = gridTable1
AdjustmentsDataGridView.DataSource = AdjustmentsBindingSource
End Sub
Private Function GetTable() As DataTable
Dim dt = New DataTable()
dt.Columns.Add("FontName", GetType(String))
dt.Columns.Add("FontSize", GetType(String))
dt.Columns.Add("LetterCombo", GetType(String))
dt.Columns.Add("Adjustment", GetType(String))
dt.Columns.Add("HorV", GetType(String))
Return dt
End Function
Private Sub FillTable(dt As DataTable)
For index = 1 To 10
dt.Rows.Add("Name_" + index.ToString(), "Size_" + index.ToString(), "Combo_" + index.ToString(), "Adjust_" + index.ToString(), "HorV_" + index.ToString())
Next
End Sub
Private Sub PasteClipboard()
If Clipboard.ContainsText Then
Dim sLines() As String = Clipboard.GetText.Split(vbCrLf)
Try
Dim dataRow As DataRow
For Each sLine As String In sLines
Dim Items() As String = sLine.Split(vbTab)
If (RowHasData(Items)) Then
dataRow = gridTable1.NewRow()
dataRow("FontName") = Items(0)
dataRow("FontSize") = Items(1)
dataRow("LetterCombo") = Items(2)
dataRow("Adjustment") = Items(3)
dataRow("HorV") = Items(4)
gridTable1.Rows.Add(dataRow)
End If
Next
Catch ex As Exception
MessageBox.Show("Error: " + ex.Message)
End Try
End If
End Sub
Private Function RowHasData(items As String())
If (items.Count >= 5) Then
For Each item In items
If (item <> "") Then Return True
Next
End If
Return False
End Function
Hope the code helps…
Last but important, I am only guessing that you may have not “TESTED” the different ways users can “SELECT” data and “how” other applications “copy” that selected data. My previous tests using the WIN-OS “Clipboard” can sometimes give unexpected results. Example, if the user selects multiple items using the ”Ctrl” key to “ADD” to the selection, extra rows appeared in the Clipboard if the selection was not contiguous. My important point is that using the OS clipboard is quirky IMHO. I recommend LOTS of testing on the “different” ways the user can select the data. If this is not an issue then the code above should work.

NUnit test how to prepare/modify function

Probably simple question but i am beginner in this area. Have NUnit project and would like to test similar functions like this example below. As you see i pass some id to it and then looping through, when data found i delete records.
Now with regards to test i would like instead of delete i would like to collect all of those id's (those which are going to be deleted) and compare them in my test project.
What i got on mind is to extend my function with additional variables to store id's then add additional paraemeter to function like isTest as Boolean and in places where are deletes put statment like: if Test then not delete but add ids to variables, but i think its very bad idea.
How this should be accomplished right way?
Public Sub DelEverythingAssociatedWithSection(secId As Integer)
Using con As New SqlConnection(strcon)
con.Open()
Using transaction = con.BeginTransaction
Try
Dim dtHtmlSection_KatSubkatDAL As New DataTable
dtHtmlSection_KatSubkatDAL = CType(New HtmlSection_KatSubkatDAL().GetAllBySecId(secId), DataTable)
If dtHtmlSection_KatSubkatDAL IsNot Nothing Then
For Each sec_katsubkat As DataRow In dtHtmlSection_KatSubkatDAL.Rows
Dim dtSubSec_SecKatSubKat_SubSubKat As New DataTable
dtSubSec_SecKatSubKat_SubSubKat = CType(New DALSubSec_SecKatSubKat_SubSubKat().GetAllBySec_KatSub(CInt(sec_katsubkat(0))), DataTable)
If dtSubSec_SecKatSubKat_SubSubKat IsNot Nothing Then
For Each subsec As DataRow In dtSubSec_SecKatSubKat_SubSubKat.Rows
Dim dtHtmlSentence_SubSec_SecKatSubKat_SubSubKat As New DataTable
dtHtmlSentence_SubSec_SecKatSubKat_SubSubKat = CType(New HtmlSentence_SubSec_SecKatSubKat_SubSubKatDAL().GetAllBySubSec_SecKatSubKat_SubSubKat(CInt(subsec(0))), DataTable)
If dtHtmlSentence_SubSec_SecKatSubKat_SubSubKat IsNot Nothing Then
For Each sent As DataRow In dtHtmlSentence_SubSec_SecKatSubKat_SubSubKat.Rows
Dim dtArtikel_Beschreibung As New DataTable
dtArtikel_Beschreibung = CType(New Artikel_BeschreibungDAL().GetAllBySentence(CInt(sent(0))), DataTable)
If dtArtikel_Beschreibung IsNot Nothing Then
For Each artBesch As DataRow In dtArtikel_Beschreibung.Rows
Call New Artikel_BeschreibungDAL().Delete(CInt(artBesch(0)), transaction)
Next
End If
Call New HtmlSentence_SubSec_SecKatSubKat_SubSubKatDAL().Delete(CInt(sent(0)), transaction)
Next
End If
Call New DALSubSec_SecKatSubKat_SubSubKat().Delete(CInt(subsec(0)), transaction)
Next
End If
Call New HtmlSection_KatSubkatDAL().Delete(CInt(sec_katsubkat(0)), transaction)
Next
End If
Call New DALSection().Delete(secId, transaction)
'If we made it this far without an exception, then commit.
transaction.Commit()
Catch ex As Exception
transaction.Rollback()
Throw 'Rethrow exception.
End Try
End Using
End Using
End Sub
I think you are making it too complicated. Just write a test like this... (pseudo-code)
// Arrange
Set up a section with some data.
Assume.That(section exists with data) // optional, in case setup fails
// Act
Delete the section
// Assert
Try to get the data in the section
Assert.That(no data is found)

Database Lookup From ComboBox selection

I have a question about database values and how to determine the id of a value that has been changed by the user at some point.
As it is currently set up there is a combobox that is populated from a dataset, and subsequent text boxes whose text should be determined by the value chosen from that combobox.
So let's say for example you select 'Company A' from the combobox, I would like all the corresponding information from that company's row in the dataset to fill the textboxes (Name = Company A, Address = 123 ABC St., etc.,)
I am able to populate the combobox just fine. It is only however when I change the index of the combobox that this specific error occurs:
An unhandled exception of type 'System.Data.OleDb.OleDbException'
occurred in System.Data.dll
Additional information: Data type mismatch in criteria expression.
Here is the corresponding code:
Imports System.Data.OleDb
Public Class CustomerContact
Dim cn As New OleDbConnection("Provider=Microsoft.ACE.OLEDB.12.0;Data Source=|datadirectory|\CentralDatabase.accdb;")
Dim da As New OleDbDataAdapter()
Dim dt As New DataTable()
Private Sub CustomerContact_Load(sender As Object, e As EventArgs) Handles MyBase.Load
cn.Open()
da.SelectCommand = New OleDbCommand("select * from Customers", cn)
da.Fill(dt)
Dim r As DataRow
For Each r In dt.Rows
cboVendorName.Items.Add(r("Name").ToString)
cboVendorName.ValueMember = "ID"
Next
cn.Close()
End Sub
Private Sub cboVendorName_SelectedIndexChanged(sender As Object, e As EventArgs) Handles cboVendorName.SelectedIndexChanged
cn.Open()
da.SelectCommand = New OleDbCommand("select * from Customers WHERE id='" & cboVendorName.SelectedValue & "'", cn)
da.Fill(dt)
Dim r As DataRow
For Each r In dt.Rows
txtNewName.Text = "Name"
txtAddress.Text = "Address"
Next
cn.Close()
End Sub
The error is caught at Line 24 of this code, at the second da.Fill(dt) . Now obviously from the exception I know that I am sending in a wrong datatype into the OleDbCommand, unfortunately I am a novice when it comes to SQL commands such as this. Also please keep in mind that I can't even test the second For loop, the one that is supposed to fill the Customer information into textboxes (for convenience I only copied the first two textboxes, of which there are nine in total). I am think I could use an If statement to determine if the row has been read, and from there populate the textboxes, but I will jump that hurdle when I can reach it.
Any guidance or suggestions would be much appreciated. Again I am a novice at managing a database and the code in question pertains to the project my current internship is having me write for them.
Since you already have all the data from that table in a DataTable, you dont need to run a query at all. Setup in form load (if you must):
' form level object:
Private ignore As Boolean
Private dtCust As New DataTable
...
Dim SQL As String = "SELECT Id, Name, Address, City FROM Customer"
Using dbcon = GetACEConnection()
Using cmd As New OleDbCommand(SQL, dbcon)
dbcon.Open()
dtCust.Load(cmd.ExecuteReader)
End Using
End Using
' pk required for Rows.Find
ignore = True
dtCust.PrimaryKey = New DataColumn() {dtCust.Columns(0)}
cboCust.DataSource = dtCust
cboCust.DisplayMember = "Name"
cboCust.ValueMember = "Id"
ignore = False
The ignore flag will allow you to ignore the first change that fires as a result of the DataSource being set. This will fire before the Display and Value members are set.
Preliminary issues/changes:
Connections are meant to be created, used and disposed of. This is slightly less true of Access, but still a good practice. Rather than connection strings everywhere, the GetACEConnection method creates them for me. The code is in this answer.
In the interest of economy, rather than a DataAdapter just to fill the table, I used a reader
The Using statements create and dispose of the Command object as well. Generally, if an object has a Dispose method, put it in a Using block.
I spelled out the columns for the SQL. If you don't need all the columns, dont ask for them all. Specifying them also allows me to control the order (display order in a DGV, reference columns by index - dr(1) = ... - among other things).
The important thing is that rather than adding items to the cbo, I used that DataTable as the DataSource for the combo. ValueMember doesn't do anything without a DataSource - which is the core problem you had. There was no DataSource, so SelectedValue was always Nothing in the event.
Then in SelectedValueChanged event:
Private Sub cboCust_SelectedValueChanged(sender As Object,
e As EventArgs) Handles cboCust.SelectedValueChanged
' ignore changes during form load:
If ignore Then Exit Sub
Dim custId = Convert.ToInt32(cboCust.SelectedValue)
Dim dr = dtCust.Rows.Find(custId)
Console.WriteLine(dr("Id"))
Console.WriteLine(dr("Name"))
Console.WriteLine(dr("Address"))
End Sub
Using the selected value, I find the related row in the DataTable. Find returns that DataRow (or Nothing) so I can access all the other information. Result:
4
Gautier
sdfsdfsdf
Another alternative would be:
Dim rows = dtCust.Select(String.Format("Id={0}", custId))
This would return an array of DataRow matching the criteria. The String.Format is useful when the target column is text. This method would not require the PK definition above:
Dim rows = dtCust.Select(String.Format("Name='{0}'", searchText))
For more information see:
Using Statement
Connection Pooling
GetConnection() method aka GetACEConnection

Why is this object function exiting?

A very simple issue im having trying to return a dataset from a VB.NET object function.
The following shows my function that is currently exiting from the function as soon as the SQL query is run and just before the new object connection is created.
The edit form is called here:
edit.Show()
Within the edit form, the following is run to to retrieve the details of the selected data in the database fro a retrieved datatset of the 'editEntry' method based on the ID set at the constructor.
Private Sub edit_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
Dim editDetails As New DBHandler(ID)
Dim returnedDetails As New DataSet
returnedDetails = editDetails.editEntry()
Dim nameReturned As Object = returnedDetails.Tables("editedTable").Rows(0)(1)
Dim firstNameEdit As String = nameReturned.ToString()
TextBox1.Text = firstNameEdit
This is the function where the problem is occuring. Nothing is being returned from the query
Constructor where the ID is set:
Public Sub New(ByVal ID As Integer)
IDofFault = ID
End Sub
The function of the class:
Public Function editEntry() As DataSet
Dim editDataSet As New DataSet
Dim editSql As String = "SELECT * FROM duraGadget WHERE _id = " + IDofFault + ""
'Exiting from the function here
Dim connectionEdit As New OleDbConnection(conString)
Dim editAdapter As New OleDbDataAdapter(editSql, connectionEdit)
connectionEdit.Open()
editAdapter.Fill(editDataSet, "editedTable")
connectionEdit.Close()
Return editDataSet
End Function
There is no error it simply exits from the function and im not sure why.
You could be receiving an exception and your visual studio debug settings are not configured to stop you on those types of exceptions.
Wrap the contents of the EditEntry function in a Try / Catch block, and put a break point inside the catch. See if that triggers and look at the exception details for more info on what occurred.
Very silly error this was guys. I simply stored the ID value as a sting...then tried to pass it as an Integer to the constructor...the result?.....Conversion exception.

<VB> Highlighting Selected dates from database in Calendar control

I am currently using VB. I want to do a Calendar Control which have dates highlighted/selected. All these dates are retrieved from a database.
First thing I need to know is how to put all the dates into an array
Second thing I need to know is how to highlight all the dates in the array.
I have done some research on the internet and they said something about selectedDates and selectedDates collection and dayrender. But frankly speaking, I can't really find any VB codes regarding this. Dates format will be in dd/MM/yyyy
Imports System.Data.SqlClient
Partial Class _Default
Inherits System.Web.UI.Page
Sub Page_Load(ByVal sender As Object, ByVal e As EventArgs)
Dim connectionString As String = ConfigurationManager.ConnectionStrings("CleanOneConnectionString").ConnectionString
Dim connection As SqlConnection = New SqlConnection(connectionString)
connection.Open()
Dim sql As String = "Select schedule From OrderDetails Where schedule is not null"
Dim command As SqlCommand = New SqlCommand(sql, connection)
Dim reader As SqlDataReader = command.ExecuteReader()
If (reader.Read()) Then
If (reader.HasRows) Then
While reader.Read()
myCalendar.SelectedDates.Add(CType(reader.GetDateTime(0), Date))
End While
End If
End If
reader.Close()
connection.Close()
myCalendar.SelectedDayStyle.BackColor = System.Drawing.Color.Red
End Sub
End Class
My Calendar
<asp:Calendar ID="myCalendar" runat="server" ShowGridLines="True">
</asp:Calendar>
Updated with what I have done, but still does not show
Thanks for the help
Assume you have a DataTable named myDates, and a Calendar control named myCalendar:
For i As Int = 0 To myDates.Rows.Count - 1
myCalendar.SelectedDates.Add(CType(myDates.Row(i)(0), Date)
Next
You can declare the highlighting in your markup:
<asp:Calendar ID="myCalendar" runat="server">
<SelectedDayStyle BackColor="red" />
</asp:Calendar>
Or programatically:
myCalendar.SelectedDayStyle.BackColor = System.Drawing.Color.Red
UPDATE For SqlDataReader (VB.NET this time)
If reader.HasRows Then
While reader.Read()
myCalendar.SelectedDates.Add(CType(reader(0), Date)
End While
End If
UPDATE based on OP's code
Are you getting any errors when the code runs? SqlDataReader.GetDateTime will throw an InvalidCastException if the column being read isn't a DateTime column.
I'm wondering if it's a format issue? Can you verify the data type of the column in the database, as well as the format the date is being stored in?
I've modified your code a bit with a couple of suggestons.
Imports System.Data.SqlClient
Partial Class _Default Inherits System.Web.UI.Page
Sub Page_Load(ByVal sender As Object, ByVal e As EventArgs)
Dim connectionString As String = ConfigurationManager.ConnectionStrings("CleanOneConnectionString").ConnectionString
' Using blocks will automatically dispose of the object, and are
' pretty standard for database connections
Using connection As New SqlConnection(connectionString)
connection.Open()
Dim sql As String = "Select schedule From OrderDetails Where schedule is not null"
Dim command As SqlCommand = New SqlCommand(sql, connection)
Dim reader As SqlDataReader = command.ExecuteReader()
' This is not needed - in fact, this line will "throw away"
' the first row in the row collection
'If (reader.Read()) Then
If (reader.HasRows) Then
While reader.Read()
myCalendar.SelectedDates.Add(CType(reader.GetDateTime(0), Date)) End While
End If
reader.Close()
' Not needed because of the Using block
'connection.Close()
End Using
myCalendar.SelectedDayStyle.BackColor = System.Drawing.Color.Red
End Sub
End Class
For the second part of your question, you can see on this post how to highlight specified days, albeit using C# syntax.
I'm going to assume a L2S format is being used to fetch the dates for now (comment with actual implementation if you need better detail).
I would recommend the dates you want to select be held in a variable on the form (instead of scoped to the function) to prevent database queries running everytime a day is rendered. With that in mind, here is some sample code (free-hand so please excuse and comment on basic/troubling syntax issues):
Private DatesToHighlight As IEnumerable(Of Date)
' implementation details provided so commented this bit out, see EDIT below
'Protected Sub PopulateDatesToHighlight()
' DatesToHighlight = db.SomeTable.Select(Function(n) n.MyDateField)
'End Sub
Protected Sub DayRenderer(ByVal object As Sender, ByVal e As DayRenderEventArgs)
If DatesToHighlight.Contains(e.Day.Date) Then
e.Cell.BackColor = System.Drawing.Color.Red
End If
End Sub
As specified in the question I linked, you'll need to change the markup for the calendar control to provide the ondayrender parameter like so ondayrender="DayRenderer"
Regarding changing your dates to an array, it depends what format they are in at the start. If in anything that inherits from IEnumerable then you can use ToArray(). If they are just variables you can initialise the array with the dates
Dim myDates() As Date = {dateVar1, dateVar2}
Hope that helps?
EDIT: (In response to OP's addition of code)
To get from your data reader to an array (though I'm not convinced you need an array) I would do the following:
' using the variable I declared earlier
DatesToHighlight = New IEnumerable(Of Date)
If reader.HasRows Then
Dim parsedDate As Date
While reader.Read()
If Date.TryParse(reader(0), parsedDate) Then
DatesToHighlight.Add(parsedDate)
End If
End While
End If
Dim myArrayOfDates() As Date = DatesToHighlight.ToArray()