wpf datagrid高刷引起界面卡顿问题探索

  • Post author:
  • Post category:其他



目录


一. 高刷错误方法:每更新一次数据,就数据驱动刷新datagird


1. UI矢量语言实现


2. UI下面的cs文件实现数据绑定(没有用ViewModel)


3. 运行结果及分析


二. 高刷改进方法:每更新一次数据,将数据选择性的放入队列,另外一个线程队列数据驱动刷新datagird


1. MainWindowViewModel.cs里面的绑定显示数据,和数据容器定义


2. 每秒最多把N个数据放入数据容器,同时需要把每秒的最后一次更新数据放入容器


三. 源码链接



一. 高刷错误方法:每更新一次数据,就数据驱动刷新datagird

两个datagrid控件,直接用线程刷新,具体实现如下:


1. UI矢量语言实现

< Border
    Grid.Row = "0"
    Height = "Auto"
    HorizontalAlignment = "Stretch"
    VerticalAlignment = "Stretch"
    Background = "AliceBlue" >

    < DataGrid
        x: Name = "dg_ModuleVara"
        Height = "200"
        HorizontalAlignment = "Stretch"
        VerticalAlignment = "Stretch"
        AutoGenerateColumns = "false"
        Background = "{StaticResource PrimaryBlackBrush}"
        BorderBrush = "{StaticResource BoxBackgroundBrush}"
        BorderThickness = "1"
        CanUserAddRows = "False"
        CanUserSortColumns = "False"
        ColumnWidth = "*"
        GridLinesVisibility = "All"
        HorizontalGridLinesBrush = "{StaticResource BoxBackgroundBrush}"
        HorizontalScrollBarVisibility = "Hidden"
        RowBackground = "{StaticResource PrimaryBlackBrush}"
        RowHeaderWidth = "0"
        SelectionUnit = "FullRow"
        VerticalGridLinesBrush = "{StaticResource BoxBackgroundBrush}"
        VerticalScrollBarVisibility = "Hidden" >
        < DataGrid.Columns >
            < DataGridTextColumn
                Width = "1*"
                Binding = "{Binding Name, Mode=OneWay}"
                Foreground = "White"
                IsReadOnly = "True" />
            < DataGridTextColumn
                Width = "1*"
                Binding = "{Binding DispValue, Mode=OneWay}"
                Foreground = "White"
                IsReadOnly = "True" />
        </ DataGrid.Columns >
    </ DataGrid >
</ Border >
< Border
    Grid.Row = "1"
    Height = "Auto"
    HorizontalAlignment = "Stretch"
    VerticalAlignment = "Stretch"
    Background = "AliceBlue" >

    < DataGrid
        x: Name = "dg_ModuleVara2"
        HorizontalAlignment = "Stretch"
        VerticalAlignment = "Stretch"
        AutoGenerateColumns = "false"
        Background = "{StaticResource PrimaryBlackBrush}"
        BorderBrush = "{StaticResource BoxBackgroundBrush}"
        BorderThickness = "1"
        CanUserAddRows = "False"
        CanUserSortColumns = "False"
        ColumnWidth = "*"
        GridLinesVisibility = "All"
        HorizontalGridLinesBrush = "{StaticResource BoxBackgroundBrush}"
        HorizontalScrollBarVisibility = "Hidden"
        RowBackground = "{StaticResource PrimaryBlackBrush}"
        RowHeaderWidth = "0"
        SelectionUnit = "FullRow"
        VerticalGridLinesBrush = "{StaticResource BoxBackgroundBrush}"
        VerticalScrollBarVisibility = "Hidden" >
        < DataGrid.Columns >
            < DataGridTextColumn
                Width = "1*"
                Binding = "{Binding Name, Mode=OneWay}"
                Foreground = "White"
                IsReadOnly = "True" />
            < DataGridTextColumn
                Width = "1*"
                Binding = "{Binding DispValue, Mode=OneWay}"
                Foreground = "White"
                IsReadOnly = "True" />
        </ DataGrid.Columns >
    </ DataGrid >
</ Border >


2. UI下面的cs文件实现数据绑定(没有用ViewModel)

public MainWindow()
{
    InitializeComponent();

    dg_ModuleVara.ItemsSource = null;
    dg_ModuleVara.ItemsSource = ModuleVaraList;
    dg_ModuleVara2.ItemsSource = null;
    dg_ModuleVara2.ItemsSource = ModuleVaraList2;
    Task.Run(() =>
    {
        while (true)
        {
            UpdateUiTool();
            UpdateUiTool2();
            Task.Delay(5).Wait();
        }
    });
}


/// <summary>
/// 更新Ui1
/// </summary>
/// <param name=""></param>
private void UpdateUiTool()
{
    try
    {
        Random random = new Random();
        System.Windows.Application.Current.Dispatcher.BeginInvoke(new Action(() =>
        {
            ModuleVaraList.Clear();
            for (int imm = 0; imm < 10; imm++)
            {
                DispVara dispVara = new DispVara() { Name = $"标题{imm.ToString()}-{random.NextDouble()}", DispValue = $"内容{imm.ToString()}-{random.NextDouble()}" };
                ModuleVaraList.Add(dispVara);
            }
        }));
    }
    catch (Exception ex)
    {

    }
}

