2
votes

Copier les lignes sélectionnées d'un DataGridView vers un autre, y compris la colonne d'image

J'essaie actuellement de copier les lignes sélectionnées d'un DataGridView vers un autre.
J'essaie de capturer la valeur de la CheckBox, où si elle est cochée, la ligne entière sera copiée dans un autre DataGridView.

Par exemple, comme ajouter au panier puis revoir le panier. J'ai fait référence au message suivant:

Cependant, cela ne semble pas aider.
J'ai essayé d'utiliser une boucle For comme celle ci-dessous, mais je ne sais pas trop comment procéder.

  Private Sub AppendColumnsToDGV2()
      Dim dt As New DataTable
      'dt.Columns.Add(CreateDGVCheckBoxCol())
      'dt.Columns.Add(CreateImageColumn())
      dt.Columns.Add(DataGridView1.Columns(3).HeaderText)
      dt.Columns.Add(DataGridView1.Columns(4).HeaderText)
      dt.Columns.Add(DataGridView1.Columns(5).HeaderText)
      dt.Columns.Add(DataGridView1.Columns(6).HeaderText)
      DataGridView2.DataSource = dt
End Sub

AppendColumnsToDGV2 :

Private Sub btnAdd_Click(sender As Object, e As EventArgs) Handles btnAdd.Click
    Dim dt As New DataTable()
    AppendColumnsToDGV2()
    For Each row As DataGridViewRow In DataGridView1.Rows
        If row.Cells("SelectColumn").Value = True Then
            Dim NewRow As DataRow
            For i As Integer = 0 To row.Cells.Count - 1
                NewRow(i) = row.Cells(i).Value
                DataGridView2.Rows.Add(NewRow)
            Next
        End If
    Next

Ce que je fais ici ne fonctionne pas et je n'ai aucune idée de la manière de procéder.
Toute aide serait appréciée, merci, gentiment.

Chaque fois que j'exécute ce code, j'obtiens l'erreur:

System.NullReferenceException: La référence d'objet n'est pas définie sur une instance d'un objet

Je ne sais pas comment y remédier.

Voici à quoi ressemble le DataGridView:

 WhatDGVLooksLike p>


7 commentaires

«DataGridView2.Rows.Add (NewRow)» doit suivre la première instruction Next. Avez-vous des erreurs ou ne voyez-vous tout simplement rien se passer?


En outre, newRow doit être construit par le DataTable. Par exemple. Dim newRow = dt.NewRow .


@JerryM J'obtiens l'erreur System.NullReferenceException: 'La référence d'objet n'est pas définie sur une instance d'un objet.' . J'utilise le datarow lorsqu'une valeur ne lui a apparemment pas été assignée.


