vb@rchiv
VB Classic
VB.NET
ADO.NET
VBA
C#
Brandneu! sevEingabe v3.0 - Das Eingabecontrol der Superlative!  
 vb@rchiv Quick-Search: Suche startenErweiterte Suche starten   Impressum  | Datenschutz  | vb@rchiv CD Vol.6  | Shop Copyright ©2000-2024
 
zurück

 Sie sind aktuell nicht angemeldet.Funktionen: Einloggen  |  Neu registrieren  |  Suchen

VB.NET - Ein- und Umsteiger
Bildervorschau - 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
Themenbaum einblendenGesamtübersicht  |  Zum Thema  |  Suchen

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
Themenbaum einblendenGesamtübersicht  |  Zum Thema  |  Suchen

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
Themenbaum einblendenGesamtübersicht  |  Zum Thema  |  Suchen

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.
Themenbaum einblendenGesamtübersicht  |  Zum Thema  |  Suchen

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
Themenbaum einblendenGesamtübersicht  |  Zum Thema  |  Suchen

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.
Themenbaum einblendenGesamtübersicht  |  Zum Thema  |  Suchen

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
Themenbaum einblendenGesamtübersicht  |  Zum Thema  |  Suchen

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
Themenbaum einblendenGesamtübersicht  |  Zum Thema  |  Suchen

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.
Themenbaum einblendenGesamtübersicht  |  Zum Thema  |  Suchen

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
Themenbaum einblendenGesamtübersicht  |  Zum Thema  |  Suchen

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
Themenbaum einblendenGesamtübersicht  |  Zum Thema  |  Suchen

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

Funktionen:  Zum Thema  |  GesamtübersichtSuchen 

nach obenzurück
 
   

Copyright ©2000-2024 vb@rchiv Dieter Otter
Alle Rechte vorbehalten.
Microsoft, Windows und Visual Basic sind entweder eingetragene Marken oder Marken der Microsoft Corporation in den USA und/oder anderen Ländern. Weitere auf dieser Homepage aufgeführten Produkt- und Firmennamen können geschützte Marken ihrer jeweiligen Inhaber sein.

Diese Seiten wurden optimiert für eine Bildschirmauflösung von mind. 1280x1024 Pixel