1

How would I go about formatting a DataGridView column to show an image from a URL instead of the URL itself as string.

Example of what the DataGridView currently looks like:

Example of datagridview

I currently have the following code:

 DataGridView1.DataSource = Tesco.uk.ghs.products.results  

Here I am binding the JSON I have to datagridview1.
This works (even though as you can see from the screenshot it seems to miss out the Description Column for some reason).

I have also tried using the class DataGridViewImageColumn however I cannot get it to actually "convert" the column to be an image column.

JSON:

Public Class Totals
   Public Property all As Integer
   Public Property [new] As Integer
   Public Property offer As Integer
End Class

Public Class Result
   Public Property image As String
   Public Property superDepartment As String
   Public Property tpnb As Integer
   Public Property ContentsMeasureType As String
   Public Property name As String
   Public Property UnitOfSale As Integer
   Public Property description As String()
   Public Property AverageSellingUnitWeight As Double
   Public Property UnitQuantity As String
   Public Property id As Integer
   Public Property ContentsQuantity As Double
   Public Property department As String
   Public Property price As Double
   Public Property unitprice As Double
End Class

Public Class Products
   Public Property input_query As String
   Public Property output_query As String
   Public Property queryPhase As String
   Public Property totals As Totals
   Public Property config As String
   Public Property results As Result()
   Public Property suggestions As Object()
End Class

Public Class Ghs
   Public Property products As Products
End Class

Public Class Uk
   Public Property ghs As Ghs
End Class

Public Class JSON
   Public Property uk As Uk
End Class
Jimi
  • 29,621
  • 8
  • 43
  • 61
  • Your property is a string, a string isnt a picture... – Trevor Jan 15 '19 at 18:20
  • Creating property without setter and getter are as useful as using variables. – Software Dev Jan 15 '19 at 18:22
  • @zackraiyan Hi Zack, Huh? Fields shouldn't be public. Remember encapsulation, one of the 3 pillars of OOP. :-) – Mary Jan 15 '19 at 19:18
  • https://social.msdn.microsoft.com/Forums/windows/en-US/601ede87-d4d8-4916-8093-9c0f4fe0e606/how-to-show-image-in-a-datagridview-cell-called-from-a-url-stored-in-an-sql-database?forum=winformsdatacontrols – Mary Jan 15 '19 at 19:25

2 Answers2

1

Note: This question has a follow-up here:
Copy selected checkbox rows from DGV to DGV2 including image column


You need to download the images after the JSON has been deserialized into your class object. You can and add a new public property of Type Bitmap to the internal Result class, which will be used to present the Image referenced by the URI found in the JSON.

To aquire the Images, you need a method that takes the resource URI and downloads it from the remote machine. I added a public method to the Products class, which references all the objects that contain a URI reference:
after the JSON has been parsed successfully, the Results property will reference all the Result classes. Each Result class will reference the Bitmap URI in the ProductImage Property.

Using a WebClient class, we can use these references to download the Images and add them to a Property of Type Bitmap.
We also need to instruct the JSON parser to ignore this Property, since it's not part of the JSON object.
This can be done adding a <JsonIgnore> attribute to the property.

Rename the RootObject (named JSON in your class definition) to Root:

Also, all the classes you're showing here are added to a Parent class named ProductsQuery, used as a container for all the class objects.

Public Class Root
   Public Property uk As Uk
End Class

Deserialize the JSON with JsonConvert.DeserializeObject:

Dim JSONObject As String = File.ReadAllText("[Source Txt]")
Dim JsonPost As ProductsQuery.Root = JsonConvert.DeserializeObject(Of ProductsQuery.Root)(JSONObject)

Use the newly added public method to download the images:

JsonPost.uk.ghs.Products.LoadImages()

Prepare the DataGridViewImageColumn that will show the Images:
Edit: also insert a DataGridViewCheckBoxColumn.

Dim DGVCheckBoxCol As DataGridViewCheckBoxColumn = New DataGridViewCheckBoxColumn(False) With {
    .AutoSizeMode = DataGridViewAutoSizeColumnMode.DisplayedCellsExceptHeader,
    .DisplayIndex = 0,
    .HeaderText = "",
    .Name = "Select"
}

Dim DGVImageCol As DataGridViewImageColumn = New DataGridViewImageColumn(False) With {
    .AutoSizeMode = DataGridViewAutoSizeColumnMode.DisplayedCellsExceptHeader,
    .DataPropertyName = "Image",
    .DisplayIndex = 1,
    .FillWeight = 1,
    .HeaderText = "Image",
    .ImageLayout = DataGridViewImageCellLayout.Normal,
    .Name = "Image",
    .ValuesAreIcons = False
}

Then, set the DataGridView DataSource and hide the Column that contains the Image URI, which is not really useful here. Also, call the AutoResizeRows method to set the size of the rows to better present the Image:

DataGridView1.DataSource = Nothing 
DataGridView1.Columns.Clear()

DataGridView1.Columns.Insert(0, DGVCheckBoxCol)
DataGridView1.Columns.Insert(1, DGVImageCol)
DataGridView1.DataSource = JsonPost.uk.ghs.Products.Results
DataGridView1.Columns(2).Visible = False
DataGridView1.AutoResizeRows()

Result:

JSON on DataGridView

The modified classes:

Note:
The description property is an array of strings, which the DGV refuses to show as it is. I've called it RawDescription and I added a new property (Description) of type String - with an <JsonIgnore> attribute - in the Result class, that will contain the flattened string.

