Event an dynamisches Control

  • VB.NET
  • .NET (FX) 4.0

Es gibt 15 Antworten in diesem Thema. Der letzte Beitrag () ist von ErfinderDesRades.

    Event an dynamisches Control

    Moin

    wenn ich zwei Dateien über den WebClient herunterlade..

    VB.NET-Quellcode

    1. Dim client As WebClient = New WebClient
    2. AddHandler client.DownloadProgressChanged, AddressOf client_ProgressChanged
    3. client.DownloadFileAsync(New Uri("http://URL.com/Myfile.exe"), "..\MyFile.exe")
    4. Dim client2 As WebClient = New WebClient
    5. AddHandler client2.DownloadProgressChanged, AddressOf client_ProgressChanged2
    6. client2.DownloadFileAsync(New Uri("http://URL.com/Myfile2.exe"), "..\MyFile2.exe")


    ..kann ich Events gezielt adressieren:

    VB.NET-Quellcode

    1. Private Sub client_ProgressChanged(ByVal sender As Object, ByVal e As DownloadProgressChangedEventArgs)
    2. Dim bytesIn As Double = Double.Parse(e.BytesReceived.ToString())
    3. Dim totalBytes As Double = Double.Parse(e.TotalBytesToReceive.ToString())
    4. Dim percentage As Double = bytesIn / totalBytes * 100
    5. ProgressBar1.Value = Int32.Parse(Math.Truncate(percentage).ToString())
    6. End Sub
    7. Private Sub client_ProgressChanged2(ByVal sender As Object, ByVal e As DownloadProgressChangedEventArgs)
    8. Dim bytesIn As Double = Double.Parse(e.BytesReceived.ToString())
    9. Dim totalBytes As Double = Double.Parse(e.TotalBytesToReceive.ToString())
    10. Dim percentage As Double = bytesIn / totalBytes * 100
    11. progressBar2.Value = Int32.Parse(Math.Truncate(percentage).ToString())
    12. End Sub


    .. und dann den Status in der ProgressBar anzeigen.

    Wenn ich nun aber eine dynamische Anzahl an WebClients/Downloads habe, wie kann ich dazu jeweils eine ProgressBar anlegen lassen und die nötigen Parameter übergeben? Letztlich möchte ich gerne den Fortschritt von verschiedenen Downloads (gleichzeitig) in einer Liste oder verschiedenen ProgressBar ausgeben.
    Moin,

    nur auf die Schnelle. Vergiss dynamische Controls, so wie Du sie im Moment verwendest. So rennst Du schnell gegen Wände.

    Auf Dein Vorhaben hier kurz einzugehen: Erstelle Dir (beispielsweise) ein UserControl, das genau diese Funktion kapselt.

    VB.NET-Quellcode

    1. Imports System.Net
    2. Public Class Downloader
    3. Private _url As Uri
    4. Public Sub New(url As String)
    5. InitializeComponent()
    6. Me.ProgressBar1.Maximum = 100
    7. _url = New Uri(url)
    8. End Sub
    9. Public Sub StartDownload()
    10. Dim wC As New AdvancedWebClient() With {.Proxy = Nothing}
    11. AddHandler wC.DownloadProgressChanged, AddressOf DownloadprogessChanged
    12. wC.DownloadFileAsync(_url, _url.Segments.Last())
    13. End Sub
    14. Private Sub DownloadprogessChanged(sender As Object, e As DownloadProgressChangedEventArgs)
    15. Me.ProgressBar1.Value = e.ProgressPercentage
    16. Me.Label1.Text = String.Concat(e.BytesReceived, "/", e.TotalBytesToReceive)
    17. End Sub




    Verwenden kannst Du das dann z. B. so:

    VB.NET-Quellcode

    1. Public Class Form1
    2. Private _urls As New List(Of String) From {"http://download.thinkbroadband.com/100MB.zip", "http://download.thinkbroadband.com/200MB.zip", _
    3. "http://download.thinkbroadband.com/20MB.zip", "http://download.thinkbroadband.com/512MB.zip"}
    4. Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
    5. For Each x As String In _urls
    6. Dim d As New Downloader(x)
    7. Me.FlowLayoutPanel1.Controls.Add(d)
    8. d.StartDownload()
    9. Next
    10. End Sub
    11. End Class


    Die WebClient-Klasse lädt per Default nur 2 Dateien gleichzeitig. Um das abzuändern:

    VB.NET-Quellcode

    1. Imports System.Net
    2. Public Class AdvancedWebClient : Inherits WebClient
    3. Protected Overrides Function GetWebRequest(address As Uri) As Net.WebRequest
    4. Dim r As HttpWebRequest = DirectCast(MyBase.GetWebRequest(address), HttpWebRequest)
    5. r.ServicePoint.ConnectionLimit = 10
    6. Return r
    7. End Function
    8. End Class


    Dies erhöht das Limit auf 10.

    Ergebnis:

    Die Unendlichkeit ist weit. Vor allem gegen Ende. ?(
    Manche Menschen sind gar nicht dumm. Sie haben nur Pech beim Denken. 8o
    hmm - erst sagst du (sehr richtig) "vergiss dynamische Controls", und dann kommste selbst mit einem, nämlich einem UserControl.
    In meiner Welt ist ein Vielfach-Download eine Datenverarbeitung und wird mit Datenmodell und Databinding abgehandelt, zuzüglich bischen OwnerDrawing. Die DownloadJobs sind Datensätze, und kann man problemlos im DGV anzeigen, und man kann mittels OwnerDrawing auch Progressbars im DGV gestalten: vb-paradise.de/allgemeines/tip…s-databinding/#post488404
    @hate_regex Zum parallelen Download gugst Du hier.
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    Hallo,

    und danke für Eure Antworten!

    @SpaceyX
    Das Problem dabei ist, dass ich kein festes Control verwenden möchte.
    Textbox1 oder ProgressBar1 gibt es nicht auf der Form sondern werden dynamisch in einer Schleife hinzugefügt.
    Konkret habe ich ein Datagrid und möchte dort für jeden Download einen Spalteneintrag machen, der sich bis zum Ende des Downloads aktualisiert.
    Leider weiß ich nicht wie ich an ein dynamisches Control anspreche. Die Lösung von dir funktioniert, jedoch nur, wenn ich die Controls statisch vorher anlege.

    @ErfinderDesRades
    Ja, ich bin kein großer Held der Programmierung - Hauptsache Ihr habt Spaß (sagt die Werbung).
    Klingt mächtig kompliziert. Danke für den Link, ich werde versuchen das zu verstehen und für meinen Fall anzuwenden.
    Ich habe bisher noch nicht durchschaut wo ich in deinem Beispiel den AsyncDownload einfügen muss um die Bar anzusprechen.

    @RodFromGermany
    Die Anzahl an parallelen Downloads sind kein Problem. Ich packe sie in einen Thread und instanziere für diesen jeweils einen neuen WebClient. Das Problem ist, die Events dieser Threads für den Client auszuwerten (Progress) und in das DGV zu schreiben.
    @hate_regex In meinem Beispielcode hast Du doch von jedem Download ein Progress-Event an die GUI.
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    Damit probiere ich gerade herum.
    Mit der Sub New habe ich Probleme, hier soll noch InitializeComponent() erfolgen, was vermutlich gewollt ist:
    'Public Sub New(index As Integer)' in designer-generated type 'WindowsApplication1.Form1' should call InitializeComponent method.
    Werd ich schon schnallen :)

    Ich denke der Lösungsvorschlag vonm ErfinderDesRades liegt nahe an dem, was ich suche. Zusammen mit deinem Multidownloader sollte das funktionieren. Irgendwie. Vielleicht verzichte ich einfach auf den Downloadprogress..

    hate_regex schrieb:

    should call InitializeComponent method
    Eine Form will in ihrem Konstruktor aufgebaut werden, deshalb muss da die Prozedur InitializeComponent() aufgerufen werden.
    Wennnicht, hast Du eine leere Form.
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    @ErfinderDesRades
    Ja, das stimmt. Leider reicht mein Verständnis in VB nicht aus um es auch nachhaltig zu verstehen. Die Lösung liegt vor der Nase, aber es macht nicht "Bling".
    Übergabe erfolgt per ADataBindingSource.AddNew() nur bekomme ich den WebClient/Download nicht verknüpft.

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „hate_regex“ ()

    Vielleicht kannst du mir unter die Arme greifen..

    Ich nehme als Grundlage den von dir geposteten Code und versuche diesen anzupassen.
    Ich möchte also nun exemplarisch bei MenuItem_Click auf Case sender Is btAdd einen Download starten.

    VB.NET-Quellcode

    1. Dim DownloadClient As New WebClient
    2. DownloadClient.DownloadFileAsync(New Uri(URL), "file.zip")

    Nur schnalle ich die BindingSource nicht ...
    ich sehe keinen DownloadJob als Datensatz eines typDatasets modelliert.

    Eine Datenverarbeitung sollte nicht mit iwelchen ButtonKlicksen anfangen, sondern zunächst muss das Datenmodell designed sein. Ich nehm da immer ein typisiertes Dataset für.

    vlt. helfen diese Videos: vier Views-Videos
    Dein Modellieren besteht nu darin, dass du dir überlegen musst, welche Eigenschaften ein DownloadJob haben muss, dass er ausgeführt werden kann.
    Spontan fällt mir dazu ein:
    Url, TargetFile, ProgressPercentage

    Wenn du soweit bist, poste Screenshot vom DatasetDesigner
    Eine BindingSource ist im Prinzip dazu dar, eine List(Of T), genauer genommen eine IEnumerable(Of T), welches du von egal woher bekommst, an ein Control "zu binden", sprich anzuzeigen.
    So kannst du etwa eine Liste von Mitarbeitern in einer Listbox anzeigen, ohne zuerst die Liste durchzulaufen und dann liste.Add(mitarbeiter) zu machen.
    Außerdem hast du die Möglichkeit, sofern sich die Urpsrungsauflistung geändert hat, diese Änderung mit einem Event zu erkennen und entsprechend die Listbox oder das DGV zu aktualisieren.

    Hier mal ein kleines (minimalistisches) Beispiel:

    VB.NET-Quellcode

    1. Imports System
    2. Imports System.Windows.Forms
    3. Imports System.ComponentModel
    4. Imports System.Collections.Generic
    5. Public Class Form1
    6. Dim WithEvents bs As BindingSource
    7. Dim WithEvents lbMitarbeiter As ListBox
    8. Dim maListe As IEnumerable(Of Mitarbeiter)
    9. Public Sub New()
    10. InitializeComponent()
    11. Me.bs = New BindingSource()
    12. Me.lbMitarbeiter = New ListBox()
    13. Me.maListe = GetData()
    14. Me.bs.DataSource = Me.maListe
    15. Me.lbMitarbeiter.DataSource = bs
    16. End Sub
    17. Public Function GetData() As IEnumerable(Of Mitarbeiter)
    18. Return New List(Of Mitarbeiter)
    19. End Function
    20. Private Sub bs_CurrentChanged(sender As Object, e As EventArgs) Handles bs.CurrentChanged
    21. Dim currentItem = CType(Me.bs.Current, Mitarbeiter)
    22. 'currentItem kann nun verwendet werden
    23. End Sub
    24. Private Sub bs_ListChanged(sender As Object, e As ListChangedEventArgs)
    25. End Sub
    26. End Class
    27. Public Class Mitarbeiter
    28. Public Property ID As Integer
    29. Public Property Name As String
    30. End Class


    Um es zusammen zu fassen: Die BindingSource fungiert als Mittelsmann zwischen der Liste und der Darstellungsform. Zuerst brauchst du ein Controls, welches die Daten darstellen kann (DataGrid/DataGridView, ListBox, Combobox) dann erzeugst du dir eine Instanz deiner BindingSource. Anschließend holst du dir noch deine Werte ab und speicherst sie in einer Auflistung (IEnumerable(Of T) ist da eigentlich immer eine gute Anlaufstelle). Dann brachst du nur noch kaskadiert (heißt nach einander) zuerst dem BindingSource-Objekt als DataSource die Auflistung mitgeben und anschließend dem Controls als DataSource die BindingSource überreichen. Fertig! Nur noch kompillieren und schon müssten die Daten in deinem Control erscheinen.

    Lg Radinator
    In general (across programming languages), a pointer is a number that represents a physical location in memory. A nullpointer is (almost always) one that points to 0, and is widely recognized as "not pointing to anything". Since systems have different amounts of supported memory, it doesn't always take the same number of bytes to hold that number, so we call a "native size integer" one that can hold a pointer on any particular system. - Sam Harwell

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „Radinator“ ()

    @ErfinderDesRades
    Ich hab mir jetzt (ohne Übertreibung) stundenlang Dokumentionen oder Videos zu diversen Themen gegeben. Alleine die Links von dir verzweigten sich ja x-Fach und haben mich gut beschäftigt. Unterm Strich jedoch sehr verwirrend, da irgendwie jedes Tutorial eine andere Herangehensweise hat und mein Thema nur zum Teil betrifft (siehe dein Nachposter der direkt eine andere Lösung vorgeschlagen hat). Keine davon ist für VB-Boons wie mich ideal da recht anspruchsvoll - im Netz finde unzähle Ansätze, aber keines zu meinem Problem explizit. Auch mit den von dir erwähnten DataSets habe ich gebastelt, jedoch auch mäßig erfolgreich. Hab ich nach wie vor nicht verstanden, für mich sieht das mehr nach Datenbankanbindung mit Table-Mapping aus. Ist vermutlich ein Thema mit dem man sich weitergehend beschäftigen muss..



    @Radinator
    Vielen Dank für das Beispiel, verstanden. Jedenfalls das gepostete Beispiel :) Ich versuche das jetzt mal mit einerm DGV und einem WebClient.
    Aktuell habe ich wohl offenbar Probleme mit Typkonvertierungen:

    Quellcode

    1. Das Objekt des Typs "WindowsApplication1.DlClass" kann nicht in Typ "System.Collections.Generic.IEnumerable`1[WindowsApplication1.DlClass]" umgewandelt werden
    .


    Edit: Ähm, ja. Ich denke hier solle ich die Kuh im Stall lassen. Das überschreitet weit meine Programmierkenntnisse, dass hatte ich mir einfacher vorgestellt.
    Dank Euch nochmal für die sehr bemühte Hilfe, aber ich will hier niemanden etwas aus der Nase ziehen müssen und ich bekomme es offenkundig selbst nicht hin. Somit erledigt, dass bringt nix. Vielleicht bleibt ja zwischen Job, Frau und Kind bei Gelegenheit mal Zeit für ein gutes Fachbuch..

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „hate_regex“ ()

    Zeig mal bitte den Code, der die Ausnahme verursacht
    In general (across programming languages), a pointer is a number that represents a physical location in memory. A nullpointer is (almost always) one that points to 0, and is widely recognized as "not pointing to anything". Since systems have different amounts of supported memory, it doesn't always take the same number of bytes to hold that number, so we call a "native size integer" one that can hold a pointer on any particular system. - Sam Harwell

    hate_regex schrieb:

    für mich sieht das mehr nach Datenbankanbindung mit Table-Mapping aus.
    Jo, ds geht mir immer so auf die Nerven.
    Ich sag "Dataset" und "Datenverarbeitung", und keiner hört zu.
    Alle hören immer nur "Datenbank" - und "Datenbank" meine ich genau nicht.

    Mit einem Multi-Downloader schreibst du eine Datenverarbeitung, also einen DownloadJob würde ich dringend empfehlen, als Datensatz aufzufassen.
    Du musst diese Datensätze eingeben, ändern, löschen können, und noch mehr, nämlich starten, evtl. pausieren, abbrechen.
    So ein Datensatz fasst die dafür notwendigen Eigenschaften zusammen - sagte ich schon.

    Von einer Datenbank sagte ich nichts, ausser, dass Datenbank hier nicht das Thema ist.

    Aber du musst dich entscheiden - welchem Konzept du folgen willst.
    SpaceyX hat was mit UserControls, RodFromGermany sein "GuckstDuHier", Radinator noch eins, und ich halt die Geschichte mittm Datenmodell.
    Vermutlich hast du noch mehr im Internet gefunden - wird dich nur verrückt machen, aber egal.

    Entscheide dich und folge einem Ansatz nachm anneren bis nicht mehr geht oder Erfolg hat.
    Versuche nicht, allen gleichzeitig zu folgen.

    Welches der erfolgsversprechendste Ansatz ist, kann dir keiner sagen, bzw jeder (mich eingeschlossen) sagt "meiner", und nennt oder zeigt auch seine Gründe dafür.

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „ErfinderDesRades“ ()