I'm using this code to capture desktop
Private Function cc() As Bitmap
Dim s As Screen = Screen.PrimaryScreen
Dim img As New Bitmap(s.Bounds.Width, s.Bounds.Height)
Dim gr As Graphics = Graphics.FromImage(img)
gr.CopyFromScreen(s.Bounds.Location, Point.Empty, s.Bounds.Size)
Return img
End Function
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
Try
Me.PictureBox1.Image = cc()
Catch ex As Exception
MsgBox(ex.Message)
End Try
End Sub
But now I want to capture only the active screen, how can I do that?
Regards,,,,,
for copy active window from our application, Just check the position and size of the window and reduced the copying to this rectangle:
Public Sub ScreenCopy(Optional file As String = "d:\test.jpg")
Dim bounds = Me.Bounds
Using bitmap As New Bitmap(bounds.Width, bounds.Height)
Using g = Graphics.FromImage(bitmap)
g.CopyFromScreen(New Point(bounds.Left, bounds.Top), Point.Empty, bounds.Size)
End Using
bitmap.Save(file, ImageFormat.Jpeg)
Process.Start(file) ' for test purposes
End Using
End Sub
for copy current active window even is not part of our application you need use an api for detect his size and location.
for this use this class (credit: #KvanTTT: https://stackoverflow.com/a/9087955/1271037)
Class ScreenCapturer
<DllImport("user32.dll")> _
Private Shared Function GetForegroundWindow() As IntPtr
End Function
<DllImport("user32.dll")> _
Private Shared Function GetWindowRect(hWnd As IntPtr, ByRef rect As Rect) As IntPtr
End Function
<StructLayout(LayoutKind.Sequential)> _
Private Structure Rect
Public Left As Integer
Public Top As Integer
Public Right As Integer
Public Bottom As Integer
End Structure
Public Function Capture(Optional activeWindowOnly As Boolean = True) As Bitmap
Dim bounds As Rectangle
If Not activeWindowOnly Then
bounds = Screen.GetBounds(Point.Empty)
CursorPosition = Cursor.Position
Else
Dim foregroundWindowsHandle = GetForegroundWindow()
Dim rect = New Rect()
GetWindowRect(foregroundWindowsHandle, rect)
bounds = New Rectangle(rect.Left, rect.Top, rect.Right - rect.Left, rect.Bottom - rect.Top)
CursorPosition = New Point(Cursor.Position.X - rect.Left, Cursor.Position.Y - rect.Top)
End If
Dim result = New Bitmap(bounds.Width, bounds.Height)
Using g = Graphics.FromImage(result)
g.CopyFromScreen(New Point(bounds.Left, bounds.Top), Point.Empty, bounds.Size)
End Using
Return result
End Function
Public Property CursorPosition() As Point
End Class
how to use:
Public Sub ScreenCopy(Optional file As String = "d:\test.jpg")
Dim sc = New ScreenCapturer()
Using bitmap = sc.Capture()
bitmap.Save(file, ImageFormat.Jpeg)
Process.Start(file) ' for test purposes
End Using
End Sub
Only a small change in syntax for VB.net regarding the DLL import:
<DllImport("user32.dll", EntryPoint:="GetForegroundWindow")> Private Shared Function GetForegroundWindow() As IntPtr
End Function
<DllImport("user32.dll", EntryPoint:="GetWindowRect")> Private Shared Function GetWindowRect(hWnd As IntPtr, ByRef rect As Rect) As IntPtr
End Function
Related
The following is the code i m using to convert doc file to image.this works well for a file that contains only one page but if there are more than one page in doc file then it converts only first page of file to image.Can some one suggest me how to convert all pages of doc file to seperate images.
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
Dim objWord As New Microsoft.Office.Interop.Word.Application
Dim objDoc As Microsoft.Office.Interop.Word.Document
Const CF_ENHMETAFILE As Integer = 14
objDoc = objWord.Documents.Open("F:\Study\Constructor.docx")
objWord.Activedocument.Select()
objWord.Selection.CopyAsPicture()
Dim ip As IntPtr
Dim metaFile As System.Drawing.Imaging.Metafile
Dim bRet As Boolean
bRet = ClipboardAPI.OpenClipboard(Me.Handle)
If bRet = True Then
'Verify the clipboard contains data available
'as an enhanced metafile.
bRet = ClipboardAPI.IsClipboardFormatAvailable(CF_ENHMETAFILE) <> 0
End If
If bRet = True Then
'Store the clipboard's contents in the IntPtr.
ip = ClipboardAPI.GetClipboardData(CF_ENHMETAFILE)
End If
'Verify the IntPrt contains data before proceeding. Passing
'an empty IntPtr to System.Drawing.Imaging.Metafile results
'in an exception.
If Not IntPtr.Zero.Equals(ip) Then
metaFile = New System.Drawing.Imaging.Metafile(ip, True)
ClipboardAPI.CloseClipboard()
Dim image As System.Drawing.Image = metaFile
'Me.PictureBox1.Image = metaFile
Dim objImageWriter As Image = New Bitmap(image.Width, image.Height)
Dim objGraphics As Graphics = Graphics.FromImage(objImageWriter)
objGraphics.Clear(Color.White)
'objGraphics.SmoothingMode = Drawing2D.SmoothingMode.AntiAlias
objGraphics.DrawImage(image, 0, 0, image.Width, image.Height)
image.Dispose()
objGraphics.Dispose()
Dim ep As Imaging.EncoderParameters = New Imaging.EncoderParameters
ep.Param(0) = New System.Drawing.Imaging.EncoderParameter(System.Drawing.Imaging.Encoder.Quality, 100)
Dim codecs() As Imaging.ImageCodecInfo = Imaging.ImageCodecInfo.GetImageEncoders()
Dim iciInfo As Imaging.ImageCodecInfo
Dim item As Imaging.ImageCodecInfo
For Each item In codecs
If (item.MimeType = "image/jpeg") Then iciInfo = item
Next
objImageWriter.Save("F:\Study\test1.jpg", iciInfo, ep)
objImageWriter.Dispose()
End If
Public Class ClipboardAPI
<Runtime.InteropServices.DllImport("user32.dll", EntryPoint:="OpenClipboard", SetLastError:=True, ExactSpelling:=True, CallingConvention:=System.Runtime.InteropServices.CallingConvention.StdCall)> _
Public Shared Function OpenClipboard(ByVal hWnd As IntPtr) As Boolean
End Function
<Runtime.InteropServices.DllImport("user32.dll", EntryPoint:="EmptyClipboard", SetLastError:=True, ExactSpelling:=True, CallingConvention:=System.Runtime.InteropServices.CallingConvention.StdCall)> _
Public Shared Function EmptyClipboard() As Boolean
End Function
<Runtime.InteropServices.DllImport("user32.dll", EntryPoint:="SetClipboardData", SetLastError:=True, ExactSpelling:=True, CallingConvention:=System.Runtime.InteropServices.CallingConvention.StdCall)> _
Public Shared Function SetClipboardData(ByVal uFormat As Integer, ByVal ByValhWnd As IntPtr) As IntPtr
End Function
<Runtime.InteropServices.DllImport("user32.dll", EntryPoint:="CloseClipboard", SetLastError:=True, ExactSpelling:=True, CallingConvention:=System.Runtime.InteropServices.CallingConvention.StdCall)> _
Public Shared Function CloseClipboard() As Boolean
End Function
<Runtime.InteropServices.DllImport("user32.dll", EntryPoint:="GetClipboardData", SetLastError:=True, ExactSpelling:=True, CallingConvention:=System.Runtime.InteropServices.CallingConvention.StdCall)> _
Public Shared Function GetClipboardData(ByVal uFormat As Integer) As IntPtr
End Function
<Runtime.InteropServices.DllImport("user32.dll", EntryPoint:="IsClipboardFormatAvailable", SetLastError:=True, ExactSpelling:=True, CallingConvention:=System.Runtime.InteropServices.CallingConvention.StdCall)> _
Public Shared Function IsClipboardFormatAvailable(ByVal uFormat As Integer) As Short
End Function
End Class
The problem is that the line "objWord.Activedocument.Select()" references the entire document rather than the document's individual pages. I've added a bit to your code to snap an image of each page's contents:
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
Dim objWord As New Microsoft.Office.Interop.Word.Application
Dim objDoc As Microsoft.Office.Interop.Word.Document
Const CF_ENHMETAFILE As Integer = 14
objDoc = objWord.Documents.Open("F:\Study\Constructor.docx")
objDoc.Repaginate()
For i As Integer = 1 To objDoc.ActiveWindow.Panes(1).Pages.Count
If i = 1 Then
With objWord.ActiveDocument
.GoTo(WdGoToItem.wdGoToPage, WdGoToDirection.wdGoToAbsolute, 1)
.Bookmarks("\Page").Range.Select()
End With
Else
With objWord.Selection
.GoTo(What:=WdGoToItem.wdGoToPage, Which:=WdGoToDirection.wdGoToNext)
.Bookmarks("\Page").Range.Select()
End With
End If
objWord.Selection.CopyAsPicture()
Dim ip As IntPtr
Dim metaFile As System.Drawing.Imaging.Metafile
Dim bRet As Boolean
bRet = ClipboardAPI.OpenClipboard(Me.Handle)
If bRet = True Then
'Verify the clipboard contains data available
'as an enhanced metafile.
bRet = ClipboardAPI.IsClipboardFormatAvailable(CF_ENHMETAFILE) <> 0
End If
If bRet = True Then
'Store the clipboard's contents in the IntPtr.
ip = ClipboardAPI.GetClipboardData(CF_ENHMETAFILE)
End If
'Verify the IntPrt contains data before proceeding. Passing
'an empty IntPtr to System.Drawing.Imaging.Metafile results
'in an exception.
If Not IntPtr.Zero.Equals(ip) Then
metaFile = New System.Drawing.Imaging.Metafile(ip, True)
ClipboardAPI.CloseClipboard()
Dim image As System.Drawing.Image = metaFile
'Me.PictureBox1.Image = metaFile
Dim objImageWriter As Image = New Bitmap(image.Width, image.Height)
Dim objGraphics As Graphics = Graphics.FromImage(objImageWriter)
objGraphics.Clear(Color.White)
'objGraphics.SmoothingMode = Drawing2D.SmoothingMode.AntiAlias
objGraphics.DrawImage(image, 0, 0, image.Width, image.Height)
image.Dispose()
objGraphics.Dispose()
Dim ep As Imaging.EncoderParameters = New Imaging.EncoderParameters
ep.Param(0) = New System.Drawing.Imaging.EncoderParameter(System.Drawing.Imaging.Encoder.Quality, 100)
Dim codecs() As Imaging.ImageCodecInfo = Imaging.ImageCodecInfo.GetImageEncoders()
Dim iciInfo As Imaging.ImageCodecInfo
Dim item As Imaging.ImageCodecInfo
For Each item In codecs
If (item.MimeType = "image/jpeg") Then iciInfo = item
Next
objImageWriter.Save("F:\Study\test" & i.ToString & ".jpg", iciInfo, ep)
objImageWriter.Dispose()
End If
Next
End Sub
Summary of the additional code changes:
I added "objDoc.Repaginate()" to get accurate page references. Word does not really make use of pages ordinarily, it is constantly querying the system's print driver to decide where it needs to break text up into pages. This ensures that we have an accurate page count in accordance with the current machine.
I enclosed your image logic in this for-loop: "For i As Integer = 1 To objDoc.ActiveWindow.Panes(1).Pages.Count". The if-else directly following that line will select the first page in the first iteration, and then any subsequent additional pages thereafter. Everything else that follows is unchanged except for the save-filename.
Lastly, I just concatenated the page number into the image's save-path for obvious reasons...
I tested this on my own computer and it worked as intended, I hope this helps!
...Just an off-topic sidenote, I don't know if the code disposing of the Word handles just wasn't included in your question or if it's actually missing, but you may want to make sure you add that; Interop class loves to leave running office processes in the background even after the program has closed if they aren't disposed of properly, this example was leaving them open on my computer.
Alright so, don't ask why, PLEASE, but i really need this.
So, i'll display a MessageBox to the user for 2 seconds then i need to close it automatically, without the user input.
Messagebox.Show("RandomStringHere")
System.Threading.Thread.Sleep("2000")
And here i got stuck. Is there any way possible i can do this? And please don't ask why, but it is indeed necessary.
I couldn't find any help on the internet with this problem i have so i guess you guys can help me.
Just create your own form. You can do this in the designer or using code as in the example below. Set a timer and close the form in two seconds:
Private _msgForm As Form
Private _tmr As Windows.Forms.Timer
Private Sub Button9_Click(sender As Object, e As EventArgs) Handles Button9.Click
_msgForm = New Form
With _msgForm
.Height = 200
.Width = 300
.StartPosition = FormStartPosition.CenterScreen
.Text = "Message"
End With
Dim btn As New Button
With btn
.Text = "OK"
.Top = _msgForm.Height - 75
.Left = _msgForm.Width - 100
.Anchor = AnchorStyles.Right Or AnchorStyles.Bottom
End With
_msgForm.Controls.Add(btn)
Dim lbl As New Label
With lbl
.Text = "This is the text of the message box"
.Left = 0
.Top = 0
.Width = _msgForm.ClientSize.Width
.Height = _msgForm.ClientSize.Height - 120
.Anchor = AnchorStyles.Bottom Or AnchorStyles.Left Or AnchorStyles.Right Or AnchorStyles.Top
End With
_msgForm.Controls.Add(lbl)
_tmr = New Windows.Forms.Timer
With _tmr
.Interval = 2000
.Enabled = True
End With
AddHandler _tmr.Tick, AddressOf TimerTick
AddHandler btn.Click, AddressOf ButtonClick
_msgForm.ShowDialog()
End Sub
Private Sub TimerTick(sender As Object, e As EventArgs)
_msgForm.Close()
End Sub
Private Sub ButtonClick(sender As Object, e As EventArgs)
CType(sender, Button).FindForm.Close()
End Sub
Example usage:
Using New CenteredMessageBox(Owner:=Me,
TextFont:=Me.Font,
TimeOut:=2500)
MessageBox.Show("Test Text",
"Test Title",
MessageBoxButtons.OK,
MessageBoxIcon.Information)
End Using
The Custom MessageBox:
' [ Centered MessageBox ]
' By Elektro
'
' The author of the original idea is Hans Passant:
' http://stackoverflow.com/questions/2576156/winforms-how-can-i-make-messagebox-appear-centered-on-mainform
'
' Examples :
'
'Using New CenteredMessageBox(Owner:=Me,
' TextFont:=New Font("Lucida Console", Font.SizeInPoints, FontStyle.Bold),
' TimeOut:=2500)
'
' MessageBox.Show("Test Text", "Test Title", MessageBoxButtons.OK, MessageBoxIcon.Information)
'
'End Using
#Region " Centered MessageBox Class"
#Region " Imports "
Imports System.Drawing
Imports System.Runtime.InteropServices
Imports System.Text
Imports System.Windows.Forms
#End Region
Class CenteredMessageBox : Implements IDisposable
#Region " Variables, Objects, Properties "
Private mTries As Integer = 0
Private mOwner As Form
Private mFont As Font
Private mTimeOut As Integer
Private WithEvents TimeoutTimer As Timer
Private ReadOnly Property MessageBoxWindowHandle As IntPtr
Get
Return _MessageBoxWindowHandle
End Get
End Property
Dim _MessageBoxWindowHandle As IntPtr = IntPtr.Zero
#End Region
#Region " P/Invoke "
Friend Class NativeMethods
Friend Const WM_SETFONT As Integer = &H30
Friend Const WM_GETFONT As Integer = &H31
Friend Delegate Function EnumThreadWndProc(hWnd As IntPtr, lp As IntPtr) As Boolean
Friend Declare Function SetWindowPos Lib "user32" (ByVal hwnd As IntPtr, ByVal hWndInsertAfter As IntPtr, ByVal x As Integer, ByVal y As Integer, ByVal cx As Integer, ByVal cy As Integer, ByVal wFlags As UInt32) As Boolean
<DllImport("user32.dll")>
Friend Shared Function EnumThreadWindows(tid As Integer, callback As NativeMethods.EnumThreadWndProc, lp As IntPtr) As Boolean
End Function
<DllImport("kernel32.dll")>
Friend Shared Function GetCurrentThreadId() As Integer
End Function
<DllImport("user32.dll", CharSet:=CharSet.Unicode)>
Friend Shared Function GetClassName(hWnd As IntPtr, buffer As StringBuilder, buflen As Integer) As Integer
End Function
<DllImport("user32.dll")>
Friend Shared Function GetDlgItem(hWnd As IntPtr, item As Integer) As IntPtr
End Function
<DllImport("user32.dll")>
Friend Shared Function SendMessage(hWnd As IntPtr, msg As Integer, wp As IntPtr, lp As IntPtr) As IntPtr
End Function
<DllImport("user32.dll")>
Friend Shared Function GetWindowRect(hWnd As IntPtr, ByRef rc As RECT) As Boolean
End Function
<DllImport("user32.dll")>
Friend Shared Function MoveWindow(hWnd As IntPtr, x As Integer, y As Integer, w As Integer, h As Integer, repaint As Boolean) As Boolean
End Function
''' <summary>
''' <para>The DestroyWindow function destroys the specified window. The function sends WM_DESTROY and WM_NCDESTROY messages to the window to deactivate it and remove the keyboard focus from it. The function also destroys the window's menu, flushes the thread message queue, destroys timers, removes clipboard ownership, and breaks the clipboard viewer chain (if the window is at the top of the viewer chain).</para>
''' <para>If the specified window is a parent or owner window, DestroyWindow automatically destroys the associated child or owned windows when it destroys the parent or owner window. The function first destroys child or owned windows, and then it destroys the parent or owner window.</para>
''' <para>DestroyWindow also destroys modeless dialog boxes created by the CreateDialog function.</para>
''' </summary>
''' <param name="hwnd">Handle to the window to be destroyed.</param>
''' <returns>If the function succeeds, the return value is nonzero. If the function fails, the return value is zero. To get extended error information, call GetLastError.</returns>
<DllImport("user32.dll", CharSet:=CharSet.Unicode, SetLastError:=True)>
Friend Shared Function DestroyWindow(hwnd As IntPtr) As <MarshalAs(UnmanagedType.Bool)> Boolean
End Function
End Class
Structure RECT
Public Left As Integer
Public Top As Integer
Public Right As Integer
Public Bottom As Integer
End Structure
#End Region
#Region " Constructors "
''' <summary>
''' Initializes a new instance of the <see cref="CenteredMessageBox"/> class.
''' </summary>
''' <param name="Owner">Indicates the form that owns this messagebox.</param>
''' <param name="TextFont">Indicates the text-font used to display the text label.</param>
''' <param name="TimeOut">
''' Indicates the timeout, in ms, to auto-close this <see cref="CenteredMessageBox"/>
''' Default is '0' which means Infinite.
''' </param>
Public Sub New(ByVal Owner As Form,
Optional TextFont As Font = Nothing,
Optional TimeOut As Integer = 0I)
mOwner = Owner
mFont = TextFont
mTimeOut = TimeOut
Owner.BeginInvoke(New MethodInvoker(AddressOf findDialog))
End Sub
#End Region
#Region " Private MEthods "
Private Sub findDialog()
' Enumerate windows to find the message box
If mTries < 0 Then
Return
End If
Dim callback As New NativeMethods.EnumThreadWndProc(AddressOf checkWindow)
If NativeMethods.EnumThreadWindows(NativeMethods.GetCurrentThreadId(), callback, IntPtr.Zero) Then
If System.Threading.Interlocked.Increment(mTries) < 10 Then
mOwner.BeginInvoke(New MethodInvoker(AddressOf findDialog))
End If
End If
If mTimeOut > 0 Then
TimeoutTimer = New Timer With {.Interval = mTimeOut, .Enabled = True}
TimeoutTimer.Start()
End If
End Sub
Private Function checkWindow(hWnd As IntPtr, lp As IntPtr) As Boolean
' Checks if <hWnd> is a dialog
Dim sb As New StringBuilder(260)
NativeMethods.GetClassName(hWnd, sb, sb.Capacity)
If sb.ToString() <> "#32770" Then Return True
' Get the STATIC control that displays the text
Dim hText As IntPtr = NativeMethods.GetDlgItem(hWnd, &HFFFF)
Me._MessageBoxWindowHandle = hWnd
Dim frmRect As New Rectangle(mOwner.Location, mOwner.Size)
Dim dlgRect As RECT
NativeMethods.GetWindowRect(hWnd, dlgRect)
If hText <> IntPtr.Zero Then
If mFont Is Nothing Then
' Get the current font
mFont = Font.FromHfont(NativeMethods.SendMessage(hText, NativeMethods.WM_GETFONT, IntPtr.Zero, IntPtr.Zero))
ElseIf mFont IsNot Nothing Then
NativeMethods.SetWindowPos(hText, 0, 70, 35, frmRect.Width, mFont.Height, 0)
End If
NativeMethods.SendMessage(hText, NativeMethods.WM_SETFONT, mFont.ToHfont(), New IntPtr(1))
' Resize and positionate the messagebox window:
NativeMethods.MoveWindow(hWnd,
frmRect.Left + (frmRect.Width - dlgRect.Right + dlgRect.Left) \ 2,
frmRect.Top + (frmRect.Height - dlgRect.Bottom + dlgRect.Top) \ 2,
(dlgRect.Right - dlgRect.Left),
(dlgRect.Bottom - dlgRect.Top), True)
End If
' Done
Return False
End Function
#End Region
#Region " Event Handlers "
Private Sub TimeoutTimer_Tick(ByVal sender As Object, ByVal e As EventArgs) Handles TimeoutTimer.Tick
NativeMethods.DestroyWindow(Me._MessageBoxWindowHandle)
Me.Dispose()
End Sub
#End Region
#Region " IDisposable "
Public Sub Dispose() Implements IDisposable.Dispose
mTries = -1
mOwner = Nothing
If mFont IsNot Nothing Then mFont.Dispose()
End Sub
#End Region
End Class
#End Region
Not exactly sure how to word this question, so I will go on to explain the details, and try to ask as best I can form it.
I have a project which consists of the following components
Canvas - Inherits PictureBox control
Layers - A collection of "Layer"
Layer - Can contain a collection of Graphics as Images with information.
Each layer can be moved and the selection box for the layer is constrained to the portion of the layer which contains graphics at maximum bounds.
All of the above is working !!
What is not working, is when I want to save the combined result (including transparency, and alpha's), the Canvas control is empty. I know the images are being drawn in the box as it does not display anything until i do Canvas1.Invalidate() .
The code I have for the classes are as follows :
Canvas
Imports System.Drawing
Imports System.Drawing.Graphics
Public Class Canvas
Inherits PictureBox
Private _MoveStart As Point
Private _Layers As List(Of Layer)
Public Sub New()
Me.DoubleBuffered = True
_Layers = New List(Of Layer)
End Sub
Public ReadOnly Property Layers() As List(Of Layer)
Get
Return _Layers
End Get
End Property
Public Property SelectedLayer As Layer
Get
'Loop through all layers and return the one that is selected
For Each l As Layer In Me.Layers
If l.Selected Then Return l
Next
Return Nothing
End Get
Set(ByVal value As Layer)
'Loop through all layers and set their Selected property to True if it is the assigned layer ("value") or False if it isn't.
For Each l As Layer In Me.Layers
l.Selected = (l Is value)
Next
End Set
End Property
Private Function GetLayerFromPoint(ByVal p As Point) As Layer
' Finds the layer that contains the point p
For Each l As Layer In Me.Layers
If l.Bounds.Contains(p) Then Return l
Next
Return Nothing
End Function
Protected Overrides Sub OnMouseDown(ByVal e As System.Windows.Forms.MouseEventArgs)
MyBase.OnMouseDown(e)
If e.Button = Windows.Forms.MouseButtons.Left Then
' Store the previous selected layer to refresh the image there
Dim oldSelection = Me.SelectedLayer
' Get the new selected layer
Me.SelectedLayer = Me.GetLayerFromPoint(e.Location)
'Update the picturebox
If oldSelection IsNot Nothing Then Me.InvalidateLayer(oldSelection)
Me.InvalidateLayer(Me.SelectedLayer)
Me.Update()
_MoveStart = e.Location
End If
End Sub
Protected Overrides Sub OnMouseMove(ByVal e As System.Windows.Forms.MouseEventArgs)
MyBase.OnMouseMove(e)
If Control.MouseButtons = Windows.Forms.MouseButtons.Left Then
If Me.SelectedLayer IsNot Nothing Then
'Store the old bounds for refreshing
Dim oldBounds As Rectangle = Me.SelectedLayer.Bounds
'Move the selected layer
Me.SelectedLayer.Move(e.Location.X - _MoveStart.X, e.Location.Y - _MoveStart.Y)
_MoveStart = e.Location
'Update the picturebox
Me.InvalidateRectangle(oldBounds)
Me.InvalidateLayer(Me.SelectedLayer)
Me.Update()
End If
End If
End Sub
Private Sub InvalidateLayer(ByVal l As Layer)
If l IsNot Nothing Then
Me.InvalidateRectangle(l.Bounds)
End If
End Sub
Private Sub InvalidateRectangle(ByVal r As Rectangle)
'Inflate by 1 pixel otherwise the border isnt visible
r.Inflate(1, 1)
Me.Invalidate(r)
End Sub
Protected Overrides Sub OnPaint(ByVal pe As System.Windows.Forms.PaintEventArgs)
MyBase.OnPaint(pe)
For Each l As Layer In Me.Layers
l.Draw(pe.Graphics)
Next
End Sub
End Class
Layer
Imports System.Drawing
Imports System.Drawing.Graphics
Public Class Layer
Private _Graphics As List(Of Graphic)
Private _Name As String
Private _Selected As Boolean
Public Sub New(ByVal name As String)
Me.Name = name
Me.Selected = False
Me.Graphics = New List(Of Graphic)
End Sub
Public Property Name() As String
Get
Return _Name
End Get
Set(ByVal value As String)
_Name = value
End Set
End Property
Public Property Selected() As Boolean
Get
Return _Selected
End Get
Set(ByVal value As Boolean)
_Selected = value
End Set
End Property
Public ReadOnly Property Bounds As Rectangle
Get
'Combine the bounds of all items
If Me.Graphics.Count > 0 Then
Dim b = Me.Graphics(0).Bounds
For i As Integer = 1 To Me.Graphics.Count - 1
b = Rectangle.Union(b, Me.Graphics(i).Bounds)
Next
Return b
End If
Return Rectangle.Empty
End Get
End Property
Public Property Graphics() As List(Of Graphic)
Get
Return _Graphics
End Get
Set(ByVal value As List(Of Graphic))
_Graphics = value
End Set
End Property
Public Sub Move(ByVal dx As Integer, ByVal dy As Integer)
'Simply move each item
For Each item As Graphic In Me.Graphics
item.Move(dx, dy)
Next
End Sub
Public Sub Draw(ByVal g As System.Drawing.Graphics)
'Draw each item
For Each item As Graphic In Me.Graphics
item.Draw(g)
Next
'Draw a selection border if selected
If Me.Selected Then
g.DrawRectangle(Pens.Red, Me.Bounds)
End If
End Sub
End Class
Graphic
Public Class Graphic
Private _Image As Image
Private _Location As Point
Private _Size As Size
Public Sub New(ByVal img As Image)
Me.Bounds = Rectangle.Empty
Me.Image = img
End Sub
Public Sub New(ByVal img As Image, ByVal location As Point)
Me.New(img)
Me.Location = location
Me.Size = img.Size
End Sub
Public Sub New(ByVal img As Image, ByVal location As Point, ByVal size As Size)
Me.New(img)
Me.Location = location
Me.Size = size
End Sub
Public Property Location() As Point
Get
Return _Location
End Get
Set(ByVal value As Point)
_Location = value
End Set
End Property
Public Property Size() As Size
Get
Return _Size
End Get
Set(ByVal value As Size)
_Size = value
End Set
End Property
Public Property Bounds() As Rectangle
Get
Return New Rectangle(Me.Location, Me.Size)
End Get
Set(ByVal value As Rectangle)
Me.Location = value.Location
Me.Size = value.Size
End Set
End Property
Public Property Image() As Image
Get
Return _Image
End Get
Set(ByVal value As Image)
_Image = value
End Set
End Property
Public Sub Move(ByVal dx As Integer, ByVal dy As Integer)
' We need to store a copy of the Location, change that, and save it back,
' because a Point is a structure and thus a value-type!!
Dim l = Me.Location
l.Offset(dx, dy)
Me.Location = l
End Sub
Public Sub Draw(ByVal g As Graphics)
If Me.Image IsNot Nothing Then
g.DrawImage(Me.Image, Me.Bounds)
End If
End Sub
End Class
Example Usage
Create a new WinForms project (Form1)
Add a Canvas object to the form (will be named Canvas1)
Add a Button object to the form (will be named Button1)
Paste the following code into the Form1 source view
Form1
Public Class Form1
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Me.DoubleBuffered = True
Me.Show()
Dim l As Layer
l = New Layer("Layer 1")
l.Graphics.Add(New Graphic(My.Resources.ActualSizeHS, New Point(10, 10)))
Canvas1.Layers.Add(l)
l = New Layer("Layer 2")
l.Graphics.Add(New Graphic(My.Resources.AlignObjectsRightHS, New Point(320, 240)))
l.Graphics.Add(New Graphic(My.Resources.AlignToGridHS, New Point(290, 140)))
l.Graphics.Add(New Graphic(My.Resources.AlignObjectsBottomHS, New Point(320, 130)))
Canvas1.Layers.Add(l)
l = New Layer("Layer 3")
l.Graphics.Add(New Graphic(My.Resources.AlignObjectsTopHS, New Point(520, 240)))
l.Graphics.Add(New Graphic(My.Resources.AlignTableCellMiddleRightHS, New Point(390, 240)))
l.Graphics.Add(New Graphic(My.Resources.AlignTableCellMiddleCenterHS, New Point(520, 130)))
Canvas1.Layers.Add(l)
Canvas1.Invalidate()
End Sub
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Canvas1.Image.Save("MyRenderedPicture.png", System.Drawing.Imaging.ImageFormat.Png)
End Sub
End Class
In the above example for Form1, replace My.Resources.* with wherever your graphics are located. This parameter is simply a System.Drawing.Image object.
The problem I am having, is when I click Button1 to save the image, the output does not contain any of the graphics that were added to the control. Please note that all of the graphics I am working with are PNG with fully transparent backgrounds, and dragging them around inside the container doesn't have the blocky effect of layering images using pictureboxes. Each image is true transparent. I wish to keep this level of transparency (and alpha blends if any exist), when i save the file -- but first ... i need to be able to save something other than a blank picturebox which clearly contains images.
Thanks in advance.
(image example of save where "shadows" are not rendering their opacity levels properly)
Now, if I do the following :
Dim x As Integer = 0
Using bmp As Bitmap = New Bitmap(Me.Width, Me.Height, System.Drawing.Imaging.PixelFormat.Format32bppArgb)
'Me.DrawToBitmap(bmp, New Rectangle(0, 0, bmp.Width, bmp.Height))
For Each l As Layer In Me.Layers
For Each g As Graphic In l.Graphics
g.Image.Save("layer" & x & ".png")
x = x + 1
Next
Next
bmp.MakeTransparent(Me.BackColor)
bmp.Save(FileName, Format)
bmp.Dispose()
End Using
Each layer is saved out properly -- individually. So the Graphics control is working as it should, it is when I combine them (and need to keep the position, and transparency), I think this is the routine I am looking for ---
How to merge System.Drawing.Graphics objects I am going to try and create a new Graphics object and try to "draw" onto it using the other graphics objects and their positions. Every example so far using clipping rectangles which will not do as that takes a pic of the stuff behind the Graphic which then needs to be made clear, etc etc.
You do not assign an image to the picbox/canvas so Image is Nothing. After all, you are just using it as a canvas not an image holder. Since the helpers already know where they are, you just need to create a bitmap and draw the images/layers to it from the bottom up:
Public Function GetBitmap(format As System.Drawing.Imaging.ImageFormat) As Bitmap
' ToDo: add graphics settings
Dim bmp As New Bitmap(Me.Width, Me.Height)
Using g As Graphics = Graphics.FromImage(bmp)
' ToDo: draw Canvas BG / COlor to bmp to start
' for BMP, JPG / non Transparents
For n As Integer = 0 To Layers.Count - 1
Layers(n).Draw(g)
Next
End Using
Return bmp
End Function
Then on the form:
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
' to do - add a literal SAve
Using bmp As Bitmap = Canvas1.GetBitmap(Imaging.ImageFormat.Png)
bmp.Save("C:\Temp\myImage.png", System.Drawing.Imaging.ImageFormat.Png)
End Using
End Sub
Are the PNGs ever being added to the Image of the Canvas? This isn't completely the same, so I apologize, but I recently made just a quick test app where I was trying to stack PNGs so thought I'd share what I did.
This loops through lstImages (which is actually a list of strings), loads the image into bmpTemp, and draws the image onto bmpBmp. That image is then used in the Image property of a PictureBox and added to the Controls collection of the form.
I just added another button to test saving and it worked fine with what I have below (after adding a name to the PictureBox).
Private Sub StackImages()
Dim bmpBmp As New Bitmap(picStack.Width, picStack.Height)
Dim graGraphic As Graphics = Graphics.FromImage(bmpBmp)
For Each i As String In Me.lstImages
Dim bmpTemp As New Bitmap(i)
graGraphic.DrawImage(bmpTemp, 0, 0)
Next
Dim picTemp As New PictureBox
picTemp.Top = picStack.Top
picTemp.Left = picStack.Left
picTemp.Width = picStack.Width
picTemp.Height = picStack.Height
picTemp.Image = bmpBmp
picTemp.Name = "NewPictureBox"
Me.Controls.Add(picTemp)
picTemp.BringToFront()
End Sub
Private Sub btnSave_Click(sender As Object, e As EventArgs) Handles btnSave.Click
Dim picNew As PictureBox = CType(Me.Controls.Item("NewPictureBox"), PictureBox)
picNew.Image.Save("c:\temp\picTest.png")
End Sub
I am delveloping a small application in VB.NET, where I need to load an image from the phone into a picture box. I am not able to add the picture into picturebox, though. It throws an OutOfMemoryException. I am wondering if there is any way to reduce the size of the picture by changing it to thumbnail.
Dim srcmap As New Bitmap(OpenFileDialog1.FileName)
Dim destbit As New Bitmap(220, 220)
Dim srcRec As New Rectangle(0, 0, srcmap.Width, srcmap.Height)
Dim destRec As New Rectangle(0, 0, 220, 220)
Dim g As Graphics
g = Graphics.FromImage(destbit)
g.DrawImage(srcmap, destRec,srcRec, GraphicsUnit.Pixel)
picturebox.Image = destbit
Here is my OpenNetCF C# snippet for that issue:
...
//imagefactory
using OpenNETCF.Drawing;
using OpenNETCF.Drawing.Imaging;
...
OpenNETCF.Drawing.Imaging.StreamOnFile m_stream;
Size m_size;
///
/// this will handle also large bitmaps and show a thumbnailed version on a picturebox
/// see http://blog.opennetcf.com/ctacke/2010/10/13/LoadingPartsOfLargeImagesInTheCompactFramework.aspx
///
/// the name of the file to load
private void showImage(string sFileName)
{
var stream = File.Open(sFileName, FileMode.Open);
m_stream = new StreamOnFile(stream);
m_size = ImageHelper.GetRawImageSize(m_stream);
System.Diagnostics.Debug.WriteLine("showImage loading " + sFileName + ", width/height = " + m_size.Width.ToString() + "/"+ m_size.Height.ToString());
//CameraPreview.Image = ImageHelper.CreateThumbnail(m_stream, CameraPreview.Width, CameraPreview.Height);
CameraSnapshot.Image = ImageHelper.CreateThumbnail(m_stream, CameraPreview.Width, CameraPreview.Height);
showSnapshot(true); //show still image
m_stream.Dispose();
stream.Close();
}
with imagehelper.cs:http://code.google.com/p/intermeccontrols/source/browse/DPAG7/Hasci.TestApp.And_Controls/IntermecControls/Hasci.TestApp.IntermecCamera3/ImageHelper.cs
Here is the VB code of the above:
Imports System
Imports System.Drawing
Imports System.Drawing.Imaging
Imports OpenNETCF.Drawing
Imports OpenNETCF.Drawing.Imaging
Imports System.Runtime.InteropServices
Imports System.Resources
Imports System.Reflection
Public Class ImageHelper
Private Shared m_factory As ImagingFactory
Private Shared Function GetFactory() As ImagingFactory
If (m_factory Is Nothing) Then
m_factory = New ImagingFactory
End If
Return m_factory
End Function
Public Shared Function CreateClip(ByVal sof As StreamOnFile, ByVal x As Integer, ByVal y As Integer, ByVal width As Integer, ByVal height As Integer) As Bitmap
Dim original As IBitmapImage = Nothing
Dim image As IImage = Nothing
Dim info As ImageInfo
GetFactory.CreateImageFromStream(sof, image)
Try
image.GetImageInfo(info)
GetFactory.CreateBitmapFromImage(image, info.Width, info.Height, info.PixelFormat, InterpolationHint.InterpolationHintDefault, original)
Try
Dim ops As IBasicBitmapOps = CType(original, IBasicBitmapOps)
Dim clip As IBitmapImage = Nothing
Try
Dim rect As RECT = New RECT(x, y, (x + width), (y + height))
ops.Clone(rect, clip, True)
Return ImageUtils.IBitmapImageToBitmap(clip)
Finally
Marshal.ReleaseComObject(clip)
End Try
Finally
Marshal.ReleaseComObject(original)
End Try
Finally
Marshal.ReleaseComObject(image)
End Try
End Function
Public Shared Function getScaledBitmap(ByVal sof As StreamOnFile, ByVal scalePercent As Integer) As Bitmap
Dim thumbnail As IBitmapImage = Nothing
Dim image As IImage = Nothing
Dim info As ImageInfo
Dim fScale As Decimal = (scalePercent / 100)
' m()
' do not remove the m specifier!
GetFactory.CreateImageFromStream(sof, image)
Try
image.GetImageInfo(info)
Dim newWidth As UInteger = CType((info.Width * fScale), UInteger)
Dim newHeight As UInteger = CType((info.Height * fScale), UInteger)
GetFactory.CreateBitmapFromImage(image, newWidth, newHeight, info.PixelFormat, InterpolationHint.InterpolationHintDefault, thumbnail)
Try
Return ImageUtils.IBitmapImageToBitmap(thumbnail)
Catch ex As Exception
Dim stream As System.IO.Stream = System.Reflection.Assembly.GetExecutingAssembly.GetManifestResourceStream("CameraCx7x.oom.png")
Return New Bitmap(stream)
' (400, 300);
Finally
Marshal.ReleaseComObject(thumbnail)
End Try
Finally
Marshal.ReleaseComObject(image)
End Try
End Function
Public Shared Function CreateThumbnail(ByVal sof As StreamOnFile, ByVal width As Integer, ByVal height As Integer) As Bitmap
Dim thumbnail As IBitmapImage = Nothing
Dim image As IImage = Nothing
Dim info As ImageInfo
GetFactory.CreateImageFromStream(sof, image)
Try
image.GetImageInfo(info)
GetFactory.CreateBitmapFromImage(image, CType(width, UInteger), CType(height, UInteger), info.PixelFormat, InterpolationHint.InterpolationHintDefault, thumbnail)
Try
Return ImageUtils.IBitmapImageToBitmap(thumbnail)
Finally
Marshal.ReleaseComObject(thumbnail)
End Try
Finally
Marshal.ReleaseComObject(image)
End Try
End Function
Public Shared Function saveScaledBitmap(ByVal sof As StreamOnFile, ByVal width As Integer, ByVal height As Integer, ByVal sNewFile As String) As Boolean
Dim bRet As Boolean = True
Try
Dim bmp As Bitmap = CreateThumbnail(sof, width, height)
bmp.Save(sNewFile, System.Drawing.Imaging.ImageFormat.Jpeg)
Catch ex As Exception
System.Diagnostics.Debug.WriteLine(("Exception in saveScaledBitmap(): " + ex.Message))
bRet = False
End Try
Return bRet
End Function
Public Shared Function GetRawImageSize(ByVal sof As StreamOnFile) As Size
Dim image As IImage = Nothing
Dim info As ImageInfo
GetFactory.CreateImageFromStream(sof, image)
Try
image.GetImageInfo(info)
Return New Size(CType(info.Width, Integer), CType(info.Height, Integer))
Finally
Marshal.ReleaseComObject(image)
End Try
End Function
End Class
and this is how to call it from a form with a lable, a button and a picturebox
Imports OpenNETCF.Drawing
Imports OpenNETCF.Drawing.Imaging
Imports System.IO
Public Class Form1
Dim _Bitmap As Bitmap
Private Sub showImage(ByVal filePath As String)
'do not load the full image!
If File.Exists(filePath) Then
label1.Text = filePath
If (Not (_Bitmap) Is Nothing) Then
_Bitmap.Dispose()
End If
' a 1944x2593 image is about 15MByte !
'create a scaled down image file
Dim _filestream As FileStream = File.Open(filePath, FileMode.Open)
Dim _stream As StreamOnFile = New StreamOnFile(_filestream)
Dim fi As System.IO.FileInfo = New FileInfo(filePath)
Dim sizeBmp As Size = ImageHelper.GetRawImageSize(_stream)
'allow 640 hight
Dim ratio As Integer = (sizeBmp.Height / 640)
Dim scale As Integer = (ratio * 10)
If (ratio > 1) Then
_Bitmap = ImageHelper.getScaledBitmap(_stream, scale)
End If
Me.pictureBox1.Image = CType(_Bitmap, Image)
_filestream.Close()
End If
End Sub
Private Sub btnLoad_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnLoad.Click
Dim ofd As New OpenFileDialog
ofd.InitialDirectory = "\My Documents"
ofd.Filter = "Jpg image|*.jpg|Bmp image|*.bmp|Gif image|*.gif|All files|*.*"
ofd.FilterIndex = 0
Dim sFile As String
If (ofd.ShowDialog = Windows.Forms.DialogResult.OK) Then
sFile = ofd.FileName
ofd.Dispose()
showImage(sFile)
End If
End Sub
End Class
I'm using the below function to generate an MD5\SH1 hash for SQL backup files.
This works well, has progress report etc but is slow if using large files.
Could I generate the MD5 at the same time as SH1 rather than having to process the file twice, doubling the time taken? What about converting an MD5 result to SHA1?
Imports System
Imports System.IO
Imports System.Security.Cryptography
Imports System.Text
Public Class ASyncFileHashAlgorithm
Protected hashAlgorithm As HashAlgorithm
Protected m_hash As Byte()
Protected cancel As Boolean = False
Protected m_bufferSize As Integer = 4096
Public Delegate Sub FileHashingProgressHandler(ByVal sender As Object, _
ByVal e As FileHashingProgressArgs)
Public Event FileHashingProgress As FileHashingProgressHandler
Public Sub New(ByVal hashAlgorithm As HashAlgorithm)
Me.hashAlgorithm = hashAlgorithm
End Sub
Public Function ComputeHash(ByVal stream As Stream) As Byte()
cancel = False
m_hash = Nothing
Dim _bufferSize As Integer = m_bufferSize
' this makes it impossible to change the buffer size while computing
Dim readAheadBuffer As Byte(), buffer As Byte()
Dim readAheadBytesRead As Integer, bytesRead As Integer
Dim size As Long, totalBytesRead As Long = 0
size = stream.Length
readAheadBuffer = New Byte(_bufferSize - 1) {}
readAheadBytesRead = stream.Read(readAheadBuffer, 0, _
readAheadBuffer.Length)
totalBytesRead += readAheadBytesRead
Do
bytesRead = readAheadBytesRead
buffer = readAheadBuffer
readAheadBuffer = New Byte(_bufferSize - 1) {}
readAheadBytesRead = stream.Read(readAheadBuffer, 0, _
readAheadBuffer.Length)
totalBytesRead += readAheadBytesRead
If readAheadBytesRead = 0 Then
hashAlgorithm.TransformFinalBlock(buffer, 0, bytesRead)
Else
hashAlgorithm.TransformBlock(buffer, 0, bytesRead, buffer, 0)
End If
RaiseEvent FileHashingProgress(Me, New _
FileHashingProgressArgs(totalBytesRead, size))
Loop While readAheadBytesRead <> 0 AndAlso Not cancel
If cancel Then
Return InlineAssignHelper(m_hash, Nothing)
End If
Return InlineAssignHelper(m_hash, hashAlgorithm.Hash)
End Function
Public Property BufferSize() As Integer
Get
Return m_bufferSize
End Get
Set(ByVal value As Integer)
m_bufferSize = value
End Set
End Property
Public ReadOnly Property Hash() As Byte()
Get
Return m_hash
End Get
End Property
'Public Sub Cancel()
' cancel = True
'End Sub
Public Overrides Function ToString() As String
Dim hex As String = ""
For Each b As Byte In Hash
hex += b.ToString("x2")
Next
Return hex
End Function
Private Shared Function InlineAssignHelper(Of T)(ByRef target As T, _
ByVal value As T) As T
target = value
Return value
End Function
End Class
Public Class FileHashingProgressArgs
Inherits EventArgs
Public Property TotalBytesRead() As Long
Get
Return m_TotalBytesRead
End Get
Set(ByVal value As Long)
m_TotalBytesRead = Value
End Set
End Property
Private m_TotalBytesRead As Long
Public Property Size() As Long
Get
Return m_Size
End Get
Set(ByVal value As Long)
m_Size = Value
End Set
End Property
Private m_Size As Long
Public Sub New(ByVal totalBytesRead__1 As Long, ByVal size__2 As Long)
TotalBytesRead = totalBytesRead__1
Size = size__2
End Sub
End Class
The following is how I'm generating a hash using the above:
Shared hasher As New ASyncFileHashAlgorithm(SHA1.Create())
Private Function Test(Byval (strFilePathAndName, as String)
Dim stream As IO.Stream = DirectCast(File.Open(strFilePathAndName, _
FileMode.Open), Stream)
AddHandler hasher.FileHashingProgress, _
AddressOf OnFileHashingProgress
Dim t = New Thread(AddressOf hasher.ComputeHash)
t.Start(stream)
While t.IsAlive
Application.DoEvents()
End While
'LblMD5.Text = hasher.ToString???
LblSHA1.Text = hasher.ToString
stream.Dispose()
End Sub
Public Sub OnFileHashingProgress(ByVal sender As Object, _
ByVal e As FileHashingProgressArgs)
SetControlPropertyValue(uxChildForm.ProgressBar, "Position", _
CInt(e.TotalBytesRead / e.Size * 100))
End Sub
You ToString method may create tremendous overhead for large strings since concatenation is expensive (it creates large temporary buffer) and you do it often. Use a StringBuilder (initialised with the correct size) instead.