I'm doing something to be supposedly straight and simple, yet I cannot make it work. I've an Access DB (64 bits), which is accessed by an OleDB connection. If I bind the values of a DataGridViewComboBoxColumn to a DataTable's fields ([int]id and [string]name), the ComboBoxes are rendered correctly (it displays the "DisplayMember", and keeps the "ValueMember" as an internal value). Yet, as soon as I set the target field for storing it's value with "DataPropertyName", the ComboBox now renders the "ValueMember" - or rather the "DataPropertyName" takes priority [??] - (see the attached images).
The code used is shown in the following snipet:
private OleDbDataAdapter empleadosDA_local = new OleDbDataAdapter();
private OleDbDataAdapter activosDA_local = new OleDbDataAdapter();
private DBLocal dbConn_local;
internal DataSet catalogosDS, auxsDS;
internal BindingSource activosBind = new BindingSource();
......
private void BindControls() {
DataGridViewComboBoxColumn activos_responsable1DGComboBox;
activosBind.DataMember = "Activos";
activosBind.DataSource = catalogosDS;
this.Activos_dataGrid.AutoGenerateColumns = false;
// Create DGComboBox
activos_responsable1DGComboBox = new DataGridViewComboBoxColumn();
activos_responsable1DGComboBox.ValueMember = "id_empleado";
activos_responsable1DGComboBox.DisplayMember = "nombre";
activos_responsable1DGComboBox.DataSource = auxsDS.Tables["Responsables1"];
activos_responsable1DGComboBox.HeaderText = "Responsable1:";
// activos_responsable1DGComboBox.DataPropertyName = "idEmpleado1"; <--- this is the line that creates the unexpected behaviour
this.Activos_dataGrid.Columns.Insert(6, activos_responsable1DGComboBox);
this.Activos_dataGrid.Columns[6].Name = "Activos_dgResponsable1";
this.Activos_dataGrid.Columns["Activos_dgResponsable1"].Width = 195;
this.Activos_dataGrid.Columns["Activos_dgResponsable1"].DefaultCellStyle.Alignment = DataGridViewContentAlignment.MiddleLeft;
this.Activos_dataGrid.DataSource = activosBind;
I've done this in other Forms and it works fine using this same logic, but I just cannot find the reason why this fails. Any help will be greatly appreciated.
EDIT:
JohnG asked for the schemas that feeds the DataSource of the ComboBox and the target table where it's stored, so we can identify any data type inconsistencies:
ComboBox DataSource:
TargetTable which feeds the DataGridView:
So it results that the ComboBox's source table has an autoincrement field (the primary key), and this is forcibly a "number long integer", and I was using for storing it a table with a field defined as integer. Setting the target field to long int solved the issue.
I am doing a small system using Ms. Access (the database has more than 10 tables ) connecting to visual studio. I made a public class for opening the connection to the database so I can use it in every form. Everything is working and I can get the data from the database But any inserting or deleting data in forms, the database in ms access not getting the update. I can see the new records in forms but nothing in the database.
Imports System.Data.OleDb
Public Class dbconnectClass1
'create db connection
Private DBcon As New OleDbConnection("Provider=Microsoft.ACE.OLEDB.12.0; Data Source=dental_clinic.accdb;")
'prepare db command
Private dbcmd As OleDbCommand
' db data
Public DBDA As OleDbDataAdapter
Public DBDT As DataTable
'query parameters
Public params As New List(Of OleDbParameter)
'query statics
Public recordcount As Integer
Public Exception As String
Public Sub ExecQuery(query As String)
'reset query status
recordcount = 0
Exception = ""
Try
'open connection
DBcon.Open()
'create db command
dbcmd = New OleDbCommand(query, DBcon)
'load params into dbcommand
params.ForEach(Sub(p) dbcmd.Parameters.Add(p))
'clear params list
params.Clear()
'excute command and fill dataset
DBDT = New DataTable
DBDA = New OleDbDataAdapter(dbcmd)
recordcount = DBDA.Fill(DBDT)
Catch ex As Exception
Exception = ex.Message
End Try
'close the database connection
If DBcon.State = ConnectionState.Open Then DBcon.Close()
End Sub
'include query and command parameters
Public Sub addparam(name As String, value As Object)
Dim newparam As New OleDbParameter(name, value)
params.Add(newparam)
End Sub
End Class
This my code inside the forms:
Public Class NewExpense
Private access As New dbconnectClass1
' a varuble having the appointment Id to connect between 2 forms
Private appointmentNo As Integer
Private Function NoError(Optional report As Boolean = False) As Boolean
If Not String.IsNullOrEmpty(access.Exception) Then
If report = True Then MsgBox(access.Exception)
Return False
Else
Return True
End If
End Function
Private Sub Savebuttum_Click(sender As Object, e As EventArgs) Handles
Savebuttum.Click
Dim oDate As DateTime = Convert.ToDateTime(DateTimePicker1.Value)
access.addparam("#expensenme", expensenmtXT.Text)
access.addparam("#expensedetail", ExpenseDetailTXT.Text)
access.addparam("#expenseamount", ExpenseAmountTXT.Text)
access.addparam("#expensedate", oDate)
access.addparam("#expensepaidTo", paidtoTXT.Text)
access.ExecQuery("INSERT INTO Expense (Expenses_name, expense_details,
expenses_amount, ExpenseDate_Paid, ExpensePaid_To) Values (#expensenme,
#expensedetail, #expenseamount, #expensedate, #expensepaidTo);")
'report on errors
If Not String.IsNullOrEmpty(access.Exception) Then
MsgBox(access.Exception) : Exit Sub
'success
access.DBDA.Update(access.DBDT)
MsgBox("Expense Has been Added Successfully")
End Sub
End Class
Hum, you have this:
params.ForEach(Sub(p) dbcmd.Parameters.Add(p))
Great, we add the parmaters - looks good to go!!!
then, next line CLEARS all the work above!!! (the parameters are removed!!!!)
'clear params list
params.Clear()
Next up? Many will build a connection object, then a reader, and then a adaptor. But you ONLY need a data adaptor if you going to update a data table. if you just going to execute a command, then you don't need the data table, and you don't need a adaptor FOR that table. Adaptor = ability to modify a existing datatable (or dataset).
You are MUCH better to use the command object.
Why?
Because the command object has a connection object (don't need a separate one)
Because the command object has a data reader for you (no need for a whole data adaptor to JUST fill a table. And remember, you don't need a whole data adaptor UNLESS you are going to send/update a data table back to the database.
And because the command object has the command text, then you don't even need a variable for that!!!
And because all objects are in "one object", then really all you need is something to handy get you the connection.
So, for your insert example, we really don't gain by having that object, do we?
Ok, so here is your insert code without using those extra objects:
so in the following, I declare ONE variable, - the sql command object.
And do the insert
And as FYI? Your save button is not a save - but a insert button - every time you hit it, you will insert a new row. Lets deal with that issue in a bit.
So, here is our code:
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Using cmdSQL As New OleDbCommand("INSERT INTO Expense
(Expenses_name, expense_details,expenses_amount, ExpenseDate_Paid, ExpensePaid_To)
Values (#expensenme,#expensedetail, #expenseamount, #expensedate, #expensepaidTo)",
New OleDbConnection(My.Settings.TESTOLEDB))
With cmdSQL.Parameters
.Add("#expensenme", OleDbType.WChar).Value = expensenmtXT.Text
.Add("#expensedetail", OleDbType.WChar).Value = ExpenseDetailTXT.Text
.Add("#expenseamount", OleDbType.Currency).Value = ExpenseAmountTXT.Text
.Add("#expensedate", OleDbType.DBDate).Value = DateTimePicker1.Value
.Add("#expensepaidTo", OleDbType.WChar).Value = paidtoTXT.Text
End With
cmdSQL.Connection.Open()
cmdSQL.ExecuteNonQuery()
End Using
So things in above:
We have strong data typing and converstion.
Because of this, note how I did NOT have to create a separate date/time variable here.
Note the SAME for "money" or so called currency conversion - again strong data type by using parameters this way.
And is this a date only, or a date+ time value? If it is date, then
.Add("#expensedate", OleDbType.DBDate).Value = DateTimePicker1.Value
If it was/is a date + time, then this:
.Add("#expensedate", OleDbType.DBTimeStamp).Value = DateTimePicker1.Value
So, notice how all your object stuff REALLY did not help one bit, and in fact you did not really save code, and above actually had LESS variables defined to do the whole job.
Now, back to the insert issue/problem. (you save is doing a insert). But what about editing existing records?
So, I would suggest you work out the problem this way:
You create/get/have/assume a data row for the form.
The form takes the data row, fills the controls. You edit, and when you hit save, the data row is sent back to the datbase. So, once this works, then to add? Well, the add code will CREATE a new data row, save to database and THEN you send that new data row to the above eixsting form that can edit a data row, and can save a data row. So the form now is able to deal with both issues (adding vs editing existing). If the user dont' want the row, then you offer a delete button.
And REALLY nice is a data row means you don't deal with SQL, and don't deal with parmaters!!
So the code (desing pattern) I use is thus this:
dim da as oledbDataAdaptor
dim myTable as DataTable = MyRstEdit("SELECT * from tblHotels WHERE ID = " & lngID,da)
dim MyDataRow as DataRow = myTable.Rows(0)
' code to fill controls
txtHotelName.Text = MyDataRow("HotelName")
txtCity.Text = MyDataRow("City")
' etc. etc. etc.
Now to save? Well I put the values back into that DataRow like this:
MyDataRow("HotelName") = txtHotelName.Text
MyDataRow("City") = txtCity.Text
MyDateRow("BookingDate") = txtTimePick1.Value
da.Update(MyTable)
Notice how I don't have parameters, and even strong data type checking occurs for say the above Date/Time booking date column.
And the above is nice, since I don't have to deal with ANY parmaters to udpate a row of data.
The MyRstEdit routine looks like this and returns byREf a "da" (data adaptor).
Public Function MyrstEdit(strSQL As String, Optional strCon As String = "", Optional ByRef oReader As SqlDataAdapter = Nothing) As DataTable
' Myrstc.Rows(0)
' this also allows one to pass custom connection string - if not passed, then default
' same as MyRst, but allows one to "edit" the reocrdset, and add to reocrdset and then commit the update.
If strCon = "" Then
strCon = GetConstr()
End If
Dim mycon As New SqlConnection(strCon)
oReader = New SqlDataAdapter(strSQL, mycon)
Dim rstData As New DataTable
Dim cmdBuilder = New SqlCommandBuilder(oReader)
Try
oReader.Fill(rstData)
oReader.AcceptChangesDuringUpdate = True
Catch
End Try
Return rstData
End Function
So, now in vb.net, I actually find it is LESS code then even writing + using recordsets in MS-Access VBA code.
However, BEFORE you go down ANY of the above road?
Have you considered using the vb.net data binding features. Data-binding in vb.net means that you do NOT write ANY of the above code. it means that vb.net will do all of the dirty work, and write and setup ALL OF the code for you to edit data on a form. The end result is you don't write any code to update a table.
You do have to use + create a "data set". Once done, then you just drag controls onto the form, and you even get this. So you just drop in a dataset, table adaptor, Binding navagator, and you get this:
Note now the tool bar at the top (and you can place it on teh bottom if you wish). So you get this:
So that WHOLE form was created without having to write ONE line of code. And you can see we have navigation, edits and saves and even the ability to add. So, you can build up a editing form - and it thus becomes similar to say working in MS-Access and ZERO lines of code is required to build the above form.
However, if you ARE going to roll your own code? Then use a data row. That way you can shuffle data to/from the table, and NOT have to use parmaters and SQL update and insert statements - but ONLY have nice clean code in which you shove, or get values from that data row. .net will "write" all the update stuff for you.
I am creating an app that has to create controls with specific parameters (some of which are custom parameters used for other applications) so the original method was like this
PSEDO CODE(kinda)
Dim ControlType1Color() as color
Dim ControlType1Name() as string
Dim ControlType1Parameter() as string
...
Dim ControlType2Color() as color
...
However when implementing that notation it ends up causing me to do quite a few test statements in order to recognize which type of control it is, Then find the correct variable to use. Which is honestly a waste of code space which could be saved.
While I was researching for something similar to java Objects (i believe?) I came across collections which should be what I need. However I need to know how I can implement it for this? Does a collection have infinite length? Can you use it as an array? And can you nest it with an array so you have an Array of Collections or is that just unnecessary?
EDIT FOR CLARIFICATION:
I am trying to record my controls that I created in run-time basically and try to use a single variable to record all Data
Lets say I have two textboxes and one button
My "Array of Collections" or whatever it would be would be like
AoC(0).Color = color
AoC(0).Type = Textbox
AoC(0).ID = ID
Aoc(1).Type = Textbox
...
Aoc(2).Type = Button
...
So if I needed to change anything I just change this thing only. I can handle the actual changes in code, I just need to know how to store it in the memory without using 5-15 variables needlessly.
You need to do something like this:
Private Structure ControlDetail
Public Color As System.Drawing.Color
Public ControlType As Type
Public ID As String
End Structure
Then you can define your data as:
Dim Aoc = New ControlDetail() _
{ _
New ControlDetail() With { .Color = System.Drawing.Color.Red, .ControlType = GetType(TextBox), .ID = "txtFoo1" }, _
New ControlDetail() With { .Color = System.Drawing.Color.Blue, .ControlType = GetType(Button), .ID = "butFoo2" } _
}
Then you can get access to the data as Aoc(0).ID, for example.
I have convert an image to be saved in SQL Server Database as Binary with column name as "img". I have PictureBox1 ready to show the image.
Now I want to import the binary data back into image, and I'm trying this code in VB.net for example:
Dim queries As String
queries = "SELECT * from StudentData where Std_fname='" & ComboBox1.Text & "'"
Dim com As New SqlCommand(queries, sqlconn)
sqlconn.Open()
Dim ds As New SqlDataAdapter(queries, sqlconn)
Dim dr As SqlDataReader
dr = com.ExecuteReader()
While dr.Read
Std_fnameTextBox.Text = dr("Std_fname")
Std_lnameTextBox.Text = dr("Std_lname")
AgeTextBox.Text = dr("age")
AddressTextBox.Text = dr("address")
StateTextBox.Text = dr("state")
CityTextBox.Text = dr("city")
CountryTextBox.Text = dr("country")
Ic_passportTextBox.Text = dr("ic_passport")
DobDateTimePicker.Text = dr("dob")
PictureBox1.Image = dr("img") 'Here is the problem. If I run it, it ask me to convert Binary to Image first.
End While
sqlconn.Close()
The problem is, I don't know how to convert binary to image in this situation. And yes, I've been googling for it, but can't seem to get the right answer.
dr("img") returns either DBNull.Value, or a byte[]. You can use the stream overload of the Bitmap constructor to load this. In C# (should be easy to translate to VB), you can do it like this:
var imageData = (byte[])dr["img"];
using (var ms = new MemoryStream(imageData))
{
var bmp = new Bitmap(ms);
// Work with bmp
}
As Mark correctly noted, you're supposed to keep the stream open for the whole life-time of the Bitmap - this is actually a bit trickier than it seems, because the Bitmap doesn't even keep a reference to the stream.
The easiest way to handle this is to clone the bitmap after you create it, to remove the dependency on the stream. Unless you can do whatever work you need to do within the using - unlikely if you want to display it in a PictureBox.
You can also use ImageConverter.ConvertFrom directly, but all it does is create the MemoryStream if used on raw byte[] data :)
I created a Windows Form, with a ComboBox, and a Local Database in Visual Studio 2010. The database has a table with a column whose rows I want to list in the combo box. How can I achieve this?
I tried adding a data source with the column I am interested in through the IDE, but it did not work.
I created a Windows Forms Application with a Windows Form containing a ComboBox.
I created a Local Database containing a Table with a single column and three test rows.
I added a data source containing the column I am interested in.
Finally, I bound the combo box to the data source, but the result is strange.
This is the raw code to accomplish what you are asking:
string strCmd = "";
string strConn = "";
SqlConnection sqlConn = new SqlConnection();
SqlCommand sqlCmd = new SqlCommand(strCmd, sqlConn);
SqlDataReader sqlRdr = new SqlDataReader();
sqlConn.Open();
if (comboBox1.Items.Count > 0)
comboBox1.Items.Clear();
sqlRdr = sqlCmd.ExecuteReader();
while (sqlRdr.Read())
comboBox1.Items.Add(sqlRdr[0].ToString());
sqlRdr.Close();
sqlConn.Close();
There are a few things you will need to wire-up first though. The first one is this:
string strCmd = ""; // Insert your SQL statement here.
Second:
string strConn = ""; // Your db connection string goes here.
Thirdly:
if (comboBox1.Items.Count > 0) // You don't have to use this. It just checks to see
comboBox1.Items.Clear(); // if there is anything in the combobox and clears it.
Lastly, since you are making something that handles interactions between your form and a database, I strongly suggest that you use SqlParameters to prevent SQL Injection attacks.