| |
VB.NET - Ein- und UmsteigerBildervorschau - Bilder verkleinern - Shellandwait | | | Autor: Volker Bunge | Datum: 05.01.13 02:02 |
| Hallo Zusammen,
bin gerade dabei, ein Programm zu schreiben, in denen ich ein Verzeichnis mit Bildern durchsuchen lassen möchte und mir die Bilder als kleine Vorschaubilder anzeigen lassen möchte.
Die Pictureboxen lasse ich per Code in das Form einfügen. Anschließend werden die Bilder geladen und per Zoom automatisch verkleinert. So weit alles bestens. Die Picturebox ist 143 x 205 Pixel groß und lädt JPG-Dateien die eine Größe zwischen 490 - 902 kb groß sind (Das sind erst einmal meine Testbilder).
Baue ich Bildrahmen(z).WaitOnLoad = True ein, dann bricht er mir mit dieser Fehlermeldung ab:
System.OutOfMemoryException wurde nicht behandelt.
HResult=-2147024882
Message=Nicht genügend Arbeitsspeicher.
Source=System.Drawing
StackTrace:
bei System.Drawing.Image.FromStream(Stream stream, Boolean useEmbeddedColorManagement, Boolean validateImageData)
bei System.Windows.Forms.PictureBox.Load()
bei System.Windows.Forms.PictureBox.OnPaint(PaintEventArgs pe)
bei System.Windows.Forms.Control.PaintWithErrorHandling(PaintEventArgs e, Int16 layer)
bei System.Windows.Forms.Control.WmPaint(Message& m)
bei System.Windows.Forms.Control.WndProc(Message& m)
bei System.Windows.Forms.Control.ControlNativeWindow.OnMessage(Message& m)
bei System.Windows.Forms.Control.ControlNativeWindow.WndProc(Message& m)
bei System.Windows.Forms.NativeWindow.DebuggableCallback(IntPtr hWnd, Int32 msg, IntPtr wparam, IntPtr lparam)
bei System.Windows.Forms.UnsafeNativeMethods.DispatchMessageW(MSG& msg)
bei System.Windows.Forms.Application.ComponentManager.System.Windows.Forms.UnsafeNativeMethods.IMsoComponentManager.FPushMessageLoop(IntPtr dwComponentID, Int32 reason, Int32 pvLoopData)
bei System.Windows.Forms.Application.ThreadContext.RunMessageLoopInner(Int32 reason, ApplicationContext context)
bei System.Windows.Forms.Application.ThreadContext.RunMessageLoop(Int32 reason, ApplicationContext context)
bei System.Windows.Forms.Application.Run(ApplicationContext context)
bei Microsoft.VisualBasic.ApplicationServices.WindowsFormsApplicationBase.OnRun()
bei Microsoft.VisualBasic.ApplicationServices.WindowsFormsApplicationBase.DoApplicationModel()
bei Microsoft.VisualBasic.ApplicationServices.WindowsFormsApplicationBase.Run(String[] commandLine)
bei Scannprogramm.My.MyApplication.Main(String[] Args) in 17d14f5c-a337-4978-8281-53493378c1071.vb:Zeile 81.
bei System.AppDomain._nExecuteAssembly(RuntimeAssembly assembly, String[] args)
bei System.AppDomain.ExecuteAssembly(String assemblyFile, Evidence assemblySecurity, String[] args)
bei Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly()
bei System.Threading.ThreadHelper.ThreadStart_Context(Object state)
bei System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
bei System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
bei System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
bei System.Threading.ThreadHelper.ThreadStart()
InnerException:
Ohne diese Zeile läuft zwar das Programm ohne Fehler durch, doch von meinen 39 Bilder werden nicht alle dagestellt. Es fehlen wahl los immer mal ein paar Bilder.
Die Prozessorbelastung steigt dabei auf ca. 1,5 GB an, was auch den o. g. Abbruch erklärt.
Laden der Bilder erfolgt über Bildrahmen(z).ImageLocation = "Pfad des Bildes"
In Access 2007 habe ich das gleiche Problem. Da helfe ich mir mit einer externen Resizer.exe, die ich über eine Shellandwait Funktion aufrufe.
Leider habe ich für VB 2010 keine passende bzw. funktionierende Routine gefunden, die dass externe Programm aufruft, wartet, bis dieses fertig ist und dann weiter macht.
Auch hier im Forum finde ich keine passende Shellandwait - Funktion für VB 2010.
Habt Ich eine passende Funktion oder gibt es eine andere Möglichkeit, die Bilder Speicherschonender anzeigen zu lassen?
Ich weiss, man soll nicht so viel schreiben, aber damit man mein Problem versteht, habe ich mir gedacht, schreibe mal alles auf.
Vielen Dank
Volker | |
Re: Bildervorschau - Bilder verkleinern - Shellandwait | | | Autor: Manfred X | Datum: 05.01.13 10:01 |
| Hallo!
Beim Zoomen per Picturebox werden die Bilder im Speicher nicht verkleinert.
Ein JPEG dieser Größe - als Bitmap decodiert geladen, nicht als MemoryStream -
benötigt vermutlich im Speicher etliche MegaByte.
Du solltest per Graphics.Drawimage jedes in eine Bitmap geladene Bild in
Thumbnail-Größe neu Zeichnen lassen und das Bild selbst wieder aus
dem Speicher entfernen (verwendete Bitmap Disposen).
MfG
Manfred | |
Re: Bildervorschau - Bilder verkleinern - Shellandwait | | | Autor: Volker Bunge | Datum: 05.01.13 11:56 |
| Hallo Manfred,
kannst Du mir auch noch kurz verraten, wie der Code dazu aussehen muß?
MfG
Volker | |
Re: Bildervorschau - Bilder verkleinern - Shellandwait | | | Autor: Manfred X | Datum: 05.01.13 12:29 |
| Hier eine Funktion, die alle
JPEGs in einem Ordner lädt, sie in eine vorgegebene Thumbnail-Größe
einpasst und ein Array zurückgibt.
Diese Funktion könnte "asynchron" in einem Backgroundworker
ausgeführt werden.
''' <summary>JPEGs im Ordner als Thumbnails laden</summary>
''' <param name="PicPath">Ordner mit JPEG-Dateien</param>
''' <param name="width">Thumbnails: maximale Breite (10-400 Pixel)</param>
''' <param name="height">Thumbnails: maximale Höhe (10-400 Pixel</param>
''' <returns>Bitmap-Array mit Thumbnails (oder NOTHING)</returns>
Public Function GetPics(ByVal PicPath As String, _
Optional ByVal width As Integer = 300, _
Optional ByVal height As Integer = 300) As Bitmap()
'Parameter prüfen
If PicPath Is Nothing Then Return Nothing
If Not IO.Directory.Exists(PicPath) Then Return Nothing
If width < 10 Or width > 400 Then Return Nothing
If height < 10 Or height > 400 Then Return Nothing
Try
'Bilderliste für Rückgabe
Dim pics As New List(Of Bitmap)
'JPEG-Dateien im Ordner ermitteln und bearbeiten
For Each file As String In My.Computer.FileSystem.GetFiles _
(PicPath, FileIO.SearchOption.SearchTopLevelOnly, "*.jpg", _
"*.jpeg")
Using bmp_loaded As New Bitmap(file) 'Bild-Datei laden
With bmp_loaded
'Thumbnail-Größe ermitteln
Dim twidth As Integer = width
Dim theight As Integer = CInt(.Height * width / .Width)
If theight > height Then
theight = height
twidth = CInt(.Width * height / .Height)
End If
'Thumbnail erstellen
Using thumbnail As New Bitmap(twidth, theight),
gr As Graphics = Graphics.FromImage(thumbnail)
gr.DrawImage(bmp_loaded, 0, 0, twidth, theight)
'Thumbnail-Kopie in der Liste anhängen
pics.Add(CType(thumbnail.Clone, Bitmap))
End Using 'Thumbnail freigeben
End With
End Using 'bmp_loaded freigeben
Next file 'JPEG-File
'Thumbnails als Array zurückgeben
Return pics.ToArray
Catch
'Fehler beim Laden des Bildes
Return Nothing
End Try
End Function Anwendung:
Dim thumbnails() As Bitmap = GetPics("C:\Pictures", 300, 300) Die Rückgabe zunächst auf Nothing abfragen
(falls Fehler aufgetreten sind).
Beitrag wurde zuletzt am 05.01.13 um 12:31:26 editiert. | |
Re: Bildervorschau - Bilder verkleinern - Shellandwait | | | Autor: Volker Bunge | Datum: 05.01.13 19:31 |
| Hallo Manfred,
vielen Dank erst einmal für die schnelle Antwort.
Bin erst gerade wiederkommen und grüble nun, wie ich die verkleinerten Bilder anzeigen lassen kann.
Was ich am Ende brauche, ist eine Information, über das gerade angeklickte Bild. Einmal um dies evtl. in einer vergrößerten Version anzeigen zu lassen und zum anderen, um weitere Aktionen ab diesem Bild ausführen zu können.
Hast Du da einen Tip für mich?
MfG
Volker | |
Re: Bildervorschau - Bilder verkleinern - Shellandwait | | | Autor: Manfred X | Datum: 05.01.13 20:11 |
| Ich weiss nicht, wie Du Deine Oberfläche gestaltest.
Hier ein Beispiel für eine Reihe von PictureBoxen, die
in einer Liste aufbewahrt werden und deren Click-Event-Behandlung
über einen Addhandler zugeordnet wird.
Die Feststellung, welche Picturebox angeclickt worden ist,
erfolgt über den Eintrag in der Tag-Eigenschaft.
Das Erstellen der Bitmaps erfolgt mit der oben geposteten Routine.
Sei "frmThumbs" ein Windows.Forms-Formular
Public Class frmThumbs
Dim mnuMain As New MenuStrip With {.Parent = Me}
Dim fbd As New FolderBrowserDialog With _
{.Description = "Ordner mit Bilddateien", _
.ShowNewFolderButton = False}
Dim pb_list As New List(Of PictureBox) 'Liste für Pictureboxen
Private Sub frmThumbs_Load(sender As System.Object, _
e As System.EventArgs) Handles MyBase.Load
Dim th_Width As Integer = 200
Me.Size = New Size(th_Width * 4 + 10, th_Width * 4 + 80)
mnuMain.Items.Add _
("Bilder laden", Nothing, AddressOf mnuLoad_click)
'Erstellung/Positionierung der Pictureboxen in Liste
Dim z As Integer
For i As Integer = 0 To 3
For k As Integer = 0 To 3
z += 1
Dim pb As New PictureBox With _
{.Parent = Me, .Top = i * th_Width + 50, _
.Left = k * th_Width, _
.Width = th_Width, .Height = th_Width, _
.BorderStyle = BorderStyle.FixedSingle,
.SizeMode = PictureBoxSizeMode.Zoom, _
.Tag = z}
AddHandler pb.Click, AddressOf pbox_click
pb_list.Add(pb)
Next k
Next i
End Sub
Private Sub pbox_click(ByVal sender As Object, e As EventArgs)
Dim pb As PictureBox = CType(sender, PictureBox)
Dim z As Integer = CInt(pb.Tag)
MsgBox(CStr(z) & " ist abgeclickt")
'Hier Code für Bildbearbeitung einfügen
End Sub
Private Sub mnuload_click(ByVal sender As Object, e As EventArgs)
'Wahl eines Ordners und Erstellung der Thumbnails
With fbd
If .ShowDialog = Windows.Forms.DialogResult.Cancel Then Exit Sub
Me.Refresh()
Me.Cursor = Cursors.WaitCursor
Dim thumbnails() As Bitmap = GetPics(.SelectedPath, 300, 300)
If thumbnails Is Nothing _
OrElse thumbnails.Length = 0 Then Exit Sub
For i As Integer = 0 To pb_list.Count - 1
If i < thumbnails.Length Then
'Thumbnails in Pictureboxen anzeigen
pb_list(i).Image = thumbnails(i)
End If
Next i
Me.Cursor = Cursors.Default
End With
End Sub
'Hier restlichen Code einfügen
Beitrag wurde zuletzt am 05.01.13 um 20:21:11 editiert. | |
Re: Bildervorschau - Bilder verkleinern - Shellandwait | | | Autor: Volker Bunge | Datum: 05.01.13 23:16 |
| Hallo Manfred,
hat nun alles super geklappt, nach einigen Anpassungen funktioniert der Aufbau der Bilder super. Auch die Geschwindigkeit ist bei meinen 390 Testbildern recht schnell (2:13 Minuten).
Das einzige, was mich jetzt noch stört ist, wenn ich eine nachträglich Bilder in das Verzeichnis hineinkopiere, dann werden zwar beim erneuten Anklicken 'Bilder Laden' die neuen Pictureboxrahmen eingefügt, aber das Bild ist nicht zu sehen.
Das Problem kann ja nicht dadurch gelöst werden, die Form bzw. das Programm neu zu starten.
Mir fehlt also ein Refresh der Anzeige.
Hast Du da auch eine Lösung für mich?
Vielen Dank
Volker | |
Re: Bildervorschau - Bilder verkleinern - Shellandwait | | | Autor: Volker Bunge | Datum: 06.01.13 10:25 |
| Hallo Manfred,
habe soeben ein kleinen Fortschritt erreicht.
Wenn Ich erst 5 und dann 10 Bilder einlesen lassen, dann werden mir die fehlenden Pictureboxen hinzugefügt.
Folgende Änderung habe ich durchgeführt:
Private Sub mnuload_click(ByVal sender As Object, e As EventArgs)
'Wahl eines Ordners und Erstellung der Thumbnails
With fbd
' Änderung bzw. Ergänzung
pb_list.Clear()
Nun brauche ich noch eine Methode, um den umgekehrten Weg optisch richtig hinzubekommen, hast Du da auch eine Lösung für mich?
MfG
Volker | |
Re: Bildervorschau - Bilder verkleinern - Shellandwait | | | Autor: Manfred X | Datum: 06.01.13 14:38 |
| Hallo!
Eigentlich arbeitet man mit einer festen Zahl von Pictureboxen,
denen Bilder variabel zugewiesen werden.
Hier ein Beispiel mit komprimierter Ladung der Bilddaten in
MemoryStreams (per Backgroundworker).
Bei Bedarf erfolgt die Bildauswahl über eine Trackbar.
(Eine Progressbar zur Anzeige des Ladefortschritts kann eingebaut werden.)
Public Class frmThumbsII
Dim mnuMain As New MenuStrip With {.Parent = Me}
Dim fbd As New FolderBrowserDialog With _
{.Description = "Ordner mit Bilddateien", _
.ShowNewFolderButton = False}
Dim pb_list As New List(Of PictureBox)
Dim pics As New List(Of IO.MemoryStream)
Dim WithEvents trbPics As New TrackBar With _
{.Parent = Me, .Dock = DockStyle.Bottom}
Dim WithEvents bgw As New System.ComponentModel.BackgroundWorker
Dim PicPath As String
Private Sub frmThumbsII_Load(sender As System.Object, _
e As System.EventArgs) Handles MyBase.Load
Me.FormBorderStyle = Windows.Forms.FormBorderStyle.FixedSingle
Dim th_Width As Integer = 150
Me.Size = New Size(th_Width * 4 + 10, th_Width * 4 + 130)
trbPics.Height = 50 : trbPics.Top = Me.Height - 80
mnuMain.Items.Add("Bilder laden", Nothing, AddressOf mnuload_click)
Dim z As Integer
For i As Integer = 0 To 3
For k As Integer = 0 To 3
z += 1
Dim pb As New PictureBox With _
{.Parent = Me, .Top = i * th_Width + 50, .Left = k * _
th_Width, _
.Width = th_Width, .Height = th_Width, _
.BorderStyle = BorderStyle.FixedSingle,
.SizeMode = PictureBoxSizeMode.Zoom, .Tag = z}
AddHandler pb.Click, AddressOf pbox_click
pb_list.Add(pb)
Next k
Next i
End Sub
Private Sub mnuload_click(ByVal sender As Object, e As EventArgs)
With fbd
If .ShowDialog = Windows.Forms.DialogResult.Cancel Then Exit Sub
Me.UseWaitCursor = True
trbPics.Visible = False
Me.Refresh()
PicPath = .SelectedPath
bgw.RunWorkerAsync()
End With
End Sub
Private Sub pbox_click(ByVal sender As Object, e As EventArgs)
Dim pb As PictureBox = CType(sender, PictureBox)
Dim z As Integer = CInt(pb.Tag)
MsgBox("Bild " & CStr(z) & " ist angeclickt")
End Sub
Private Sub trbPics_ValueChanged(sender As Object, _
e As System.EventArgs) Handles trbPics.ValueChanged
Static IsBusy As Boolean
If IsBusy Then Exit Sub
IsBusy = True
For i As Integer = 0 To pb_list.Count - 1
With pb_list(i)
If .Image IsNot Nothing Then
.Image = Nothing : .Tag = -1
End If
Dim index As Integer = trbPics.Value + i
If index < pics.Count Then
.Image = Image.FromStream(pics(index))
.Tag = trbPics.Value + i
End If
.Refresh()
End With
Next i
IsBusy = False
End Sub
Private Sub bgw_DoWork(sender As Object, _
e As System.ComponentModel.DoWorkEventArgs) Handles bgw.DoWork
pics.Clear()
For Each file As String In My.Computer.FileSystem.GetFiles(PicPath, _
FileIO.SearchOption.SearchTopLevelOnly, "*.jpg")
Using fs As New IO.FileStream(file, IO.FileMode.Open, _
IO.FileAccess.Read)
Dim buffer(CInt(fs.Length - 1)) As Byte
fs.Read(buffer, 0, buffer.Length)
pics.Add(New IO.MemoryStream(buffer))
End Using
Next file
End Sub
Private Sub bgw_RunWorkerCompleted(sender As Object, _
e As System.ComponentModel.RunWorkerCompletedEventArgs) _
Handles bgw.RunWorkerCompleted
With trbPics
.Value = 0
.Visible = pics.Count > pb_list.Count
If .Visible Then .Maximum = pics.Count - pb_list.Count
.TickFrequency = Math.Max(1, pics.Count \ 10)
End With
Me.Text = CStr(pics.Count) & " JPEGs sind geladen"
trbPics_ValueChanged(Me, Nothing)
Me.UseWaitCursor = False
End Sub
End Class
Beitrag wurde zuletzt am 06.01.13 um 14:40:59 editiert. | |
Re: Bildervorschau - Bilder verkleinern - Shellandwait | | | Autor: Volker Bunge | Datum: 10.01.13 17:10 |
| Hallo Manfred.
erst einmal vielen Dank für schnelle Antwort und sorry, dass ich mich erst jetzt melde.
Bis jetzt funktioniert alles so, wie ich es will.
Ich erkläre noch einmal kurz, was mein Programm machen soll bzw. kann.
- Seiten werden gescannt und als Page xxxx.jpg abgelegt (Externes Scannprogramm)
- Eine Barcodeerkennungsroutine läuft über diese Dateien und erkennt die Barcodes und ändert die Dateinamen.
- Die Übersicht soll nun alle erkannten Barcodefälle (1 oder mehrere Seiten) anzeigen.
Da die Erkennung und somit die Trennung der Seiten nicht immer 100%ig funktioniert, stehen folgende Möglichkeiten zur Verfügung:
- Drehen der Seiten
- Barcodefall nach oben zum vorherigen schieben
- Barcodefall trennen. Also ab der markierten Seite einen neuen Fall erzeugen
- Seiten löschen (ist zur Zeit noch nicht eingebaut, kommt aber noch)
Diese Veränderungen sind bei einem Neustart des Programms auch alle richtig dargestellt.
Und hier liegt nun mein optischer Haken bzw. Problem: Erst nach einem Neustart.
Wunsch:
Eine Möglichkeit, die mir die alten Pictureboxen (und mittlerweile auch für jedes Bild eine passende Textbox) löscht und die Übersicht wieder neu aufbaut (letzteres müsste ja durch die vorhandene Funktion möglich sein).
Ich hoffe, dass meine Erklärung nun ein paar Fragen beantwortet und somit die gesuchte Lösung meinem Wunsch entspricht.
Vielen Dank schon einmal im Voraus.
Volker | |
Re: Bildervorschau - Bilder verkleinern - Shellandwait | | | Autor: Volker Bunge | Datum: 10.01.13 20:17 |
| Hallo Manfred,
Hallo alle anderen,
habe gerade noch einmal einwenig rumprobiert und eine Lösung gefunden, die ich Euch nicht vorenthalten möchte
Public Sub Anzeige_aktualisieren(ByVal sender As System.Object, ByVal e As System.EventArgs)
For i As Integer = 0 To pb_list.Count - 1
With pb_list(i)
.Dispose()
.Refresh()
End With
Next i
For i As Integer = 0 To tb_list.Count - 1
With tb_list(i)
.Dispose()
.Refresh()
End With
Next i
' Man kann natürlich auch die beiden Schleifen zusammen fassen, habe ich aber nicht getan.
Call mnuload_click(sender, e)
End Sub
Die Variablendeklaration benötige ich, damit ich die Sender und e-Variable an die mnuload_click weiterleiten kann. Ich mußte zwar noch einige Variablen, die ich für die Anzeige der Pictureboxen benötige, auf den Ausgangswert setzen, aber jetzt funktioniert es, so wie ich es mir vorgestellt habe. Die Sub rufe ich überall dort auf, wo eine Aktualisierung nötig ist.
Die Speicherauslastung sieht auch recht gut aus, so dass ich davon ausgehe, genau das richtige getroffen zu haben.
Vielen Dank noch einmal an Manfred, ohne Dich wäre ich überhaupt nicht weiter gekommen.
Gruß
Volker | |
| Sie sind nicht angemeldet! Um auf diesen Beitrag zu antworten oder neue Beiträge schreiben zu können, müssen Sie sich zunächst anmelden.
Einloggen | Neu registrieren |
|
|
Neu! sevCoolbar 3.0
Professionelle Toolbars im modernen Design!
Mit sevCoolbar erstellen Sie in wenigen Minuten ansprechende und moderne Toolbars und passen diese optimal an das Layout Ihrer Anwendung an (inkl. große Symbolbibliothek) - für VB und MS-Access Weitere InfosTipp des Monats TOP Entwickler-Paket
TOP-Preis!!
Mit der Developer CD erhalten Sie insgesamt 24 Entwickler- komponenten und Windows-DLLs. Die Einzelkomponenten haben einen Gesamtwert von 1605.50 EUR...
Jetzt nur 599,00 EURWeitere Infos
|