Toon posts:

[VB.NET] Treeview snel kunnen vullen

Pagina: 1
Acties:

Verwijderd

Topicstarter
Ik heb de volgende MSSQL tabel:
code:
1
2
3
4
5
tbl_folders:
  id           INT(11)   AutoIncrement KEY
  parentid     INT(11)
  name         VARCHAR(255)
  isfolder     INT(11)

Hier heb ik de volgende vb.net code bij:
Visual Basic:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
    Private Sub frmMain_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load

        pr_sql.OpenConnection()
        Dim folders As ADODB.Recordset
        folders = pr_sql.Execute1("SELECT id, parent, name FROM folders WHERE isfolder='-1' and parent is null order by name")
        folders.MoveFirst()
        Do While Not (folders.EOF)
            Dim index As String = folders.Fields(0).Value
            TreeView.Nodes.Add(index, folders.Fields(2).Value, 0)
            If nodeHasChild(index) Then
                TreeView.Nodes(index).Nodes.Add("Tijdelijk")
            End If
            folders.MoveNext()
            Application.DoEvents()
        Loop
    End Sub

    Function nodeHasChild(ByVal id As Integer) As Boolean
        Dim rs As ADODB.Recordset
        rs = pr_sql.Execute1("SELECT id, parent, name FROM folders WHERE isfolder='-1' and parent=" & id)
        If Not rs.EOF Then
            Return True
        End If
        Return False
    End Function

    Private Sub TreeView_BeforeExpand(ByVal sender As Object, ByVal e As System.Windows.Forms.TreeViewCancelEventArgs) Handles TreeView.BeforeExpand
        If Not e.Node.Tag Then
            TreeView.BeginUpdate()
            e.Node.Tag = True
            e.Node.Nodes.Clear()
            Dim id As Integer = e.Node.Name
            Dim rs As ADODB.Recordset
            rs = pr_sql.Execute1("SELECT id, parent, name FROM folders WHERE isfolder='-1' and parent=" & id & " order by name")
            progress.Maximum = rs.RecordCount
            progress.Minimum = 0
            progress.Value = 0
            rs.MoveFirst()
            Do While Not (rs.EOF)
                progress.Value += 1
                Dim index As String = rs.Fields(0).Value
                e.Node.Nodes.Add(index, rs.Fields(2).Value, 0)
                If nodeHasChild(index) Then
                    e.Node.Nodes(index).Nodes.Add("Tijdelijk")
                End If
                rs.MoveNext()
            Loop
            progress.Value = 0
            TreeView.EndUpdate()
        End If
    End Sub


Het probleem

De treelist word dus bij het laden van de form gevult (alleen de root)
Als er een node word uitgeklapt (expanded) kijkt hij of de tag van de desbetreffende node true is.
Als dit het geval is betekend het dat deze node al gevult is.

Zo niet dan moet deze node gevult worden met de data uit de database.

Het vullen van deze data gaat echter zeer traag (16 nodes per seconde)
aangezien er in sommige mappen een paar honderd submappen zitten moet er ontzettend lang gewacht worden. Het ligt puur aan het vullen van de treelist, de data uit de database halen gaat lekker snel (tenminste de query uitvoeren)

Weer iemand een oplossing op de treelist anders te vullen of tenminste de snelheid te verbeteren.

  • RobIII
  • Registratie: December 2001
  • Niet online

RobIII

Admin Devschuur®

^ Romeinse Ⅲ ja!

(overleden)
Waarom gebruik je een Application.DoEvents?
Ik gebruik meestal zowieso een treeview.beginupdate en treeview.endupdate om het redrawen te voorkomen. Wellicht dat dat de operatie wat versnelt? Dat gebruik jij ook :P
Weet je zeker dat het aan 't "vullen" van de treeview ligt? Maak eens een test-case waarbij je (zonder ADODB te gebruiken) gewoon je treeview vult in een loopje met 1000 random strings ofzo?
Heb je wellicht het sorting property op iets anders dan "none" staan?

[ Voor 44% gewijzigd door RobIII op 30-08-2006 11:55 ]

There are only two hard problems in distributed systems: 2. Exactly-once delivery 1. Guaranteed order of messages 2. Exactly-once delivery.

Je eigen tweaker.me redirect

Over mij


Verwijderd

Topicstarter
RobIII schreef op woensdag 30 augustus 2006 @ 11:52:
Waarom gebruik je een Application.DoEvents?
Ik gebruik meestal zowieso een treeview.beginupdate en treeview.endupdate om het redrawen te voorkomen. Wellicht dat dat de operatie wat versnelt? Dat gebruik jij ook :P
Weet je zeker dat het aan 't "vullen" van de treeview ligt? Maak eens een test-case waarbij je (zonder ADODB te gebruiken) gewoon je treeview vult in een loopje met 1000 random strings ofzo?
Application.DoEvents heb ik gebruikt om te kijken of het iets zo uitmaken (aangezien tijdens het vullen van een node de hele applicatie blijft hangen).

Application.DoEvents lost dat probleem wel op, maar het vullen blijft traag

Die testcase ga ik nu eens maken...

[ Voor 55% gewijzigd door Verwijderd op 30-08-2006 11:56 ]


  • RobIII
  • Registratie: December 2001
  • Niet online

RobIII

Admin Devschuur®

^ Romeinse Ⅲ ja!

(overleden)
Verwijderd schreef op woensdag 30 augustus 2006 @ 11:54:
[...]


Application.DoEvents heb ik gebruikt om te kijken of het iets zo uitmaken (aangezien tijdens het vullen van een node de hele applicatie blijft hangen).

Application.DoEvents lost dat probleem wel op, maar het vullen blijft traag

Die testcase ga ik nu eens maken...
Heb je gekeken naar het sorting property? Laat je je treeview alles sorten? Want het komt (zo te zien) al gesort uit je DB dus dat zou onzin zijn (en voor onnodige vertragingen zorgen).
Misschien duurt het opvragen van de .recordcount wel zo lang? En een testcase (zoals ik aangaf) al eens geprobeerd?

Ik zou die DoEvents er uit halen. Hoewel je GUI responsiever lijkt vertraagt het alleen maar. Lock liever je window (of bepaalde controls) tijdens updates.

There are only two hard problems in distributed systems: 2. Exactly-once delivery 1. Guaranteed order of messages 2. Exactly-once delivery.

Je eigen tweaker.me redirect

Over mij


Verwijderd

Topicstarter
Ik heb even gekeken of ik de "sorting property" van de treelist kan vinden, maar als ik het goed begreep moet je die sorting property zelf instellen (als je dat niet doet sorteerd hij ook niet)

Aangezien ik dit niet gedaan heb staat de sorting property ook niet aan.

Verder lijkt het probleem te liggen aan:

Visual Basic:
1
2
3
4
5
6
7
8
        Do While Not (folders.EOF)
            'Dim index As String = folders.Fields(0).Value
            'TreeView1.Nodes.Add(index, folders.Fields(2).Value, 0)
            'If nodeHasChild(index) Then
                'TreeView1.Nodes(index).Nodes.Add("Tijdelijk")
            'End If
            folders.MoveNext()
        Loop


Het doorlopen van de recordset dus, niet het invoegen van de nodes 8)7
Iemand enig idee hoe dit sneller zou kunnen?

BTW: De testcase gaat perfect (dus zonder ADODB) het vullen van 1000 nodes met 1000 nodes < 1 seconde

[ Voor 6% gewijzigd door Verwijderd op 30-08-2006 12:16 ]


  • RobIII
  • Registratie: December 2001
  • Niet online

RobIII

Admin Devschuur®

^ Romeinse Ⅲ ja!

(overleden)
Het lijkt mij te zitten in nodeHasChild (dat lijkt me een eigen functie van je?). Zo ja, post daar de code eens van?
Goedemorgen :D Code staat er al :P
Vind je het gek? Je gaat voor iedere node wéér een query uitvoeren (nodeHasChild) :D Tjees, dat ik dat niet gezien heb...
Je kunt beter, als je toch al de recordset ophaalt met de nodes (in BeforeExpand) meteen een extra kolom met "haschild"/"childcount" toevoegen d.m.v. een left join / count ofzo.

