简体   繁体   中英

Filter a DataGridView without losing unbound changes

Im developing a vb.net application and I have a dgv which displays a list of employees. The dgv has an unbound checkbox column which is used in selecting employees for a required action. I use rowFilter to filter the list by department or location or whatever the user chooses.

If the user filters using Department 1 and checks the checkboxes next to the employees he wants, to select them and then filters to Department 2, to select a few more employees, when we filter back to Department 1, the checked employees aren't checked any more.

How can I make it such that once an employee is checked, you can filter with other parameters with losing the earlier selections. Im thinking of saving the checked state of each employee to the database before the next filter but I feel is possibly a quicker easier way. Is there?

This is the code I'm using to fill the dgv

 sql = "SELECT employee_paysetup.EmployeeNumber AS EmployeeNo, employee_paysetup.FName, employee_paysetup.MName, " _
                & "employee_paysetup.LName, (case when employee_paysetup.MName = ' ' then concat(employee_paysetup.FName,' ' , employee_paysetup.LName) else " _
                & "concat(employee_paysetup.FName,' ' , employee_paysetup.MName,' ' , employee_paysetup.LName) end) " _
                & "AS FullName, costcenters.CostCenterName AS cosName, departments.DepartmentName AS depName, " _
                & "positions.PositionName AS posName, locations.LocationName AS locName, CostCenter, Department, Position, EmployeeLocation, PayGroup " _
                & "FROM ((((((employee_paysetup LEFT JOIN employees ON employee_paysetup.EmployeeNumber = employees.EmployeeNumber) " _
                & "LEFT JOIN costcenters ON employees.CostCenter = costcenters.CostCenterID) " _
                & "LEFT JOIN departments ON employees.Department = departments.DepartmentID) " _
                & "LEFT JOIN positions ON employees.Position = positions.PositionID) " _
                & "LEFT JOIN locations ON employees.EmployeeLocation = locations.LocationID) " _
                & "LEFT JOIN paygroups ON employee_paysetup.PayGroup = paygroups.ID) " & filterValue
        cmd = New MySqlCommand(sql, conn)
        cmd.Parameters.AddWithValue("@Status", "ACTIVE")
        da.SelectCommand = cmd
        dt.Clear()
        da.Fill(dt)
        'sort
        dt.DefaultView.Sort = "FullName ASC"
        dgvEmployees.DataSource = dt.DefaultView

And I filter using

dt.DefaultView.RowFilter  = "myFilter"

Lets say you have Employee:

Public Class Employee
    Public Property EmpName As String
    Public Property EmpNumber As Integer
End Class

And your winform may have a method to get a list of Employee:

Public Function GetEmployees() As List(Of Employee)
        'obviously, actually fill a list...
        Return New List(Of Employee)()
End Function

Often the "View" (in your case, the winform), has different needs than the back-end data. So you make an EmployeeViewModel :

Public Class EmployeeVM
    Public Property EmpName As String
    Public Property EmpNumber As Integer
    Public Property EmpIsSelected As Boolean
End Class

So you edit GetEmployees to get and transform the employee list into an EmployeeVM list, and bind that to the grid. Then you can filter all you want and the EmpIsSelected property will persist.

When you Save , or whatever, you transform the view model back into Employee .

This is a rudimentary MVVM pattern that solves the problem of UI having different data needs than the back end.

The problem is that there us no storage provided for the unbound column. A fairly simple way of doing this is to use the DataTable . Add a Selected column via SQL so that a column is created for it in the DataTable :

Dim sql = "SELECT False As Selected, Id, Name, Descr, Bird, Color, ItemDate FROM Sample"

With most DB providers this will work fine: The table will have a Boolean column and the DGV will add a check column for it. But there is a slight problem with MySQL since it lacks a way to CAST to Boolean or tinyint. As a result the column will be text. So, for MySql, skip the SQL Selected column and add a new column to the table:

' after the DS is built, but BEFORE it is bound to the DGV:
Dim dc As New DataColumn("Selected", GetType(Boolean))
dc.DefaultValue = False    ' important!
dtSample.Columns.Add(dc)
dc.SetOrdinal(0)           ' make it column 0

Be sure to set the DefaultValue to something or it will be DBNull for all the rows and choke the check column. It is also important to do this after the DataSource / DataTable is built but before you bind it to the DGV.

You can also catch and replace the Text column for a Check column in the ColumnAdded event. Either way NET knows it is a "virtual" column, so it will not interfere with any DBCommand objects you may have built for the DataAdapter (the OP cant have any based on that query, but future readers may). Results:

在此处输入图片说明

I check-selected several orange and Stork rows, then filtered and the checks remained. At the core, this and Mr CrowCoder's answer do the same thing just differently: provide somewhere to store the Selected state. This method uses the DGV's normal ability to automatically save to the DataTable .


If you want to use the ColumnAdded event:

If e.Column.Name = "Selected" AndAlso TypeOf e.Column Is DataGridViewTextBoxColumn Then
    Dim dc As New DataGridViewCheckBoxColumn()
    dc.HeaderText = "Selected"
    dc.Name = "Selected"
    dc.DataPropertyName = "Selected"
    dc.ValueType = GetType(Boolean)
    dgv5.Columns.Remove(e.Column)
    dgv5.Columns.Insert(0, dc)
    dc.DisplayIndex = 0
End If

The really important thing is to assign the DataPropertyName so the DGV knows where in the table to store the data.

Directly adding a column to the DataTable is simpler and more direct, this can be useful in other situations.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM