You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

DevExpress XtraGrid CustomDrawCell:验证图标仅交互后显示问题

DevExpress XtraGrid 异步验证后单元格图标延迟显示的优化方案

问题场景

我通过CustomDrawCell事件在XtraGrid单元格中绘制图标,核心逻辑是:异步执行数据验证,将结果存入缓存后,根据状态和验证结果绘制对应图标。功能本身正常,但存在两个问题:

  • 验证完成后,图标仅在鼠标悬停单元格或点击表单元素时才会显示
  • 尝试调用view.Invalidate()刷新视图,初始加载有效,但滚动列表时UI卡顿严重

原实现代码:

Private Sub CtlGridView_CustomDrawCell(sender As Object, e As RowCellCustomDrawEventArgs) Handles CtlGridView.CustomDrawCell
    If e.Column.FieldName = NameOf(DataModel.Contract.Status) Then
        Dim view As GridView = CType(sender, GridView)
        Dim iconKeys As New List(Of String)
        Dim cellValue = e.CellValue.ToString() ' 补充原代码缺失的cellValue定义

        ' Check Validation
        Dim guidString As String = view.GetRowCellValue(e.RowHandle, NameOf(DataModel.Contract.GuidId)).ToString()
        Dim guidId As Guid

        If Guid.TryParse(guidString, guidId) Then
            Dim validationResult As New DataModel.ValidationResult()

            ' Check if result is in cache
            If Not _validationCache.TryGetValue(guidId, validationResult) Then
                Task.Run(Sub()
                             Using taskContext As New DbContext()
                                 Dim entry As DataModel.Contract= taskContext.Contracts.Find(guidId)
                                 If entry IsNot Nothing Then
                                     validationResult = entry.Validate(taskContext, exitOnFirstError:=True)
                                 Else
                                     validationResult = New DataModel.ValidationResult
                                 End If

                                 ' Add result to cache
                                 _validationCache.TryAdd(guidId, validationResult)

                             End Using
                         End Sub)
            End If

            Select Case cellValue
                Case "O" : iconKeys.Add("trackingchanges_showmarkup")
                Case "T" : iconKeys.Add("trackingchanges_trackchanges")
                Case "I" : iconKeys.Add("functionsinformation")
                Case "G" : iconKeys.Add("trackingchanges_locktracking")
                Case "W" : iconKeys.Add("trackingchanges_allmarkup")
                Case "K" : iconKeys.Add("business_cash")
            End Select

            If validationResult.Icon IsNot Nothing Then
                iconKeys.Add(validationResult.Icon)
            End If

            If iconKeys.Count > 0 Then
                Dim iconSize As New Size(32, 32)
                IconHelper.DrawIcons(e, iconKeys, _imageCollection, iconSize)
            End If

            e.Handled = True
        End If
    End If
End Sub

问题根源

  1. 异步任务完成后未通知UI线程触发重绘,缓存更新后单元格无法自动刷新
  2. 直接调用view.Invalidate()会触发全视图重绘,且此时缓存尚未更新,同时频繁全量重绘会导致滚动卡顿
  3. 跨线程操作UI控件存在风险,必须回到UI线程执行重绘操作

优化方案

1. 异步任务完成后在UI线程刷新指定单元格

修改异步任务逻辑,在缓存更新后,通过Control.Invoke回到UI线程,仅刷新当前对应的单元格,避免全视图重绘:

If Not _validationCache.TryGetValue(guidId, validationResult) Then
    Dim currentRowHandle = e.RowHandle ' 捕获当前行句柄
    Task.Run(Sub()
                 Using taskContext As New DbContext()
                     Dim entry As DataModel.Contract = taskContext.Contracts.Find(guidId)
                     Dim result As DataModel.ValidationResult = If(entry IsNot Nothing, entry.Validate(taskContext, exitOnFirstError:=True), New DataModel.ValidationResult)

                     ' 更新缓存
                     _validationCache.TryAdd(guidId, result)
                 End Using

                 ' 回到UI线程刷新指定单元格
                 view.Invoke(Sub()
                                 ' 确保行仍在视图中
                                 If view.IsValidRowHandle(currentRowHandle) Then
                                     ' 精确刷新单个单元格,比刷新整行更高效
                                     view.RefreshRowCell(currentRowHandle, e.Column)
                                 End If
                             End Sub)
             End Sub)
End If

2. 避免重复发起异步验证请求

新增一个状态字典标记验证状态,防止同一行被多次触发验证请求,减少不必要的异步操作:

' 定义验证状态枚举
Private Enum ValidationState
    Unchecked
    Checking
    Completed
End Enum

' 新增状态缓存,记录每行的验证进度
Private _validationStates As New Dictionary(Of Guid, ValidationState)

' 在CustomDrawCell中修改验证逻辑
If Guid.TryParse(guidString, guidId) Then
    Dim validationResult As New DataModel.ValidationResult()
    Dim state As ValidationState

    If _validationStates.TryGetValue(guidId, state) Then
        Select Case state
            Case ValidationState.Completed
                _validationCache.TryGetValue(guidId, validationResult)
            Case ValidationState.Checking
                ' 正在验证中,可绘制加载图标提示用户
                iconKeys.Add("loading_icon_key")
        End Select
    Else
        ' 标记为正在验证,避免重复请求
        _validationStates.Add(guidId, ValidationState.Checking)
        Dim currentRowHandle = e.RowHandle
        Task.Run(Sub()
                     ' ... 原验证逻辑 ...
                     _validationCache.TryAdd(guidId, result)
                     ' 更新状态为已完成
                     _validationStates(guidId) = ValidationState.Completed
                     ' 刷新UI
                     view.Invoke(Sub()
                                     If view.IsValidRowHandle(currentRowHandle) Then
                                         view.RefreshRowCell(currentRowHandle, e.Column)
                                     End If
                                 End Sub)
                 End Sub)
    End If
End If

3. 额外性能优化建议

  • 确保IconHelper.DrawIcons方法内部逻辑高效,避免不必要的图形对象创建与销毁
  • 若列表数据量极大,可考虑开启XtraGrid的ServerMode或VirtualServerMode,减少一次性加载的数据量

内容的提问来源于stack exchange,提问作者John

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.21 07:54:54