Iets als dit dus:
SQL:
1
2
3
4
5
6
SELECT p.id, p.parent, p.name, count(c.id) as childcount
FROM folders p
LEFT JOIN folders c on p.id = c.parent
WHERE isfolder='-1' and p.parent = <$id>
GROUP BY p.id, p.parent, p.name
ORDER BY p.name


Overigens, waarom is "isfolder" gequote? Is dat geen int?

[ Voor 88% gewijzigd door RobIII op 30-08-2006 12:42 ]

There are only two hard problems in distributed systems: 2. Exactly-once delivery 1. Guaranteed order of messages 2. Exactly-once delivery.

Je eigen tweaker.me redirect

Over mij


Verwijderd

Topicstarter
RobIII schreef op woensdag 30 augustus 2006 @ 12:17:
Het lijkt mij te zitten in nodeHasChild (dat lijkt me een eigen functie van je?). Zo ja, post daar de code eens van?
Goedemorgen :D Code staat er al :P
Vind je het gek? Je gaat voor iedere node wéér een query uitvoeren (nodeHasChild) :D Tjees, dat ik dat niet gezien heb...
Je kunt beter, als je toch al de recordset ophaalt met de nodes (in BeforeExpand) meteen een extra kolom met "haschild"/"childcount" toevoegen d.m.v. een left join / count ofzo.
Ik weet echt niet hoe ik zo'n query zou moeten uitvoeren:
[code=sql]

SELECT folders.id, folders.parent, folders.name, folders1.id AS Expr1
FROM folders LEFT OUTER JOIN
folders folders1 ON folders.id = folders1.parent AND folders1.isfolder = '-1'
WHERE (folders.isfolder = '-1') AND (folders1.isfolder = '-1')
GROUP BY folders.id, folders.parent, folders.name, folders1.id
[/code]


Had de vorige post nog niet gelezen

Verwijderd

Topicstarter
Als ik nu de query uitvoer krijg ik deze foutmelding:

rs.Bookmark {"De huidige recordset ondersteunt geen bladwijzers. Dit kan een beperking zijn van de voorziening of van het geselecteerde cursortype."}

de query werkt overigens perfect in de SQL Enterprice Manager

  • RobIII
  • Registratie: December 2001
  • Niet online

RobIII

Admin Devschuur®

^ Romeinse Ⅲ ja!

(overleden)
Euh... hoe ziet je code er nu uit dan? (Alleen relevante code posten a.u.b.) En waar gaat het dan fout?
offtopic:
Enterprise manager ;)

[ Voor 34% gewijzigd door RobIII op 30-08-2006 13:18 ]

There are only two hard problems in distributed systems: 2. Exactly-once delivery 1. Guaranteed order of messages 2. Exactly-once delivery.

Je eigen tweaker.me redirect

Over mij


Verwijderd

Topicstarter
De volgende code heb ik nu:


Visual Basic:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
    Private pr_sql As New clsQuery(pr_settings)
    

   Private Sub TreeView1_BeforeExpand(ByVal sender As Object, ByVal e As System.Windows.Forms.TreeViewCancelEventArgs) Handles TreeView1.BeforeExpand
    pr_sql.OpenConnection()
    If Not e.Node.Tag = True Then
            TreeView1.BeginUpdate()
            e.Node.Tag = True
            e.Node.Nodes.Clear()
            Dim id As Integer = e.Node.Name
            Dim rs As ADODB.Recordset
            rs = pr_sql.Execute1("SELECT p.id, p.parent, p.name, COUNT(c.id) AS childcount FROM folders p LEFT OUTER JOIN folders c ON p.id = c.parent AND c.isfolder = '-1' WHERE (p.isfolder = '-1') AND (p.parent = " & id & ") GROUP BY p.id, p.parent, p.name ORDER BY p.name")
            progress.Maximum = rs.RecordCount
            progress.Minimum = 0
            progress.Value = 0
            rs.MoveFirst()
            Do While Not (rs.EOF)
                progress.Value += 1
                Dim index As String = rs.Fields(0).Value
                e.Node.Nodes.Add(index, rs.Fields(2).Value, 0)
                If rs.Fields(3).Value > 0 Then
                    e.Node.Nodes(index).Nodes.Add("Tijdelijk")
                End If
                rs.MoveNext()
            Loop
            progress.Value = 0
            TreeView1.EndUpdate()
        End If
    End Sub