/// <summary>
/// 更新Ui2
/// </summary>
/// <param name=""></param>
private void UpdateUiTool2()
{
    try
    {
        Random random = new Random();
        System.Windows.Application.Current.Dispatcher.BeginInvoke(new Action(() =>
        {
            ModuleVaraList2.Clear();
            for (int imm = 0; imm < 10; imm++)
            {
                DispVara dispVara = new DispVara() { Name = $"标题{imm.ToString()}-{random.NextDouble()}", DispValue = $"内容{imm.ToString()}-{random.NextDouble()}" };
                ModuleVaraList2.Add(dispVara);
            }
        }));
    }
    catch (Exception ex)
    {

    }
}


3. 运行结果及分析


运行5分钟后的内存占用情况


现状:

两个datagrid每秒刷新1000/2=200次/秒,界面相应迟钝,内存200M;五分钟左右涨到了2000M+,肉眼可见的刷新频率明显只有5次/秒,再继续没时间不测试了意义不大


原因分析:

数据更新太快,被堆积了的大量消息(BeginInvoke),造成了内存增加,界面相应变慢


对策:

一般的显示器刷新频率60或75HZ,数据刷新的太快了,屏幕显示不过来,就是显示过来了,眼睛也看不清,所以我们只需要把刷新频率限制到10次/秒,即可解决问题

待继续…

————————————————————————————————————————-


二. 高刷改进方法:每更新一次数据,将数据

选择性

的放入队列,另外一个线程队列数据驱动刷新datagird


1.


MainWindowViewModel.cs里面的绑定显示数据,和数据容器定义

//工具显示绑定数据
private ObservableCollection<DispVara> _moduleVaraList = new ObservableCollection<DispVara>();
public ObservableCollection<DispVara> ModuleVaraList
{
    get
    {
        return _moduleVaraList;
    }
    set
    {
        _moduleVaraList = value;
        OnPropertyChanged("ModuleVaraList");
    }
}
//工具显示绑定数据
private ObservableCollection<DispVara> _moduleVaraList2 = new ObservableCollection<DispVara>();
public ObservableCollection<DispVara> ModuleVaraList2
{
    get
    {
        return _moduleVaraList2;
    }
    set
    {
        _moduleVaraList2 = value;
        OnPropertyChanged("ModuleVaraList2");
    }
}


//工具显示数据容器,每秒钟只再容器中放入10个数据
private Queue<List<DispVara>> _moduleVaraContainer = new Queue<List<DispVara>>();
public Queue<List<DispVara>> ModuleVaraListContainer
{
    get
    {
        return _moduleVaraContainer;
    }
    set
    {
        _moduleVaraContainer = value;
    }
}
//工具显示数据容器,每秒钟只再容器中放入10个数据
private Queue<List<DispVara>> _moduleVaraContainer2 = new Queue<List<DispVara>>();
public Queue<List<DispVara>> ModuleVaraListContainer2
{
    get
    {
        return _moduleVaraContainer2;
    }
    set
    {
        _moduleVaraContainer2 = value;
    }
}


2. 每秒最多把N个数据放入数据容器,

同时需要把每秒的最后一次

更新数据放入容器

/// <summary>
/// 更新Ui容器
/// </summary>
/// <param name=""></param>
private void UpdateUiContainer()
{
    try
    {
        Random random = new Random();
        List<DispVara> temp = new List<DispVara>();
        for (int imm = 0; imm < 10; imm++)
        {
            DispVara dispVara = new DispVara() { Name = $"标题{imm.ToString()}-{random.NextDouble()}", DispValue = $"内容{imm.ToString()}-{random.NextDouble()}", GenerateTime = DateTime.Now };
            temp.Add(dispVara);
        }
        if (ModuleVaraListContainer.Count == 0 && _lastDispVara.Count == 0)
        {
            if (temp.Count > 0)
            {
                _lastDispVara = new List<DispVara>(temp);
                ModuleVaraListContainer.Enqueue(temp);
            }
        }
        else
        {
            if(temp.Count > 0)
            {
                if((temp[0].GenerateTime - _lastDispVara[0].GenerateTime).TotalMilliseconds >= 1000.0 / (_showTimesMaxSencond*1.0))
                {
                    ModuleVaraListContainer.Enqueue(temp);
                    _lastDispVara.Clear();
                    _lastDispVara = new List<DispVara>(temp);
                }
                else if ((temp[0].GenerateTime - _lastDispVara[0].GenerateTime).Seconds == 1)//每秒的最后一次更新,需要加到显示容器
                {
                    ModuleVaraListContainer.Enqueue(_lastDispVara);
                    ModuleVaraListContainer.Enqueue(temp);
                    _lastDispVara.Clear();
                    _lastDispVara = new List<DispVara>(temp);
                }
                else
                {
                    //放弃显示
                }
            }
        }

    }
    catch (Exception ex)
    {

    }
}


效果视频如下:

datagird通过数据容器刷新


三. 源码链接


(19条消息) Wpfdatagrid高刷问题解决方案-C#文档类资源-CSDN文库

参考资料:


WPF绘制图表时,1ms更新一次数据,界面变得特别卡? – 知乎 (zhihu.com)


wpf datagrid卡顿是什么原因? – 知乎 (zhihu.com)



版权声明:本文为gukewee原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。