Copy/Paste Image from Clipboard

L

lgbjr

Hello All,

I¡¯m using a context menu associated with some pictureboxes to provide
copy/paste functionality. Copying the image to the clipboard was easy. But
pasting an image from the clipboard is proving to be more difficult. These
pictureboxes are bound to an AccessDB. If the user wants to add an image,
they select an image using an OpenFileDialog:

Dim result As DialogResult = Pic_Sel.ShowDialog()
If (result = DialogResult.OK) Then
Dim fs As FileStream = New FileStream(Pic_Sel.FileName, FileMode.Open,
FileAccess.Read)
Dim rawData() As Byte = New Byte(fs.Length) {}
fs.Read(rawData, 0, System.Convert.ToInt32(fs.Length))
fs.Close()
Table.CurrentRow.Item("Pic1") = rawData
End If

As you can see, I'm not placing the image directly into the picturebox (ie
PB1.image=image.fromfile). I'm putting the image directly into the data
table, then letting the picturebox update from the table. For some reason,
if I place the image directly into the picturebox and let the picturebox
update the table, I get a GDI+ error if I try to save the image from the
table back to a file.

Anyway, I need to do something similar when I do a GetDataObject from the
clipboard. Any ideas?

TIA,
Lee
 
K

Ken Tucker [MVP]

Hi,

Here is a quick example. Loads the northwind databases category
names into a listbox (listbox1) and displays the image in a picture box
(picturebox1).

Dim ds As DataSet

Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As
System.EventArgs) Handles MyBase.Load

Dim strConn As String

Dim conn As SqlClient.SqlConnection

Dim daCustomer As SqlClient.SqlDataAdapter

ds = New DataSet


strConn = "Server = " + Environment.MachineName + "\VSdotNet;"

strConn &= "Database = NorthWind;"

strConn &= "Integrated Security = SSPI;"

conn = New SqlClient.SqlConnection(strConn)

daCustomer = New SqlClient.SqlDataAdapter("Select * from Categories", conn)

ds = New DataSet

daCustomer.Fill(ds, "Categories")

ListBox1.DataSource = ds.Tables("Categories")

ListBox1.DisplayMember = "CategoryName"

End Sub



Private Sub ListBox1_SelectedValueChanged(ByVal sender As Object, ByVal e As
System.EventArgs) Handles ListBox1.SelectedValueChanged

Dim dr As DataRow = ds.Tables("Categories").Rows(ListBox1.SelectedIndex)

Dim ms As New System.IO.MemoryStream

Dim bm As Bitmap

Dim arData() As Byte = dr.Item("Picture")

ms.Write(arData, 78, arData.Length - 78)

bm = New Bitmap(ms)

PictureBox1.Image = bm

End Sub



Ken

-----------------

Hello All,

I¡¯m using a context menu associated with some pictureboxes to provide
copy/paste functionality. Copying the image to the clipboard was easy. But
pasting an image from the clipboard is proving to be more difficult. These
pictureboxes are bound to an AccessDB. If the user wants to add an image,
they select an image using an OpenFileDialog:

Dim result As DialogResult = Pic_Sel.ShowDialog()
If (result = DialogResult.OK) Then
Dim fs As FileStream = New FileStream(Pic_Sel.FileName, FileMode.Open,
FileAccess.Read)
Dim rawData() As Byte = New Byte(fs.Length) {}
fs.Read(rawData, 0, System.Convert.ToInt32(fs.Length))
fs.Close()
Table.CurrentRow.Item("Pic1") = rawData
End If

As you can see, I'm not placing the image directly into the picturebox (ie
PB1.image=image.fromfile). I'm putting the image directly into the data
table, then letting the picturebox update from the table. For some reason,
if I place the image directly into the picturebox and let the picturebox
update the table, I get a GDI+ error if I try to save the image from the
table back to a file.

Anyway, I need to do something similar when I do a GetDataObject from the
clipboard. Any ideas?

TIA,
Lee
 
L

lgbjr

Hi Ken,

I'm not sure whether you misunderstood what I was asking, or whether what
you've provided is way over my head (because I'm not sure I understand)!
Anyway, let me give you a bit more information:

I have a form with several pictureboxes (and other controls) that are bound
to an AccessDB. To avoid the nasty "Generic GDI+ Error", I am using the
following code to store an image in the DB:

Dim result As DialogResult = Pic_Sel.ShowDialog()
If (result = DialogResult.OK) Then
Dim fs As FileStream = New FileStream(Pic_Sel.FileName, FileMode.Open,
FileAccess.Read)
Dim rawData() As Byte = New Byte(fs.Length) {}
fs.Read(rawData, 0, System.Convert.ToInt32(fs.Length))
fs.Close()
M_BTS.CurrentRow.Item("BTS_Pic1") = rawData
End If

To retrieve the image from the DB (save it back to disk), I'm using the
following (where pbfile is the disk file):

Dim cmd As OleDbCommand = New OleDbCommand(sql, conn)
Dim fs As FileStream
Dim bw As BinaryWriter
Dim bufferSize As Integer = 300000
Dim outbyte(300000 - 1) As Byte
Dim retval As Long
Dim startIndex As Long = 0
Dim reader As OleDbDataReader =
cmd.ExecuteReader(CommandBehavior.SequentialAccess)
reader.Read()
fs = New FileStream(pbfile, FileMode.OpenOrCreate, FileAccess.Write)
bw = New BinaryWriter(fs)
startIndex = 0
retval = reader.GetBytes(0, 0, outbyte, 0, bufferSize)
bw.Write(outbyte)
bw.Flush()
bw.Close()
fs.Close()
reader.Close()

The reason I'm doing this is because if I do a pb.image.save, I get a GDI+
error.

So, now I'm able to get images in/out of the AccessDB, while displaying them
in the pictureboxes.

Now, I'm adding a context menu to the Pictureboxes to allow
open/edit/preview/copy/paste of the images. Open, edit, and preview are no
problem. when I load the form, I'm going to the registry and getting the
information for whatever programs the user's system has defined for opening,
editing, and previewing images and using that in my context menu. For the
copy, I'm doing the following:

If CM1.SourceControl Is Pic1 Then
Clipboard.SetDataObject(Pic1.Image, False)
ElseIf CM1.SourceControl Is Pic2 Then
etc.

I can paste the contents of the clipboard to any app that supports images
(Word, Excel, Outlook, etc.).

For paste (into a picturebox), currently I'm using

If CM1.SourceControl Is Pic1 Then
M_BTS.CurrentRow.Item("BTS_PIC1") =
Clipboard.GetDataObject.GetData(DataFormats.Bitmap, True)
ElseIf CM1.SourceControl Is Pic2 Then
etc.