Imports System.IO
Imports System.Net
Imports Newtonsoft.Json

Public Class ProductsQuery

    Public Class Root
        Public Property uk As Uk
    End Class

    Public Class Uk
        Public Property ghs As Ghs
    End Class

    Public Class Ghs
        Public Property products As Products
    End Class

    Public Class Products
        Public Property input_query As String
        Public Property output_query As String
        Public Property filters As Filters
        Public Property queryPhase As String
        Public Property totals As Totals
        Public Property config As String

        <JsonProperty("results")>
        Public Property Results As Result()
        Public Property suggestions As Object()

        Public Sub LoadImages()
            Using client As WebClient = New WebClient()
                For Each result As Result In Results
                    Dim bitmapBytes = client.DownloadData(result.ProductImage)
                    Using ms As MemoryStream = New MemoryStream(bitmapBytes)
                        ms.Position = 0
                        result.Image = CType(Image.FromStream(ms).Clone(), Bitmap)
                    End Using
                    result.ProductDescription = result.RawDescription(0)
                Next
            End Using
        End Sub
    End Class

    Public Class Filters
    End Class

    Public Class Totals
        Public Property all As Integer
        <JsonProperty("new")>
        Public Property NewProducts As Integer
        Public Property offer As Integer
    End Class

    Public Class Result

        <JsonIgnore>
        Public Property Image As Bitmap

        <JsonProperty("image")>
        Public Property ProductImage As String

        <JsonProperty("superDepartment")>
        Public Property SuperDepartment As String
        Public Property tpnb As Integer
        Public Property ContentsMeasureType As String

        <JsonProperty("name")>
        Public Property Name As String
        Public Property UnitOfSale As Integer

        <JsonIgnore>
        Public Property Description As String

        <JsonProperty("description")>
        Public Property RawDescription As String()
        Public Property AverageSellingUnitWeight As Double
        Public Property UnitQuantity As String
        Public Property id As Integer
        Public Property ContentsQuantity As Double

        <JsonProperty("department")>
        Public Property Department As String

        <JsonProperty("price")>
        Public Property Price As Double

        <JsonProperty("unitprice")>
        Public Property Unitprice As Double
    End Class
End Class
Jimi
  • 29,621
  • 8
  • 43
  • 61
  • Thank you so much for this, I wish SO had a way of crediting others with money. Thank you, sir <3 – SchmellerMeller Jan 15 '19 at 19:58
  • I've just noticed in your edited code you actually add the column using `DataGridView1.Columns.Insert(0, DGVImageCol)` I currently do not have this in my code, yet the column is still there? I'm trying to add a checkbox column using the same method however it's not appearing for some reason. If I use `DGV1.Columns.Insert` then I have the column appearing too many times. Would you know how to go about this? – SchmellerMeller Jan 18 '19 at 17:57
  • I made a new Column this way because I can then set some properties that can be useful: `ImageLayout` (you could specify to Zoom the image) and `FillWeight` (better speed in loading the data), for example. The Column `DataPropertyName` is set to the name of the corresponding property of the `Result` class, thus the DataGridView knows it has use this Column to show the related data (and doesn't auto-generate another Column). About your `CheckBoxColumn`, it depends on how/when/where you are creating/inserting it. I hope not in a loop ;). I'll update the code to show how you can do it. – Jimi Jan 18 '19 at 18:06
  • I've found my issue, it's specifically with this line `DataGridView1.DataSource = JsonPost.uk.ghs.Products.Results` whenever this gets executed, the lines of code after it are also executed again. Which I guess explains why I get multiple columns appearing. – SchmellerMeller Jan 18 '19 at 18:18
  • Thank you for this @Jimi still the problem persists though, when I perform multiple searches, another `CheckBoxCol` is added, which makes the `Image` column change visibility. There's probably a much simpler way of doing this which I just cannot see :( – SchmellerMeller Jan 18 '19 at 18:31
  • But, if you make another search, you need to clear the Columns collection and the `DataGridView.DataSource` before specifying a new one. Set it first to `nothing` then to the new one: `DataGridView1.DataSource = Nothing DataGridView1.Columns.Clear() (...same code...) ` – Jimi Jan 18 '19 at 18:35
0

You have to first convert the column where you want the images as an Image column.

You can do it in the properties of that datagridview OR programatically when you create the datagridview and you add your columns to it :

 Dim imgColumn as new DataGridViewImageColumn
 DataGridView1.Columns.Add(imgColumn)

Once you've added the column/changed the type of the column you could then convert each cell in that column into a bitmap image:

It could look roughly like this (replace colindex with the index number of your image column for example your imgColumn we declared above):

 Dim currRow as Int = 0
 For Each row As DataGridViewRow In DataGridView1.Rows

            Dim img = new Bitmap(DataGridView1.Item(colindex, currRow).value);

            DataGridView1.Item(colindex, currRow).value = img

            currRow = currRow + 1


  Next

Reference here

Malcolm Salvador
  • 1,476
  • 2
  • 21
  • 40
  • Thank you for this, I'm trying to syntax it in my code however I'm having trouble doing so. In particular with the line `dim img = new Bitmap(@datagridview1.Item(colindex, currRow).value` I'm assuming this is C#, would you know how I would syntax this in VB.NET? – SchmellerMeller Jan 15 '19 at 19:13
  • uh, just remove the @ symbol – Malcolm Salvador Jan 15 '19 at 20:10