You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

WPF自定义Grid动态行列变更后调用InvalidateVisual重绘边框问题

解决动态Grid自定义边框重绘异常的问题

这种动态调整行列后边框没正确重绘的情况我之前做类似控件时也踩过坑,核心问题通常出在绘制逻辑未同步最新状态或者重绘时机/方法不对上,给你几个针对性的排查和解决方向:

1. 确保OnRender里用的是最新的行列与尺寸数据

很多时候重绘异常是因为在绘制时还拿着旧的行列计数或单元格尺寸。别缓存RowDefinitions.Count/ColumnDefinitions.Count这类值,每次OnRender时直接取最新的,并且用ActualWidth/ActualHeight获取布局后的真实尺寸:

protected override void OnRender(DrawingContext dc)
{
    base.OnRender(dc);
    // 每次绘制都实时获取当前行列数
    int rowCount = RowDefinitions.Count;
    int colCount = ColumnDefinitions.Count;
    Pen borderPen = new Pen(Brushes.DarkGray, 1);

    // 绘制竖边框
    double currentX = 0;
    for (int col = 0; col <= colCount; col++)
    {
        dc.DrawLine(borderPen, new Point(currentX, 0), new Point(currentX, ActualHeight));
        if (col < colCount)
        {
            currentX += ColumnDefinitions[col].ActualWidth;
        }
    }

    // 绘制横边框
    double currentY = 0;
    for (int row = 0; row <= rowCount; row++)
    {
        dc.DrawLine(borderPen, new Point(0, currentY), new Point(ActualWidth, currentY));
        if (row < rowCount)
        {
            currentY += RowDefinitions[row].ActualHeight;
        }
    }
}

2. 调整重绘方法的调用顺序

只调用InvalidateVisual()可能跳过了Grid的布局测量/排列步骤,导致绘制时单元格的位置还是旧的。建议修改行列定义后按以下顺序调用:

// 修改RowDefinitions和ColumnDefinitions的逻辑...

// 先触发重新测量和排列,再强制重绘
InvalidateMeasure();
InvalidateArrange();
InvalidateVisual();

这样能保证布局系统先更新每个单元格的位置和尺寸,再执行你的自定义绘制逻辑。

3. 确保UI操作在主线程执行

如果你的ViewModel行列变化是在后台线程触发的,修改RowDefinitions/ColumnDefinitions的操作必须切换到UI线程,否则布局更新会失效:

Application.Current.Dispatcher.Invoke(() =>
{
    // 这里写修改RowDefinitions和ColumnDefinitions的代码
    InvalidateMeasure();
    InvalidateArrange();
    InvalidateVisual();
});

4. 用绑定替代手动修改行列定义

手动维护RowDefinitions/ColumnDefinitions容易出错,建议把行列数据绑定到ViewModel的ObservableCollection,通过转换器自动生成行列定义:
比如ViewModel里定义:

public ObservableCollection<double> RowHeights { get; } = new ObservableCollection<double>();
public ObservableCollection<double> ColumnWidths { get; } = new ObservableCollection<double>();

然后在XAML里用转换器绑定:

<local:CustomGrid>
    <local:CustomGrid.RowDefinitions>
        <MultiBinding Converter="{StaticResource CollectionToRowDefinitionsConverter}">
            <Binding Path="RowHeights"/>
        </MultiBinding>
    </local:CustomGrid.RowDefinitions>
    <!-- ColumnDefinitions同理 -->
</local:CustomGrid>

这样当ViewModel的集合变化时,WPF会自动更新行列定义,布局系统也会自动触发重绘,比手动调用方法更可靠。

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

火山引擎 最新活动