This is where the problem is. If I copy an image from pb1 and paste it to
pb2, the image in the picturebox looks fine (which means that whatever data
is being put in the table is being properly translated by the picturebox).
But, if I try to open the image (save it to disk, then open it, the file is
corrupted, or at minimum, changed.

some examples:

If I store an image from a digital camera (c.jpg) in the AccessDB, then save
it to disk (c1.jpg), the file is exactly the same as the original (including
the MetaData). So, I know my method for getting images into and from the DB
is working.

If I copy an image from one pb to another pb (let's say c.jpg), update the
DB, then try to open the image (save to disk), the image is corrupted. The
original image was 1024x768x24b. The image created from the pasted data is
1024x768x32b. There is no MetaData. And only about the top 20% of the
original image exists. Additionally, the image type for the original image
is JPEG, the image from the pasted data is PNG.

For a smaller file, say 150x150x24b, all of the same corruption occurs, but
I can see the entire image.

So, I don't know if it's the way I'm copying the image to the clipboard, or
the way I'm pasting it back to my app, or some combination of both. I'm
hoping that it's only the way I'm pasting it, as I can't control the way
other apps copy images to the clipboard.

TIA,
Lee
 
K

Ken Tucker [MVP]

Hi,

You dont need to use the clipboard to display an image stored in an
database. Here are two functions to convert an image to and from a format
you can save in a database. Storeimage converts image to binary format.
ConvertToBitmap converts it back. Note the offset for the northwind database
is 78 my storeimage function has a offset of 0. Once you have converted the
image into a bitmap you can use bitmap.save to store the image.

Private Function StoreImage(ByVal bm As Bitmap) As Object

Dim ms As New MemoryStream

Try

bm.Save(ms, Imaging.ImageFormat.Jpeg)

Return ms.GetBuffer

Catch

Return Convert.DBNull

End Try

End Function

Private Function ConvertToBitmap(ByVal data() As Byte, ByVal offset As
Integer) As Bitmap

Dim ms As New System.IO.MemoryStream

Dim bm As Bitmap

ms = New MemoryStream

ms.Write(data, offset, data.Length - offset)

bm = New Bitmap(ms)

Return bm

End Function

Bitmap save
http://msdn.microsoft.com/library/d...tml/frlrfsystemdrawingimageclasssavetopic.asp

http://support.microsoft.com/default.aspx?scid=kb;en-us;818410

Picture box control that I overloaded to support binding to a database.
http://www.gotdotnet.com/Community/...mpleGuid=daf38a9e-0e33-4777-93d3-c664ae2f91f8


Ken

----------------------
Hi Ken,

I'm not sure whether you misunderstood what I was asking, or whether what
you've provided is way over my head (because I'm not sure I understand)!
Anyway, let me give you a bit more information:

I have a form with several pictureboxes (and other controls) that are bound
to an AccessDB. To avoid the nasty "Generic GDI+ Error", I am using the
following code to store an image in the DB:

Dim result As DialogResult = Pic_Sel.ShowDialog()
If (result = DialogResult.OK) Then
Dim fs As FileStream = New FileStream(Pic_Sel.FileName, FileMode.Open,
FileAccess.Read)
Dim rawData() As Byte = New Byte(fs.Length) {}
fs.Read(rawData, 0, System.Convert.ToInt32(fs.Length))
fs.Close()
M_BTS.CurrentRow.Item("BTS_Pic1") = rawData
End If

To retrieve the image from the DB (save it back to disk), I'm using the
following (where pbfile is the disk file):

Dim cmd As OleDbCommand = New OleDbCommand(sql, conn)
Dim fs As FileStream
Dim bw As BinaryWriter
Dim bufferSize As Integer = 300000
Dim outbyte(300000 - 1) As Byte
Dim retval As Long
Dim startIndex As Long = 0
Dim reader As OleDbDataReader =
cmd.ExecuteReader(CommandBehavior.SequentialAccess)
reader.Read()
fs = New FileStream(pbfile, FileMode.OpenOrCreate, FileAccess.Write)
bw = New BinaryWriter(fs)
startIndex = 0
retval = reader.GetBytes(0, 0, outbyte, 0, bufferSize)
bw.Write(outbyte)
bw.Flush()
bw.Close()
fs.Close()
reader.Close()

The reason I'm doing this is because if I do a pb.image.save, I get a GDI+
error.

So, now I'm able to get images in/out of the AccessDB, while displaying them
in the pictureboxes.

Now, I'm adding a context menu to the Pictureboxes to allow
open/edit/preview/copy/paste of the images. Open, edit, and preview are no
problem. when I load the form, I'm going to the registry and getting the
information for whatever programs the user's system has defined for opening,
editing, and previewing images and using that in my context menu. For the
copy, I'm doing the following:

If CM1.SourceControl Is Pic1 Then
Clipboard.SetDataObject(Pic1.Image, False)
ElseIf CM1.SourceControl Is Pic2 Then
etc.

I can paste the contents of the clipboard to any app that supports images
(Word, Excel, Outlook, etc.).

For paste (into a picturebox), currently I'm using

If CM1.SourceControl Is Pic1 Then
M_BTS.CurrentRow.Item("BTS_PIC1") =
Clipboard.GetDataObject.GetData(DataFormats.Bitmap, True)
ElseIf CM1.SourceControl Is Pic2 Then
etc.

This is where the problem is. If I copy an image from pb1 and paste it to
pb2, the image in the picturebox looks fine (which means that whatever data
is being put in the table is being properly translated by the picturebox).
But, if I try to open the image (save it to disk, then open it, the file is
corrupted, or at minimum, changed.

some examples:

If I store an image from a digital camera (c.jpg) in the AccessDB, then save
it to disk (c1.jpg), the file is exactly the same as the original (including
the MetaData). So, I know my method for getting images into and from the DB
is working.

If I copy an image from one pb to another pb (let's say c.jpg), update the
DB, then try to open the image (save to disk), the image is corrupted. The
original image was 1024x768x24b. The image created from the pasted data is
1024x768x32b. There is no MetaData. And only about the top 20% of the
original image exists. Additionally, the image type for the original image
is JPEG, the image from the pasted data is PNG.

For a smaller file, say 150x150x24b, all of the same corruption occurs, but
I can see the entire image.

So, I don't know if it's the way I'm copying the image to the clipboard, or
the way I'm pasting it back to my app, or some combination of both. I'm
hoping that it's only the way I'm pasting it, as I can't control the way
other apps copy images to the clipboard.

TIA,
Lee
 
L

lgbjr

Ken,

Again, thanks for the reply, but also again, I think there's a
misunderstanding. Maybe I'm just not being clear about what I am having
trouble with, or I'm too dense to understand what your saying!

I understand that I don't "need" the clipboard for displaying images from a
DB. If the user wants to place an image in the DB, they click an
open image button and using the code from the previous post, I stream the
image into the DB. Once it's there, the picturebox displays it.

If the user wants to retrieve a copy of an image in the DB (ie. a disk
file), they click a save to disk button and I use the code in the previous
post to stream the image to a disk file. If the original image had MetaData
associated with it, the image that is saved to disk has the same MetaData.

Both of these work with no problems.

Now, if the user doesn't want/need a disk file, but wants to place a copy of
one
of the images in the DB in another app (let's say a Word Document), again, I
use the code from the previous post (in a context menu) to put a copy of the
image from the picturebox (not from the DB) onto the clipboard. The user
goes to the target app and clicks paste. I understand that in this case, any
MetaData associated with the image is not included on the clipboard (which
is ok). I actually used the info from
http://support.microsoft.com/default.aspx?scid=kb;en-us;818410 to verify
that what I'm copying to the clipboard is a valid image (by saving the
contents of the clipboard to a file. The saved image file is exactly the
same as the original image file (minus any MetaData)

So, This also works with no problems.

If the user wants to add an image to the DB and there isn't an image file,
ie.
they want to copy an image from another App (again, let's say Word), and
paste it into a picturebox, using the code in my previous post, I'm pasting
the contents of the clipboard into the DB. This is where the problem is.
Whether I paste the image directly to the picturebox
(Pic1.Image=Clipboard.GetDataObject.GetData(DataFormats.Bitmap, True)) or
paste the image directly to the table as shown in the code below, the image
is corrupted (See previous post for description of corrupted images). I've
also tried setting the AutoConvert flag to False for both methods. Same
problem.

I'm assuming that I need to do something similar to how I store an image in
the DB from disk. I use a filestream to fill a byte array, then store the
byte array directly to the DB, then let the picturebox display the image
from the DB, rather than using Pic1.Image=Image.Fromfile and letting the
picturebox store the image to the DB. The later method causes GDI+ errors if
I subsequently try to save the image from the DB to a file.

So, I guess what I need to know is how to get the contents of the clipboard
into a byte array, so I can store the byte array directly to the DB, and
then let the picbox display the image.

Sorry for any previous confusion!

TIA,
Lee
 
C

Cor Ligthert

lgbjr,

I did not check it, however probably is what you are looking in an object.

Why don't you set a breakpoint there (after) the point you have that object.

Open the quickwatch and sees how that image (most probably a byte array) is
stored in that object?

That is the way I would do this.

I hope this gives some ideas?

Cor
 
L

lgbjr

Ken,

You can ignore my last post. I was obviously too dense to understand what
you were trying to say. I used the storeimage function and passed it the
contents of the clipboard, then saved the return value to the DB. Works
fine!!

Thanks!!

Lee
 
L

lgbjr

Hi Cor,

Thanks for the idea. I was actually in the process of setting breakpoints
and trying to look at the byte array when I got the post from Ken. At first,
I didn't grasp what he was saying, so I sent another post trying to explain
again what I was trying to do. then I took a 5 minute coffee break! when I
sat back down in front of the computer and thought about it, I realized that
Ken sent me the answer I was looking for. I was just to dense to see it when
I first read his post. So, all's well now.

To retrieve an image that's been placed on the clipboard directly to the DB:

Table.CurrentRow.Item("Pic1")=StoreImage(Clipboard.GetDataObject.GetData(DataFormats.Bitmap,
False))

Works like a charm!

cheers,
Lee
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top