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
问题根源
- 异步任务完成后未通知UI线程触发重绘,缓存更新后单元格无法自动刷新
- 直接调用
view.Invalidate()会触发全视图重绘,且此时缓存尚未更新,同时频繁全量重绘会导致滚动卡顿 - 跨线程操作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
相关产品推荐
相关产品推荐

