简体   繁体   English

过滤 DataGridView 而不丢失未绑定的更改

[英]Filter a DataGridView without losing unbound changes

Im developing a vb.net application and I have a dgv which displays a list of employees.我正在开发一个 vb.net 应用程序,我有一个显示员工列表的 dgv。 The dgv has an unbound checkbox column which is used in selecting employees for a required action. dgv 有一个未绑定的复选框列,用于为所需操作选择员工。 I use rowFilter to filter the list by department or location or whatever the user chooses.我使用rowFilter按部门或位置或用户选择的任何内容过滤列表。

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.如果用户使用部门 1 过滤并选中他想要的员工旁边的复选框,选择它们然后过滤到部门 2,再选择几个员工,当我们过滤回部门 1 时,选中的员工没有被选中没有了。

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这是我用来填充 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:你的 winform 可能有一个方法来获取员工列表:

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.通常,“视图”(在您的情况下是 winform)与后端数据有不同的需求。 So you make an EmployeeViewModel :所以你创建了一个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.因此,您编辑GetEmployees以获取员工列表并将其转换为EmployeeVM列表,并将其绑定到网格。 Then you can filter all you want and the EmpIsSelected property will persist.然后您可以过滤您想要的所有内容,并且EmpIsSelected属性将保持EmpIsSelected

When you Save , or whatever, you transform the view model back into Employee .当您Save或其他任何东西时,您将视图模型转换回Employee

This is a rudimentary MVVM pattern that solves the problem of UI having different data needs than the back end.这是一个基本的 MVVM 模式,它解决了 UI 与后端具有不同数据需求的问题。

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 .一个相当简单的方法是使用DataTable Add a Selected column via SQL so that a column is created for it in the DataTable :通过 SQL 添加一个Selected列,以便在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.对于大多数数据库提供程序,这将正常工作:该表将有一个布尔列,DGV 将为其添加一个检查列。 But there is a slight problem with MySQL since it lacks a way to CAST to Boolean or tinyint.但是 MySQL 有一个小问题,因为它缺乏将 CAST 转换为Boolean或 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:因此,对于 MySql,跳过 SQL Selected 列并向表中添加一个新列:

' 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.确保将DefaultValue设置为某个DefaultValue ,否则所有行都将是DBNull并阻塞检查列。 It is also important to do this after the DataSource / DataTable is built but before you bind it to the DGV.构建DataSource / DataTable之后将其绑定到 DGV之前执行此操作也很重要。

You can also catch and replace the Text column for a Check column in the ColumnAdded event.您还可以在ColumnAdded事件中捕获并替换 Check 列的 Text 列。 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).无论哪种方式 NET 都知道它是一个“虚拟”列,因此它不会干扰您可能为DataAdapter构建的任何DBCommand对象(OP 不能有任何基于该查询的对象,但未来的读者可能会)。 Results:结果:

在此处输入图片说明

I check-selected several orange and Stork rows, then filtered and the checks remained.我检查选择了几个orangeStork行,然后过滤并保留了检查。 At the core, this and Mr CrowCoder's answer do the same thing just differently: provide somewhere to store the Selected state.本质上,this 和 Mr CrowCoder 的回答做同样的事情,只是不同:提供某个地方来存储Selected状态。 This method uses the DGV's normal ability to automatically save to the DataTable .此方法使用 DGV 的正常功能自动保存到DataTable


If you want to use the ColumnAdded event:如果要使用ColumnAdded事件:

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.真正重要的是分配DataPropertyName以便 DGV 知道在表中存储数据的位置。

Directly adding a column to the DataTable is simpler and more direct, this can be useful in other situations.直接向DataTable添加一列更简单、更直接,这在其他情况下很有用。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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