02/10/2018, 11:30

[VB.NET] Hướng dẫn cuộn chuột load image vào Grid View từ URL bất đồng bộ (Async load image to gridview big data)

Bài viết hôm nay, mình sẽ hướng dẫn các bạn load hình ảnh từ đường dẫn trong thư mục ổ đĩa của bạn vào Column của GridView Devexpress. Hôm trước, mình cũng đã có viết bài hướng dẫn cho các bạn tải hình ảnh từ url vào gridview devexpress bằng VB.NET ...

Bài viết hôm nay, mình sẽ hướng dẫn các bạn load hình ảnh từ đường dẫn trong thư mục ổ đĩa của bạn vào Column của GridView Devexpress.

Hôm trước, mình cũng đã có viết bài hướng dẫn cho các bạn tải hình ảnh từ url vào gridview devexpress bằng VB.NET. Tuy nhiên, khi các bạn tải dữ liệu ít vào gridview thì không sao.

Nếu dữ liệu của các bạn là dữ liệu lớn (big data) khoảng 1000 record trở lên, thì khi các bạn kéo chuột trong gridview thì sẽ có cảm giác bị lag.

Vì vậy, bài toán đặt ra là chúng ta sẽ làm thế nào cho load dữ liệu không bị lag.

+ Load dữ liệu khi rê chuột, tức là kéo chuột đến đâu sẽ tải dữ liệu đến đó giống như các bạn load hình ảnh theo Ajax trên website vậy.

+ Mình sẽ tải hình ảnh bất đồng bộ để tối ưu hóa tối độ tải trên gridview.

+ Khi kéo chuột đến record nào trên gridview hiển thị sẽ ưu tiên cho hiển thị hình ảnh ở vị trí đó.

Dưới đây là giao diện của demo của chương trình:

Tải hình ảnh từ url thành hình ảnh vào gridcontrol devexpress

+ Viết hàm tạo dữ liệu demo sử dụng for để tạo datatable trong bài mình ví dụ là tạo ra 100.000 record.

 Private Function FillDataTable() As DataTable
        Dim _dataTable As New DataTable()
        Dim col As DataColumn

        Dim row As DataRow

        _dataTable.TableName = "SomeTable"

        col = New DataColumn()
        col.ColumnName = "Display Image"
        col.DataType = System.Type.[GetType]("System.String")

        _dataTable.Columns.Add(col)
        Dim j = 0
        For i = 1 To 100000
            j = j + 1
            row = _dataTable.NewRow()
            row("Display Image") = "photoimg" & j.ToString & ".jpg"

            _dataTable.Rows.Add(row)
            If (j = 13) Then
                j = 0
            End If

        Next
        Return _dataTable
    End Function

+ Tiếp tục cần tạo Module với tên sau: mld_LoadAsyncImage.vb

Source code