CLASS: clsQuery
Visual Basic:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
    Public Sub OpenConnection()
        Try
            If pr_connected = True Then MsgBox("ERROR: Allready connected") : Exit Sub
            pr_connection.ConnectionString = "Provider=SQLOLEDB.1; Data Source = BMTServer; User ID = " & pr_settings.sql_username & "; Initial Catalog =" & pr_settings.sql_database & "; Password = " & pr_settings.sql_password & ";"
            pr_connection.Open()
            pr_connected = True
        Catch e As Exception
            RaiseEvent err(e.Message)
            pr_connected = False
        End Try
    End Sub
    Public Function Execute1(ByVal query As String) As ADODB.Recordset
        Dim pr_result As New ADODB.Recordset
        pr_result.Open(query, pr_connection, ADODB.CursorTypeEnum.adOpenDynamic, ADODB.LockTypeEnum.adLockOptimistic)
        Return pr_result
    End Function

  • whoami
  • Registratie: December 2000
  • Laatst online: 20:35
Waarom maak je eigenlijk gebruik van de oude ADO classes, ipv de ADO.NET classes ?
Je kan je query uitvoeren, en de results in een datatable plaatsen (gebruik maken van een data-adapter), ofwel maak je gebruik van een DataReader (fast forward only)

https://fgheysels.github.io/


Verwijderd

Topicstarter
Dank jullie allebei :)

De nieuwe query werkt perfect en de ADO.NET class werkt echt stukken sneller _/-\o_

  • Woy
  • Registratie: April 2000
  • Niet online

Woy

Moderator Devschuur®
Ik zou als ik jou was overigens ook gebruik maken van geparametriseerde querys. Query's die met string concatenation gemaakt zijn is ook niet echt handig.

“Build a man a fire, and he'll be warm for a day. Set a man on fire, and he'll be warm for the rest of his life.”


  • RobIII
  • Registratie: December 2001
  • Niet online

RobIII

Admin Devschuur®

^ Romeinse Ⅲ ja!

(overleden)
rwb schreef op woensdag 30 augustus 2006 @ 15:13:
Ik zou als ik jou was overigens ook gebruik maken van geparametriseerde querys. Query's die met string concatenation gemaakt zijn is ook niet echt handig.
Tja, dat kan in vele topics wel geroepen worden. Maar in dit geval valt het allemaal nogal mee als je zelf de treeview vult vanuit code en de gebruiker er niet in kan rommelen (er zijn geen querystrings en dat soort flauwekul waar gebruikers in kunnen rommelen). Hoewel het altijd een goed idee is om parametrized query's te gebruiken is het hier niet vreselijk belangrijk en IMHO zelfs offtopic.

There are only two hard problems in distributed systems: 2. Exactly-once delivery 1. Guaranteed order of messages 2. Exactly-once delivery.

Je eigen tweaker.me redirect

Over mij


  • whoami
  • Registratie: December 2000
  • Laatst online: 20:35
Nou, het is niet helemaal offtopic, aangezien een parametrized query kan gecached worden (het exec plan dan althans).

https://fgheysels.github.io/


  • RobIII
  • Registratie: December 2001
  • Niet online

RobIII

Admin Devschuur®

^ Romeinse Ⅲ ja!

(overleden)
True, maar of dat nou het verschil maakt in de "vertraging" waar TS mee kampte vraag ik me af ;)

Overigens raad ik het wel degelijk aan; je zou die classes natuurlijk altijd in de toekomst nog kunnen her-gebruiken op plekken waar het gevaar voor SQL-injection groter is ;)

[ Voor 46% gewijzigd door RobIII op 30-08-2006 15:41 ]

There are only two hard problems in distributed systems: 2. Exactly-once delivery 1. Guaranteed order of messages 2. Exactly-once delivery.

Je eigen tweaker.me redirect

Over mij


  • Woy
  • Registratie: April 2000
  • Niet online

Woy

Moderator Devschuur®
Ja idd. hier kan het niet echt kwaad aangezien je zeker weet dat je een int concat. De opmerking was ook niet bedoeld voor SQL injection specifiek.

“Build a man a fire, and he'll be warm for a day. Set a man on fire, and he'll be warm for the rest of his life.”

Pagina: 1