I'd like to have a way to export the graphical contents of a FlowLayoutPanel to a file (don't mind what format, bmp is probably the easiest). I'd also like it to scroll the contents so that the exported file contains the entire contents of the Panel.
Is there any way to do this? I'm using C# WinForms and Framework 4.
Try looking into xml serialization
You could serialize the panel and save the xml. and load the xml and deserialize it back into the panel
Also check this out.
To save as an image you just do this:
Bitmap image = new Bitmap(flowLayoutPanel1.Width, flowLayoutPanel1.Height);
flowLayoutPanel1.DrawToBitmap(image, new Rectangle(0, 0, flowLayoutPanel1.Width, flowLayoutPanel1.Height));
image.Save("SAVE PATH");
The trick is to temporaly set the flowLayoutPanel as large as to fit all the controls within, even if it gets too large for the visible screen, then do the DrawToBitmap using the flowLayoutPanel.clientRectangle area, not the .Width and .Height.
In my example, Outside_Splitter is docked to the form, with two panels, and fraAction is a groupbox and is the last control on the panel, that is vertically scrolled.
Public Sub Print_Panel()
Dim newHeight As Integer
Dim pos As Point, oheight As Integer, owidth As Integer, xDock As DockStyle
With Outside_Splitter ' This contains the two panel ...
pos.X = .Left ' Store original position and size
pos.Y = .Top
oheight = .Height
owidth = .Width
xDock = .Dock ' get original dock set
newHeight = FraAction.Top + FraAction.Height + 30 ' calculate new height based on position and size of the last control
.Dock = DockStyle.None ' undock it
.Height = newHeight ' set new height
.Refresh()
.SetBounds(pos.X, pos.Y, owidth, newHeight) ' Set position and size, temporarily
.Refresh()
End With
'Create Bitmap based on panel.ClientRectangle
Dim myBmp As New Bitmap(Painel_Detalhe_NC.ClientRectangle.Width, Painel_Detalhe_NC.ClientRectangle.Height)
'Paint the bitmap
Painel_Detalhe_NC.DrawToBitmap(myBmp, Painel_Detalhe_NC.ClientRectangle)
'Create pdf
Dim _pdf As New C1.C1Pdf.C1PdfDocument
_pdf.Clear()
_pdf.Landscape = False
_pdf.PaperKind = PaperKind.A4
Dim rec As New RectangleF ' Set 5% margin around the page
rec = _pdf.PageRectangle
rec.X = 0.05 * rec.Width
rec.Y = 0.05 * rec.Height
rec.Width = 0.9 * _pdf.PageRectangle.Width
rec.Height = 0.9 * _pdf.PageRectangle.Height
_pdf.DrawImage(myBmp, rec) ' paint/resize bitmap to that size on the pdf
'Save it and show it
_pdf.Save(My.Computer.FileSystem.SpecialDirectories.Temp & "\temp.pdf")
Process.Start(My.Computer.FileSystem.SpecialDirectories.Temp & "\temp.pdf")
myBmp.Dispose() ' Clear it
With Outside_Splitter ' put it back to where it was
.Left = pos.X
.Top = pos.Y
.Dock = xDock ' Back to filling the form
.Refresh()
End With
End Sub
Related
In a WPF application the user can double click a line in a DataGrid to open a new page that contain tabs with grids - some of these have GridSplitters like this
Private Function CentreGrid() As Grid
Try
Dim vGrid As New Grid
For i As Integer = 0 To 2
Dim vCol As New ColumnDefinition
If i = 1 Then
vCol.Width = New GridLength(5, GridUnitType.Auto)
End If
vGrid.ColumnDefinitions.Add(vCol)
Next
Dim vLeftGrid As Grid = LeftGrid()
Grid.SetColumn(vLeftGrid, 0)
vGrid.Children.Add(vLeftGrid)
Dim vGridSplitter As New GridSplitter
With vGridSplitter
.VerticalAlignment = Windows.VerticalAlignment.Stretch
.HorizontalAlignment = Windows.HorizontalAlignment.Center
.ResizeBehavior = GridResizeBehavior.PreviousAndNext
.Background = New SolidColorBrush(Colors.Blue)
.Width = 5
.Margin = New Thickness(5)
End With
Grid.SetColumn(vGridSplitter, 1)
vGrid.Children.Add(vGridSplitter)
Dim vRightGrid As Grid = RightGrid()
Grid.SetColumn(vRightGrid, 2)
vGrid.Children.Add(vRightGrid)
Return vGrid
Catch ex As Exception
EmailError(ex)
Return Nothing
End Try
End Function
As everything is generated dynamically from code-behind is there any method to get a handle on the relative grid position that has been changed by the user so that when the page is closed and another instance opened the GridSplitter can be in the same relative position that the user selected beforehand?
Thanks
I put two solid circles (shapes) onto a form, change their colors, and move them around. When I get them into the right position, I want to capture the full-color image of the form and put it into an array as quickly as possible. How can I do this?
Here is how I draw the objects:
I get an OvalShape from VB PowerPacks in VB2010Express and draw it as a circle on the form. After that, I adjust it in code:
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
OccultSize1 = 100
OccultSize2 = 50
OvalShape1.Width = OccultSize1
OvalShape1.Height = OccultSize1
OvalShape2.Width = OccultSize2
OvalShape2.Height = OccultSize2
OvalShape1.Left() = 155 - OvalShape1.Width / 2
OvalShape1.Top() = 153 - OvalShape1.Height / 2
OvalShape2.Left() = 155 - OvalShape2.Width / 2
OvalShape2.Top = 85 'temporary
OvalShape1.FillColor = Color.Yellow
OvalShape1.BorderColor = Color.Yellow
OvalShape2.FillColor = Color.Blue
OvalShape2.BorderColor = OvalShape2.FillColor
Label1.Text = "100"
Label2.Text = "50"
Label3.Text = "xxx"
tbHue.Value = OvalShape2.FillColor.GetHue
RadioButton1.Checked = False
RadioButton2.Checked = True
LineShape1.Visible = False
LineShape2.Visible = True
OvalShape3.Visible = False
End Sub
Here is what I have tried with DrawToBitmap (copied most of it from Stack Overflow): Button1.Click is my addition.
Private Function GetFormImage(ByRef GetBitmap As String) As Bitmap
' Make the bitmap.
Dim wid As Integer = Me.Width
Dim hgt As Integer = Me.Height
Dim bm As New Bitmap(wid, hgt)
' Draw the form onto the bitmap.
Me.DrawToBitmap(bm, New Rectangle(0, 0, wid, hgt))
bm.Save("E:\rhtempsave\bm.bmp")
' Make a smaller bitmap without borders.
wid = 240 'Me.ClientSize.Width
hgt = 320 'Me.ClientSize.Height
Dim bm2 As New Bitmap(wid, hgt)
' Get the offset from the window's corner to its client
' area's corner.
Dim pt As New Point(0, 0)
pt = PointToScreen(pt)
Dim dx As Integer = 37 'pt.X - Me.Left
Dim dy As Integer = 10 'pt.Y - Me.Top
' Copy the part of the original bitmap that we want
' into the bitmap.
Dim gr As Graphics = Graphics.FromImage(bm2)
gr.DrawImage(bm, 0, 0, New Rectangle(dx, dy, wid, hgt), _
GraphicsUnit.Pixel)
bm2.Save("E:\rhtempsave\bm2.bmp")
Return bm2
End Function
This is bm2.
For some reason, the bitmaps appeared on my disk today, but not yesterday. The only thing I did in the meantime was shutdown and restart my computer. Strangely, the bmps are not readable by Photoshop, Photoshop Elements or Win10's photo reader yet they show up as icons in the disk file dialog. Probably a header problem. I think I'm good to go with the next step of putting the bmp into an array for later retrieval. Thanks. I'm very grateful for your prompt response.
I'm trying to resize and save the 3 images as defined in the Page_load event.
Within method ResizeAndSave I have 2 methods I'm trying: FastResize and SlowResize.
When I uncomment the FastResize codeline: IMAGE 1 and 2 are saved and resized correctly. IMAGE 3 however, is saved in dimensions 625x441px and so does not respect the 200x200 box I want it to resize to.
When I instead use the SlowResize codeline: IMAGE 1 and 2 are again saved and resized correctly. IMAGE 3 however, is not saved at all.
No errors are thrown in my code. I will import images from a variety of sources so it's critical my code works on a wide range of image formats. And apparently there's something special about IMAGE 3 and I don't know what it is or how to handle it.
Here's my full code, you should be able to just copy/paste it and test it for yourself:
Imports System.Drawing
Imports System.Drawing.Imaging
Imports System.IO
Imports System.Xml
Imports System.Data.SqlClient
Imports System.Net
Imports System.Windows.Media.Imaging
Imports System.Windows.Media
Partial Class importfeeds
Inherits System.Web.UI.Page
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
'IMAGE 1
ResizeAndSave(200, 200, "https://upload.wikimedia.org/wikipedia/commons/8/82/Dell_Logo.png")
'IMAGE 2
ResizeAndSave(200, 200, "https://upload.wikimedia.org/wikipedia/commons/d/d8/Square-1_solved.jpg")
'IMAGE 3
ResizeAndSave(200, 200, "http://cdn2.emobassets.eu/media/catalog/product/1/1/1116220.jpg")
End Sub
Private Sub ResizeAndSave(ByVal maxWidth As Integer, ByVal maxHeight As Integer, ByVal imageURL As String)
Dim imgRequest As WebRequest = WebRequest.Create(imageURL)
Dim imgResponse As WebResponse = imgRequest.GetResponse()
Dim streamPhoto As Stream = imgResponse.GetResponseStream()
Dim memStream As New MemoryStream
streamPhoto.CopyTo(memStream)
memStream.Position = 0
Dim bfPhoto As BitmapFrame = ReadBitmapFrame(memStream)
Dim newWidth, newHeight As Integer
Dim scaleFactor As Double
If bfPhoto.Width > maxWidth Or bfPhoto.Height > maxHeight Then
If bfPhoto.Width > maxWidth Then
scaleFactor = maxWidth / bfPhoto.Width
newWidth = Math.Round(bfPhoto.Width * scaleFactor, 0)
newHeight = Math.Round(bfPhoto.Height * scaleFactor, 0)
End If
If newHeight > maxHeight Then
scaleFactor = maxHeight / newHeight
newWidth = Math.Round(newWidth * scaleFactor, 0)
newHeight = Math.Round(newHeight * scaleFactor, 0)
End If
End If
Dim bfResize As BitmapFrame = FastResize(bfPhoto, newWidth, newHeight)
'Dim bfResize As BitmapFrame = SlowResize(bfPhoto, newWidth, newHeight, BitmapScalingMode.Linear)
Dim baResize As Byte() = ToByteArray(bfResize)
Dim strThumbnail As String = Guid.NewGuid.ToString() + ".png"
Dim saveToPath As String = Server.MapPath(ConfigurationManager.AppSettings("products_photospath")) + "\49\" + strThumbnail
File.WriteAllBytes(saveToPath, baResize)
End Sub
Private Shared Function FastResize(bfPhoto As BitmapFrame, nWidth As Integer, nHeight As Integer) As BitmapFrame
Dim tbBitmap As New TransformedBitmap(bfPhoto, New ScaleTransform(nWidth / bfPhoto.PixelWidth, nHeight / bfPhoto.PixelHeight, 0, 0))
Return BitmapFrame.Create(tbBitmap)
End Function
'http://weblogs.asp.net/bleroy/resizing-images-from-the-server-using-wpf-wic-instead-of-gdi
Public Shared Function SlowResize(photo As BitmapFrame, width As Integer, height As Integer, scalingMode As BitmapScalingMode) As BitmapFrame
Dim group = New DrawingGroup()
RenderOptions.SetBitmapScalingMode(group, scalingMode)
group.Children.Add(New ImageDrawing(photo, New Windows.Rect(0, 0, width, height)))
Dim targetVisual = New DrawingVisual()
Dim targetContext = targetVisual.RenderOpen()
targetContext.DrawDrawing(group)
Dim target = New RenderTargetBitmap(width, height, 96, 96, PixelFormats.[Default])
targetContext.Close()
target.Render(targetVisual)
Dim targetFrame = BitmapFrame.Create(target)
Return targetFrame
End Function
Private Shared Function ToByteArray(bfResize As BitmapFrame) As Byte()
Using msStream As New MemoryStream()
Dim pbdDecoder As New PngBitmapEncoder()
pbdDecoder.Frames.Add(bfResize)
pbdDecoder.Save(msStream)
Return msStream.ToArray()
End Using
End Function
Private Shared Function ReadBitmapFrame(streamPhoto As Stream) As BitmapFrame
Dim bdDecoder As BitmapDecoder = BitmapDecoder.Create(streamPhoto, BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.None)
Return bdDecoder.Frames(0)
End Function
End Class
UPDATE 1
#Hans Passant: Both your suggestions on filenaming and pixelWidth usage are spot on and helped me run this code successfully on the 3 images in the Page_load event.
I updated my original code.
However, when I run this code as part of my actual application, where I import ~100 images from a feed. The new code fails with an out of memory exception when it tries to process IMAGE 3. This happens for both FastResize and SlowResize methods. Is there something in my code or in the image in question that would cause this increase in memory usage, maybe a leak somewhere or an inefficient resizing method I use?
I have a lot of memory available on my machine, so would be very surprised if that were the problem, although I do see a big increase in the System and compressed memory (to 1.1GB) task in my Windows task manager. And still, this much memory usage would have me believe that there's something wrong in my code.
What can it be?
IMAGE 3 however, is saved in dimensions 625x441px
That is because the image is slightly different from the other ones, its DPI (dots per inch) is 300 instead of 96. Its size in pixels is 3071 x 2172 but you are using the Width and Height properties, the size in inches with a unit of 1/96" which is 982.72 x 695.04 for this image. Fix this by using the PixelWidth and PixelHeight properties instead:
Dim tbBitmap As New TransformedBitmap(bfPhoto,
New ScaleTransform(nWidth / bfPhoto.PixelWidth, nHeight / bfPhoto.PixelHeight, 0, 0))
IMAGE 3 however, is not saved at all
That doesn't add up completely, but you do have a critical bug in this statement:
Dim strThumbnail As String = "success" + Date.Now.Second.ToString + ".png"
This name is not sufficiently unique to ensure that you don't overwrite an existing file. And if the code is "fast" then Date.Now.Second will have the same value and your code overwrites a previous written image file. Note how this bug won't repro when you debug, that makes the code artificially slower and the second will be different.
You'll need a better way to name the file, Guid.NewGuid.ToString() is a very good way for example, guaranteed to be unique. Or use a simple counter that you increment for each image. You do need to focus on cleanup.
I want to create an array of bits and turn them into a bitmap in vb? I know how to create a bitmap like this Dim NBitmap as new bitmap(width,height), Then edit its pixels like this NBitmap.setpixel(x,y,color). This is slow however I thought that creating an array or something then converting it to a bitmap would be faster. But i don't know how... Any help would be nice!
Example:
Dim SM = DateTime.Now.Millisecond
Dim Texture As Bitmap = Image.FromFile("C:\Users\Noah\Desktop\graphics\Grass.jpg")
Dim w = 600
Dim h = 600
Dim Pixels(600, 600) As Color
Dim x = 0
Do Until x = 600
Dim y = 0
Do Until y = 600
Pixels(x, y) = Texture.GetPixel(x, y)
y += 1
Loop
x += 1
Loop
Dim EM = DateTime.Now.Millisecond
Dim FM = EM - SM
If FM < 0 Then
FM += 1000
End If
Dim FPS = 1000 / FM
Dim ETDFrame As New Bitmap(600, 600)
ETDFrame.image = Pixels().convertBMP
I know that this code dosen't work but if gives an example if what i want.My goal is to create a new bitmap and edit all the pixels super fast, fast enough for a 3d game...
It's not clear what you are trying to do. It appears as if you are trying to convert a .jpg to a .bmp. If that is so, you can just call the .Save method of the Image class:
'Load the bitmap
Dim bm As Bitmap = Image.FromFile("C:\Users\Noah\Desktop\graphics\Grass.jpg")
'Save as .bmp
bm.Save("C:\Users\Noah\Desktop\graphics\Grass.bmp", System.Drawing.Imaging.ImageFormat.Bmp)
If you really need to work with the pixels, then using GetPixel and SetPixel will be too slow. You need to use LockBits to work directly with the bitmap data. Something like this:
Imports System.Drawing.Imaging
Imports System.Runtime.InteropServices
Private Sub DoGraphics()
Dim x As Integer
Dim y As Integer
'PixelSize is 3 bytes for a 24bpp Argb image.
'Change this value appropriately
Dim PixelSize As Integer = 3
'Load the bitmap
Dim bm As Bitmap = Image.FromFile("C:\Users\Noah\Desktop\graphics\Grass.jpg")
'lock the entire bitmap for editing
'You can change the rectangle to specify different parts of the image if needed.
Dim bmData As BitmapData = bm.LockBits(New Rectangle(0, 0, bm.Width, bm.Height), System.Drawing.Imaging.ImageLockMode.ReadWrite, bm.PixelFormat)
'Declare empty Color array
Dim pixels(bm.Width - 1, bm.Height - 1) As Color
'loop through the locked area of the bitmap.
For x = 0 To bmData.Width - 1
For y = 0 To bmData.Height - 1
'Get the various color offset locations for each pixel.
'This calculation is for a 24bpp rgb bitmap
Dim blueOfs As Integer = (bmData.Stride * x) + (PixelSize * y)
Dim greenOfs As Integer = blueOfs + 1
Dim redOfs As Integer = greenOfs + 1
'Read the value for each color component for each pixel
Dim red As Integer = Marshal.ReadByte(bmData.Scan0, redOfs)
Dim green As Integer = Marshal.ReadByte(bmData.Scan0, greenOfs)
Dim blue As Integer = Marshal.ReadByte(bmData.Scan0, blueOfs)
'Create a Color structure from each color component of the pixel
'and store it in the array
pixels(x, y) = Color.FromArgb(red, green, blue)
Next
Next
'Do something to the pixels array here:
For x = 0 To bmData.Width - 1
For y = 0 To bmData.Height - 1
pixels(x, y) = Color.Red
Next
Next
'Update the bitmap from the pixels array
For x = 0 To bmData.Width - 1
For y = 0 To bmData.Height - 1
'Get the various color offset locations for each pixel.
'This calculation is for a 24bpp rgb bitmap
Dim blueOfs As Integer = (bmData.Stride * x) + (PixelSize * y)
Dim greenOfs As Integer = blueOfs + 1
Dim redOfs As Integer = greenOfs + 1
'Set each component of the pixel
'There are 3 bytes that make up each pixel (24bpp rgb)
Marshal.WriteByte(bmData.Scan0, blueOfs, pixels(x, y).B)
Marshal.WriteByte(bmData.Scan0, greenOfs, pixels(x,y).G)
Marshal.WriteByte(bmData.Scan0, redOfs, pixels(x,y).R)
Next
Next
'Important!
bm.UnlockBits(bmData)
'Save the updated bitmap
bm.Save("C:\Users\Noah\Desktop\graphics\Grass.bmp", System.Drawing.Imaging.ImageFormat.Bmp)
End Sub
I hope this helps.
UPDATE: I have updated the code to show changing the pixel values.
Unfortunately, there isn't a property of the Bitmap class that returns an array/list of all the pixel colors, so you can't set them all at once.The Microsoft documentation page actually provides example code getting and setting pixel colors in two nested loops, much like you're already doing.
If your end goal is to store the pixel information to pass to another Bitmap, I would suggest the Clone method. Rather than expend the time and effort of extracting the data into your own format, just use the one that's already provided. The predefined methods are almost always the most efficient, so this should be your best bet for performance.
If you truly must store the colors in an array, then you'll have to write a method to loop through every color and use SetPixel one pixel at a time. This sounds like exactly what you're doing now though, so this won't save any time.
The Winforms System.Windows.Forms.Control class has an instance method "DrawToBitmap" which I think is very useful in a variety of circumstances. I'm wondering if there's an equivalent way of getting a System.Drawing.Bitmap from a WPF application?
I realize I could do some P/Invoke stuff to just get the application window, however I don't like this because it doesn't accomodate the 64bit transition very well, and doesn't let me render sub-controls only, as DrawToBitmap does.
Thanks,
Richard
Use RenderTargetBitmap as on MSDN
RenderTargetBitmap bitmap = new RenderTargetBitmap(width, height, 96, 96, PixelFormats.Pbgra32);
bitmap.Render(this.YourVisualControlNameGoesHere);
TFD is spot on.
You could also use the less elegant reference example from MSDN:
Dim width As Integer = 128
Dim height As Integer = width
Dim stride As Integer = CType(width / 8, Integer)
Dim pixels(height * stride) As Byte
' Try creating a new image with a custom palette.
Dim colors As New List(Of System.Windows.Media.Color)()
colors.Add(System.Windows.Media.Colors.Red)
colors.Add(System.Windows.Media.Colors.Blue)
colors.Add(System.Windows.Media.Colors.Green)
Dim myPalette As New BitmapPalette(Colors)
' Creates a new empty image with the pre-defined palette
Dim image As BitmapSource = System.Windows.Media.Imaging.BitmapSource.Create(width, height, 96, 96, PixelFormats.Indexed1, myPalette, pixels, stride)
Dim stream As New FileStream("new.bmp", FileMode.Create)
Dim encoder As New BmpBitmapEncoder()
Dim myTextBlock As New TextBlock()
myTextBlock.Text = "Codec Author is: " + encoder.CodecInfo.Author.ToString()
encoder.Frames.Add(BitmapFrame.Create(image))
encoder.Save(stream)