Imports System.IO
Imports System.Threading
Imports System.Threading.Tasks
Imports DevExpress.XtraGrid
Imports DevExpress.XtraGrid.Views.Base
Imports DevExpress.XtraGrid.Views.Grid
Module mld_LoadAsyncImage
    Private imageArr() As Image
    Private Const THUMBNAIL_IMG_HEIGHT As Integer = 80
    Private cancelTaskSourceGridView As CancellationTokenSource = New CancellationTokenSource()
    Private listCancelTaskSourceVisibleRow As List(Of CancellationTokenSource) = New List(Of CancellationTokenSource)
    Private myView As GridView


    Public Sub GridControl_DataSourceChanged(sender As Object, e As EventArgs)
        Dim gc = TryCast(sender, GridControl)
        myView = TryCast(gc.FocusedView, GridView)
        Dim dt = TryCast(gc.DataSource, DataTable)
        cancelTaskSourceGridView.Cancel()
        DisposeImageArr()

        If dt IsNot Nothing Then
            imageArr = New Image(dt.Rows.Count - 1) {}
            cancelTaskSourceGridView = New CancellationTokenSource
            Task.Factory.StartNew(Sub()
                                      AsyncLoadImage(cancelTaskSourceGridView)
                                  End Sub, cancelTaskSourceGridView)
        End If
    End Sub

    Private Sub AsyncLoadImage(cancelSource As CancellationTokenSource)
        Dim dt = TryCast(myView.GridControl.DataSource, DataTable)
        Dim imgArr = imageArr
        For r = 0 To dt.Rows.Count - 1 Step 1
            If Not cancelSource.Token.IsCancellationRequested Then
                'Load hình từ file
                Dim filename As String = dt.Rows(r)("Display Image")
                Dim img As Image
                If File.Exists(filename) Then
                    Using imgStream = New FileStream(filename, FileMode.Open, FileAccess.Read)
                        img = ResizeImage(Image.FromStream(imgStream), THUMBNAIL_IMG_HEIGHT)
                        imgStream.Close()
                    End Using
                Else
                    img = ResizeImage(My.Resources.logo, THUMBNAIL_IMG_HEIGHT, False)
                End If
                imgArr(r) = img

                'load hình từ ftp server
                'ftp://192.168.0.96/green_human.jpg
                'Dim request = WebRequest.Create("ftp://192.168.0.96/green_human.jpg")
                'Dim response = request.GetResponse()
                'Dim Stream = response.GetResponseStream()
                'Dim bmp = Bitmap.FromStream(Stream)
            Else
                'MsgBox("task cancel")
                Exit For
            End If
        Next
    End Sub

    Public Sub BandedGridView1_CustomDrawCell(sender As Object, e As RowCellCustomDrawEventArgs)
        If e.Column.FieldName = "Display Image" Then
            If imageArr IsNot Nothing Then
                Dim rowSourceIndex = myView.GetDataSourceRowIndex(e.RowHandle)
                If rowSourceIndex > -1 AndAlso imageArr(rowSourceIndex) IsNot Nothing Then
                    Dim sourceImg = imageArr(rowSourceIndex)
                    Dim rect = e.Bounds
                    Dim hs = Math.Min(rect.Width / sourceImg.Width, rect.Height / sourceImg.Height)
                    rect.Width = Convert.ToInt32(sourceImg.Width * hs)
                    rect.Height = Convert.ToInt32(sourceImg.Height * hs)

                    rect.X = rect.X + Convert.ToInt32((e.Bounds.Width - rect.Width) / 2)
                    rect.Y = rect.Y + Convert.ToInt32((e.Bounds.Height - rect.Height) / 2)

                    e.Graphics.DrawImage(sourceImg, rect)
                Else
                    Dim cancelSource = New CancellationTokenSource
                    SyncLock listCancelTaskSourceVisibleRow
                        listCancelTaskSourceVisibleRow.Add(cancelSource)
                        If listCancelTaskSourceVisibleRow.Count > 20 Then
                            listCancelTaskSourceVisibleRow(0).Cancel()
                            listCancelTaskSourceVisibleRow.RemoveAt(0)
                        End If
                    End SyncLock
                    Task.Factory.StartNew(
                        Sub()
                            AsyncLoadImageDrawCell(e, cancelSource)
                        End Sub, cancelSource)
                End If
            End If
            e.Handled = True

        End If
    End Sub

    Private Sub AsyncLoadImageDrawCell(e As RowCellCustomDrawEventArgs, cancelSource As CancellationTokenSource)
        If Not cancelSource.Token.IsCancellationRequested Then
            Dim rowSourceIndex = myView.GetDataSourceRowIndex(e.RowHandle)
            Dim img As Image
            If File.Exists(e.CellValue) Then
                img = ResizeImage(Image.FromFile(e.CellValue), THUMBNAIL_IMG_HEIGHT)
            Else
                img = ResizeImage(My.Resources.logo, THUMBNAIL_IMG_HEIGHT, False)
            End If
            imageArr(rowSourceIndex) = img
            'trigger CustomDrawCell reload khi đã có Img
            myView.GridControl.BeginInvoke(
            Sub()
                myView.SetRowCellValue(e.RowHandle, "Display Image", myView.GetRowCellValue(e.RowHandle, "Display Image"))
            End Sub)

        End If
    End Sub

    Private Sub DisposeImageArr()
        If imageArr IsNot Nothing Then
            For r = 0 To imageArr.Length - 1 Step 1
                If imageArr(r) IsNot Nothing Then
                    imageArr(r).Dispose()
                End If
            Next
            imageArr = Nothing
        End If
    End Sub

    Public Function ResizeImage(b As Image, awidth As Integer, height As Integer, Optional ByVal disposeOriginal As Boolean = True) As Image
        Dim result As Image = New Bitmap(awidth, height)
        Using g = Graphics.FromImage(result)
            g.DrawImage(b, 0, 0, awidth, height)
            If disposeOriginal Then
                b.Dispose()
                b = Nothing
            End If
        End Using
        Return result
    End Function

    Public Function ResizeImage(b As Image, height As Integer, Optional ByVal disposeOriginal As Boolean = True) As Image
        Dim awidth = Convert.ToInt32(height / b.Height * b.Width)
        Return ResizeImage(b, awidth, height, disposeOriginal)
    End Function
End Module

+ Tiếp theo mình sẽ viết sự kiện cho Datasource_Change

Private Sub SimpleButton1_Click(sender As Object, e As EventArgs) Handles SimpleButton1.Click
        AddHandler GridControl1.DataSourceChanged, AddressOf mld_LoadAsyncImage.GridControl_DataSourceChanged
        AddHandler GridView1.CustomDrawCell, AddressOf mld_LoadAsyncImage.BandedGridView1_CustomDrawCell
        Dim dt As DataTable = FillDataTable()
        GridControl1.DataSource = dt


    End Sub

VIDEO DEMO ỨNG DỤNG

DOWNLOAD SOURCE

HAVE FUN :)

Tags: bất đồng bộgridviewdatagridview
0