I want to create a textbox which contents are validated every time the text in it changes. And an image is displayed beside the textbox if it is correct and another image shows up if its not correct. Pretty much like ajax.
Heres the method which I called from the class.
Public Sub read1()
cmd.CommandText = "SELECT * FROM testdb WHERE Name='" & name & "'"
Try
rdr = cmd.ExecuteReader
Dim n As String
If rdr.HasRows Then
n = rdr("Name").ToString
Form1.PictureBox1.Image = Image.FromFile("C:\wamp\www\Dropbox\Public\Check-icon.png")
Else
Form1.PictureBox1.Image = Image.FromFile("C:\wamp\www\Dropbox\Public\Delete-icon.png")
End If
Catch ex As Exception
And here the event which calls for the method:
Private Sub TextBox1_TextChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles TextBox1.TextChanged
x.name = TextBox1.Text
x.read1()
End Sub
End Try
End Sub
Problem is, the method seems to be only called once. And the first image that was displayed stays the same even after inputting a data which does not exist in the database.
I'm connecting this program to a mysql database using odbc. Can you help me determine whats the problem and how to fix it?
Heres the screenshot:
With a DataReader object, you have to call the Read method before you can access the row. I think it would need to look like the following. I am guessing at the VB.Net syntax and method name, but I am reasonably sure it would be simply rdr.Read.
If rdr.HasRows Then
rdr.Read
n = rdr("Name").ToString
Edit In addition, you probably need to close the DataReader. Some .NET data providers allow only a single reader to be open at a time on a given connection, and it seems likely that the ODBC provider is one of them. Without a specific call to close it, you would be relying on the garbage collector to clean it up:
rdr.Close
And while this has nothing to do with the question directly, you should probably use a parameterized query to avoid an SQL injection attack.
Related
Public Sub Table()
Dim Connection As MySqlConnection
Dim DataAdapter As MySqlDataAdapter
Dim Command As MySqlCommand
Connection = New MySqlConnection("server=localhost; userid=root; database=setting;")
Command = New MySqlCommand("Select * FROM table",
Connection)
DataAdapter = New MySqlDataAdapter(Command)
Dim DataTable As New DataSet
DataAdapter.Fill(DataTable)
Ddlname.DataSource = DataTable
Ddlname.DataTextField = "Name"
Ddlname.DataValueField = "Name"
Ddlname.DataBind()
End Sub
You don't show your markup, but remember, any button click, any control with autopost back, or any event trigger on the page will re-run the page load event.
So, in theory, even just a plane jane button click might well re-run your code that loads up the combo box. So each event, each click can potenail add or load up the combo box again and again.
So, the design pattern for near EVERY page is to only load the grids, the listbox, dropdowns (combo box) etc. is ONE time.
So, your code would and should look like this:
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
If Not IsPostBack Then
LoadTable()
End If
End Sub
Public Sub LoadTable()
Using Connection As New MySqlConnection("server=localhost; userid=root; database=setting;")
Using Command As New MySqlCommand("SELECT * from table", Connection)
Connection.Open
Dim MyTable As New DataTable
MyTable.Load(Command.ExecuteReader)
Ddlname.DataSource = DataTable
Ddlname.DataTextField = "Name"
Ddlname.DataValueField = "Name"
Ddlname.DataBind()
End Using
End Using
End Sub
So, make sure your page load code has that all important If Not IsPostBack, so you REALLY only load + run your code to load the combo box one time.
So this "is Post back" test? 99% of all your pages will work this. I often thought that asp.net pages should have a "firstLoad" event, since page load WILL and DOES fire each and every time - and does so for ANY button, and ANY code that triggers a page post back. Hence, your combo box will load over and over (and grow and grow), since your running the code to load up the grid, the listbox, or dropdown list each time the page loads. So, adopt, use and "love" the IsPostBack test - you do this for all pages, and 99% if not more of my pages require that test.
In fact, you can't quite much build any functional web page if you fail to adopt the above advice.
I have a program that's main function is to pull data from a MySQL database every x amount of time and adjust the GUI accordingly.
I have a two part problem in that the first thing that happens is that my GUI loads incredibly slowly in the background whilst the connection is being tried, you can literally see each label/box/image loading one by one until the MySQL check is complete.
A shoddy fix would be to add a me.Hide() before the function and then make it reappear after the results of the check have been displayed. Can you please take a look at the code? Will adding the MySQL check to a function and then calling it on Form1_Load help?
The second part of this is that my MySQL connection checker doesn't seem to work, now my host is accepting remote MySQL sessions and it does seem to think about connecting for a while... It's a good 6/7 seconds before the connection error message appears.
I pretty much followed guides and wrote the code myself, I understand each part but I think perhaps my ConnectionString is invalid.
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
MaximizeBox = False
Dim conn As MySqlConnection
conn = New MySqlConnection
conn.ConnectionString = "Server=50.28.x.x; Port=3306; User id=xxx_admin; password=xxxx; Database=xxx_software; Connect Timeout=60;"
Try
conn.Open()
Catch myerror As MySqlException
MsgBox("There was an error connecting to the MySQL Database")
Label42.ForeColor = Color.Red
Label42.Text = "ERROR"
Timer1.Stop()
End Try
Timer1.Start()
Label37.Text = Now.ToShortTimeString()
End Sub
.Connect() is a blocking call, therefore you shouldn't put it into a sub that handles the Load event. Put it into Activated for example and check if it has run or not. You might also want to look into async/await which is officially available since VS2012. Though it's still not a good idea to put anything into Load that can cause an exception, because there is a problem with exception handling in this stage that occurs on 64bit machines running code in a 32bit version (to keep it simple).
The basic idea is to keep the UI-Thread (which handles the redrawing of your forms etc) free from "heavy" work, which means that the best results you will get when using some kind of "threading". And async/await is a very easy way to do it, because it keeps the usual program flow.
Public Class Form1
Private InitDone As Boolean = False
Private Async Sub Form1_Activated(sender As Object, e As EventArgs) Handles Me.Activated
If Not InitDone Then
InitDone = True
' simulate some blocking call
Await Task.Run(Sub() Threading.Thread.Sleep(10000))
Me.Text = "Ready to go"
End If
End Sub
End Class
Async is a decorator which simply "allows" you to use the await keyword in a method. This is needed because await is/was not a reserved word in C# and otherwise it might break old code.
The Await means that after the task has been scheduled, the UI-thread will continue and can redraw, process events etc. When the inner methode finished (usually in its own thread), the UI-thread "jumps" to the line directly after Await and executs that code. Nice an easy way to keep your app responsive AND to maintain a (seemingly) linear flow.
There is much more on async/await of course and you might need to invest some time to get used to it, but I would guess that it's worth any minute invested!
I am trying to retrieve an ADODB recordset from a function in MS-Access 2007 but get a very annoying error message thrown in my face saying: "Argument not optional (Error 449)".
I really can't figure out what I am doing wrong, please help!
Regards,
Stefan
FUNCTION:
Function Rs(sourceSQL As String) As ADODB.Recordset
' Create New Disconnected Recordset
Dim rsConnection As ADODB.Connection
Dim rsRecordset As ADODB.Recordset
Set rsConnection = New ADODB.Connection
rsConnection.Open CurrentProject.Connection
Set rsRecordset = New ADODB.Recordset
rsRecordset.CursorLocation = adUseClient
rsRecordset.Open sourceSQL, rsConnection
Set Rs = rsRecordset
Set rsRecordset.ActiveConnection = Nothing
End Function
FUNCTION CALL:
Private Sub Form_Load()
Call Rs("tblDocumentCode")
Debug.Print Rs.txtDocumentCode(0).Value
End Sub
You are using rs twice, once as a function, once as the name of a recordset:
Private Sub Form_Load()
Set Myrs= Rs("tblDocumentCode")
Debug.Print MyRs(0).Value
End Sub
Assuming that "txtDocumentCode" is a field in the recordset, this:
Private Sub Form_Load()
Call Rs("tblDocumentCode")
Debug.Print Rs.txtDocumentCode(0).Value
End Sub
...should be changed to this:
Private Sub Form_Load()
Debug.Print Rs("tblDocumentCode").Fields("txtDocumentCode").Value
End Sub
So far as I can tell, that should work without needing to assign the recordset returned by the function to a variable.
But let me step back a bit and suggest that fixing this syntactical error begs the question: what's being done her is pretty inadvisable. Every time this function is called, you're opening a new connection, but Access works better with a single connection that is used over and over again. That's true of both Jet/ACE back ends and server back ends. The overhead required to initialize the connection is going to make your forms load really slowly.
But even worse, this is not Access programming -- this is "refugee from a programming environment that lacks bound forms/controls" programming. You should be using ODBC with linked tables and then you can assign recordsources to your forms without having to muck about with ADO recordsets. If you insist on doing what you're doing, you might as well not be using Access at all, because you're discarding 75% or more of the advantages of Access, and you're getting no performance or concurrency benefits from doing so (and buying yourself a world of problems).
Of course, now that I look at it again, you're using the CurrentProject.Connection, and I'm not sure how this interacts with ODBC linked tables. Indeed, I guess it's possible this is an ADP and not an MDB/ACCDB, but that makes even less sense, since you don't need to do anything extra at all in an ADP to populate your forms with ADO recordsets -- it's the default.
So, in general, something is wrong beyond the syntax error -- what you are doing simply doesn't make a lot of sense.
Is there a way to write a hook that will run before changes are committed to an Access DB? I'm looking for a way to block changes if a specific process is currently querying the DB.
You didn't give us much information to work with. Can you adapt something like Create and Use Flexible AutoNumber Fields so specific process first opens a table exclusively? Then any other operations which might change data would have to wait until they can lock that same table.
What is specific process? Do you have a method to determine when/if it is reading data from your database?
If specific process is external to the database, like web server ASP code which uses ADO to fetch data, you could see whether the ADO connection Mode and IsolationLevel properties can help.
Update: I used a form to experiment with the adModeShareDenyWrite Mode property for an ADO connection. While the form is open, other users can open the database, but not make any changes. However, if another user already has the db open when the form attempts to open the connection, it triggers an error.
Perhaps your deployment script could attempt to open a adModeShareDenyWrite connection, and bail out on error.
Option Compare Database
Option Explicit
Dim cn As ADODB.Connection
Private Sub Form_Close()
If Not (cn Is Nothing) Then
cn.Close
Set cn = Nothing
End If
End Sub
Private Sub Form_Load()
Set cn = New ADODB.Connection
cn.ConnectionString = "Provider=Microsoft.Jet.OLEDB.4.0;" & _
"Data Source=\\Cmpq\export\Access\backend\links2003.mdb;" & _
"User Id=admin;Password=;"
cn.Mode = adModeShareDenyWrite
cn.Open
End Sub
There is some literature available at expert's exchange and at teck republic about using the combobox.recordset property to populate a combobox in an Access form.
These controls are usually populated with a "SELECT *" string in the 'rowsource' properties of the control, referencing a table or query available on the client's side of the app. When I need to display server's side data in a combobox, I create a temporary local table and import requested records. This is time consuming, specially with large tables.
Being able to use a recordset to populate a combobox control would allow the user to directly display data from the server's side.
Inspired by the 2 previous examples, I wrote some code as follow:
Dim rsPersonne as ADODB.recordset
Set rsPersonne = New ADODB.Recordset
Set rsPersonne.ActiveConnection = connexionActive
rsPersonne.CursorType = adOpenDynamic
rsPersonne.LockType = adLockPessimistic
rsPersonne.CursorLocation = adUseClient
rsPersonne.Open "SELECT id_Personne, nomPersonne FROM Tbl_Personne"
fc().Controls("id_Personne").Recordset = rsPersonne
Where:
connexionActive: is my permanent ADO connection to my database server
fc(): is my current/active form
controls("id_Personne"): is the
combobox control to populate with
company's staff list
Access version in 2003
Unfortunately, it doesn't work!
In debug mode, I am able to check that the recordset is properly created, with requested columns and data, and properly associated to the combobox control. Unfortunately, when I display the form, I keep getting an empty combobox, with no records in it! Any help is highly appreciated.
EDIT:
This recordset property is indeed available for the specific combobox object, not for the standard control object, and I was very surprised to discover it a few days ago.
I have already tried to use combobox's callback function, or to populate a list with the "addItem" method of the combobox,. All of these are time consuming.
To set a control that accepts a rowsource to a recordset you do the following:
Set recordset = currentDb.OpenRecordset("SELECT * FROM TABLE", dbOpenSnapshot)
Set control.recordset = recordset
Works with DAO Recordsets for sure, I haven't tried ADO recordsets because I don't have any real reason to use them.
When done this way, a simple requery will not work to refresh the data, you must do a repeat of the set statement.
As was said, you have to get the RowSourceType to "Table/Query" (or "Table/RequĂȘte" if in french) in order to show query results in the combobox.
Your memory problems arise from opening the recordset (rsPersonne) without closing it. You should close them when closing/unloading the form (but then again you would have scope problems since the recordset is declared in the function and not in the form).
You could also try to create and save a query with Access's built-in query creator and plug that same query in the RowSource of your combobox. That way the query is validated and compiled within Access.
I found the trick ... the "rowSourceType" property of the combobox control has to be set to "Table/Query". Display is now ok, but I have now another issue with memory. Since I use these ADO recordsets on my forms, memory usage of Access is increasing each time I browse a form. Memory is not freed either by stopping the browsing or closing the form, making MS Access unstable and regularly freezing. I will open a question if I cannot solve this issue
good method with using the Recordset property, thanks for that hint!
Patrick, the method you shown on your page has a big disadvantage (I tried that too on my own): The value list can only be 32 KB, if you exceed this limit the function will throw an error.
The callback method has the big disadvantage that it is very slow and it is called once for every entry which makes it unuseable for a longer list.
Using the recordset method works very well. I needed this because my SQL string was longer than 32 KB (lot of index values for WHERE ID IN(x,x,x,x,x...)).
Here's a simple function which uses this idea to set a recordset to the combobox:
' Fills a combobox with the result of a recordset.
'
' Works with any length of recordset results (up to 10000 in ADP)
' Useful if strSQL is longer than 32767 characters
'
' Author: Christian Coppes
' Date: 16.09.2009
'
Public Sub fnADOComboboxSetRS(cmb As ComboBox, strSQL As String)
Dim rs As ADODB.Recordset
Dim lngCount As Long
On Error GoTo fnADOComboboxSetRS_Error
Set rs = fnADOSelectCommon(strSQL, adLockReadOnly, adOpenForwardOnly)
If Not rs Is Nothing Then
If Not (rs.EOF And rs.BOF) Then
Set cmb.Recordset = rs
' enforces the combobox to load completely
lngCount = cmb.ListCount
End If
End If
fnADOComboboxSetRS_Exit:
If Not rs Is Nothing Then
If rs.State = adStateOpen Then rs.Close
Set rs = Nothing
End If
Exit Sub
fnADOComboboxSetRS_Error:
Select Case Err
Case Else
fnErr "modODBC->fnADOComboboxSetRS", True
Resume fnADOComboboxSetRS_Exit
End Select
End Sub
(The function fnADOSelectCommon opens an ADO recordset and gives it back. The function fnErr shows a message box with the error, if there was one.)
As this function closes the opened recordset there should be no problem with the memory. I tested it and didn't saw any increasing of memory which wasn't released after closing the form with the comboboxes.
In the Unload Event of the form you can additionaly use a "Set rs=Me.Comboboxname.Recordset" and then close it. This should not be necessary regarding memory, but it may be better to free up open connections (if used with a backend database server).
Cheers,
Christian
A combo box control does not have a recordset property. It does have a RowSource property but Access is expecting a SQL string in there.
You can change the RowSourceType to the name of a user defined "callback" function. Access help will give you more information including sample code by positioning yourself on the RowSourceType and pressing F1. I use this type of function when I want to give the users a list of available reports, drive letters, or other data that is not available via a SQL query.
I don't understand what you mean by your third paragraph with respect to using data directly from the server side. Or rather I don't understand what the problem is with using standard queries.
In MS Access, it's ok, but in VB, you may use something like this using adodc (Jet 4.0):
Private sub Form1_Load()
with Adodc1
.commandtype = adcmdtext
.recordsource = "Select * from courses"
.refresh
while not .recordset.eof
combo1.additem = .recordset.coursecode
.recordset.movenext
wend
end with
End Sub