Vous devez passer dt à la routine AppendColumnsToDGV2. Exemple `Private Sub AppendColumnsToDGV2 (dt As DataTable) '. Vous utilisez actuellement 2 tables de données distinctes.


Utilisez-vous toujours les classes générées à partir du JSON affichées dans votre question précédente ? Si tel est le cas, la solution est vraiment simple.


@Jimi je suis oui, c'est super de te voir xD


Alors, donnez-moi une minute (ou deux :), je vais écrire quelque chose.


3 Réponses :


0
votes

Vous utilisez actuellement 2 DataTables distincts. Vous essayez également d'ajouter une ligne à chaque fois que vous définissez une valeur de colonne. Cela pourrait fonctionner pour vous.

Private Sub btnAdd_Click(sender As Object, e As EventArgs) Handles btnAdd.Click
    Dim dt As New DataTable()
    AppendColumnsToDGV2(dt)
    For Each row As DataGridViewRow In DataGridView1.Rows
        If row.Cells("SelectColumn").Value = True Then
            Dim NewRow = dt.NewRow
            For i As Integer = 0 To row.Cells.Count - 1
                NewRow(i) = row.Cells(i).Value
            Next
            dt.Rows.Add(NewRow)
        End If
    Next
End Sub

Private Sub AppendColumnsToDGV2(dt As DataTable)
    'dt.Columns.Add(CreateDGVCheckBoxCol())
    'dt.Columns.Add(CreateImageColumn())
    dt.Columns.Add(DataGridView1.Columns(3).HeaderText)
    dt.Columns.Add(DataGridView1.Columns(4).HeaderText)
    dt.Columns.Add(DataGridView1.Columns(5).HeaderText)
    dt.Columns.Add(DataGridView1.Columns(6).HeaderText)
    DataGridView2.DataSource = dt
End Sub


4 commentaires

Merci pour ce @JerryM, mais maintenant j'obtiens l'erreur 'Les lignes ne peuvent pas être ajoutées par programme à la collection de lignes de DataGridView lorsque le contrôle est lié aux données.' alors je l'ai changé en datagridview2.datasource.rows.add (newrow) et maintenant j'obtiens l'erreur System.ArgumentException: 'Cette ligne appartient déjà à cette table.'


Oops. Oui. La ligne doit être ajoutée à la table de données. J'ai mis à jour la réponse.


Aussi, lorsque j'utilise dt.rows.add (newrow) , j'obtiens également cette ligne appartient déjà à cette table


J'ai oublié de suivre mes propres conseils sur le déplacement de l'appel de méthode Add ci-dessous, puis l'instruction Next .



2
votes

Si vous effectuez correctement la liaison de données, vos données sous-jacentes seront mises à jour lorsque la case à cocher est activée. Ensuite, vous pouvez simplement utiliser un certain LINQ. Vous devriez éviter d'itérer sur vos DataGridViewRows chaque fois que possible (ici c'est possible) car ils ne devraient pas contenir les données, mais plutôt les afficher.

Cet exemple simple fonctionne dans un formulaire avec deux DataGridViews et un Button avec des noms par défaut, dans vb .net.

Public Class Form1

    Private allProducts As List(Of Product)
    Private basketProducts As List(Of Product)

    Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
        allProducts = New List(Of Product) From {
                New Product() With {.Name = "Fairy Gel", .ID = 1},
                New Product() With {.Name = "Fairy Caps", .ID = 2},
                New Product() With {.Name = "Fairy Liquid", .ID = 3}}
        DataGridView1.DataSource = allProducts
    End Sub

    Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
        basketProducts = allProducts.Where(Function(p) p.Selected).ToList()
        DataGridView2.DataSource = basketProducts
    End Sub

    ' dummy class to emulate your data
    Private Class Product
        Public Property Selected As Boolean
        Public Property Name As String
        Public Property ID As Long
    End Class

End Class

entrez la description de l'image ici


3 commentaires

@SchmellerMeller cela ne nécessite aucune extension ni référence supplémentaire au-delà de celles par défaut dans une application WinForms. De quoi parlez-vous précisément?


@SchmellerMeller Dans votre question, vous essayiez d'opérer sur le formulaire plutôt que sur les données sous-jacentes, et je voulais juste souligner que vous devriez opérer sur les données. Voilà comment résoudre le problème. En fait, ma solution est la même que celle de Jimi (ce qui a l'air super car il était au courant de votre question précédente). En général, ma solution résout le problème général posé dans votre question. Jimi's résout votre problème spécifique car il est adapté à votre question précédente.


Ma faute. Je viens de réaliser que c'est fondamentalement exactement la même chose. Oops.



5
votes

Cette question est strictement liée à la précédente:
Afficher des images dans une colonne DataGridView en utilisant des objets JSON comme DataSource

Vous utilisez une sous-classe ( Résultat ) de RootObject pour remplir le premier DataGridView.

Modifiez la classe Result comme suit:

  • Ajoutez une nouvelle propriété, Selected As Boolean , décorée d'un attribut .
  • Ajoutez une nouvelle sous-classe, appelée SelectionResult ici, une sélection de propriétés de la classe Result que vous pensez être nécessaires dans le deuxième DataGridView pour afficher les produits sélectionnés.
  • Ajoutez une méthode de copie à la classe Result qui renvoie une sous-section d'elle-même sous forme de SelectionResult objet.

Private Sub btnAdd_Click(sender As Object, e As EventArgs) Handles btnAdd.Click
    SelectedProducts.AddRange(CurrentProducts.
                       Where(Function(p) p.Selected = True AndAlso
                             (Not SelectedProducts.Any(Function(sp) sp.ID = p.id))).
                       Select(Function(p) p.GetSelectionResult()).ToArray())
    ResetCart()
End Sub

Private Sub btnRemoveSelection_Click(sender As Object, e As EventArgs) Handles btnRemoveSelection.Click
    If DataGridView2.SelectedRows.Count = 0 Then Return

    Dim itemsRemoved As Boolean = False
    Dim selectedItems() As Integer = DataGridView2.SelectedRows.
                                     OfType(Of DataGridViewRow)().
                                     Select(Function(r) CInt(r.Cells("ID").Value)).ToArray()
    For Each ID As Integer In selectedItems
        Dim currentIndex As Integer = SelectedProducts.FindIndex(Function(p) p.ID = ID)
        If currentIndex >= 0 Then
            SelectedProducts.RemoveAt(currentIndex)
            itemsRemoved = True
        End If
    Next
    If itemsRemoved Then
        ResetCart()
    End If
End Sub

Private Sub ResetCart()
    DataGridView2.DataSource = Nothing
    DataGridView2.DataSource = SelectedProducts
    DataGridView2.Columns(0).Visible = False
    DataGridView2.AutoResizeRows()
End Sub
  • Ajoutez deux List (Of Class) en tant que champs dans le formulaire. La classe principale, dans la question précédente, s'appelait ProductsQuery , donc je réutilise les noms déjà définis ici:

CurrentProducts.AddRange(JsonPost.uk.ghs.Products.Results)
  • Dans la méthode qui remplit le premier DataGridView, initialisez la liste CurrentProducts :

    CurrentProducts = New List(Of ProductsQuery.Result)()
    
  • Une fois le JSON désérialisé, remplissez la liste avec les résultats JSON:

    Private CurrentProducts As List(Of ProductsQuery.Result) = New List(Of ProductsQuery.Result)()
    Private SelectedProducts As List(Of ProductsQuery.SelectionResult) = New List(Of ProductsQuery.SelectionResult)()
    

Dans le gestionnaire d'événements du Button qui ajoute les produits sélectionnés au deuxième DataGridView, insérez ce code:

Modifier :
La liste SelectedProducts préserve les éléments sélectionnés dans le premier DataGridView: seuls les éléments qui ne sont pas déjà dans la liste CurrentProducts sont ajoutés à la sélection.

Le bouton btnRemoveSelection supprime les éléments sélectionnés dans le deuxième DataGridView de la liste SelectedProducts . La sélection de la ligne DataGridView est quelque peu lourde, vous pouvez donc souhaiter ajouter une colonne CheckBox pour faciliter la sélection des éléments à supprimer.

Public Class Result
    <JsonIgnore>
    Public Property Selected As Boolean

    '(...)

    Public Function GetSelectionResult() As SelectionResult
        Return New SelectionResult With {
            .ID = Me.id,
            .Image = Me.Image,
            .Name = Me.Name,
            .ProductDescription = Me.ProductDescription,
            .Department = Me.Department,
            .Price = Me.Price,
            .Unitprice = Me.Unitprice
        }
    End Function
End Class

Public Class SelectionResult
    Public Property ID As Integer
    Public Property Image As Bitmap
    Public Property Name As String
    Public Property ProductDescription As String
    Public Property Department As String
    Public Property Price As Decimal
    Public Property Unitprice As Decimal
End Class

Ceci remplit la List (Of SelectedProducs) avec les éléments sélectionnés du premier DataGridView et définit la DataSource de le deuxième DataGridView de cette liste.

Notez que la première colonne de DataGridView est définie sur Visible = False , car cette colonne correspond au ID propriété de l'élément sélectionné

Le GetSelectionResult() de la classe Result renvoie le les valeurs de propriétés définies dans la classe SelectionResult . Vous pouvez bien sûr redéfinir cette classe pour qu'elle contienne toutes les propriétés qui vous conviennent.


Voici le résultat de ces modifications:

 Résultats DataGridView JSON


6 commentaires

Merci pour cela @Jimi savez-vous comment j'obtiendrais réellement les données dans le deuxième DGV pour y rester. J'ai supprimé les lignes DataGridView2.DataSource = Nothing et SelectedProducts.Clear () cependant, il n'y a aucune différence. Chaque fois que je fais une autre recherche, ils sont partis.


Voulez-vous dire que vous voulez que le deuxième DGV conserve les résultats des différentes sélections effectuées à différents moments? En tant que dépôt de choix ou panier de magasin?


Ouais exactement, comme un panier, où tous les résultats sélectionnés restent pour que l'utilisateur final puisse les examiner quand il le souhaite.


Très bien, cela a du sens. Je ferai une retouche dès que j'aurai un moment.


Code mis à jour. J'ai ajouté un deuxième bouton qui permet de supprimer des éléments de la liste de sélection. Lisez les notes. Voyez si cela vous convient.


Exactement ce que j'avais en tête, merci beaucoup @Jimi.