2012-03-04 115 views
2

我在表单上实施了DataGridView并成功实施VirtualMode。这从本地缓存中检索单元格数据,并且在填充网格/分页等时全部显示正常工作。我处理DataGridView.CellValueNeeded事件以填充单元格。VirtualMode中的WinForms DataGridView何时调用AutoResizeColumn?

在DataGridView上,我将AutoSizeColumnsMode属性设置为DataGridViewAutoSizeColumnsMode.DisplayedCells。我注意到在使用VirtualMode时,在填充单元格后DataGridView看起来并不尊重AutoSizeColumnsMode。我已检查了this article但未找到解决方案。

我最终会做的是不依靠AutoSizeColumnsMode财产,而是调用.AutoResizeColumn()方法地方来调整,所以我最初自动调整列,但随后允许用户调整。

我试图用有限的或者没有成功如下:

  1. 设置DataGridView.AutoSizeColumnsMode.None。然后在我的 .CellValueNeeded处理

    private void dataGridView_CellValueNeeded(object sender, DataGridViewCellValueEventArgs e) 
    { 
        // ... Get cell value from cache 
        dataGridView.AutoResizeColumn(e.ColumnIndex, DataGridViewAutoSizeColumnMode.DisplayedCells); 
    } 
    

    这将引发StackOverFlowException大概是因为它 多次提出.CellValueNeeded

  2. 尝试完全相同的事情,除了在.CellFormatting 事件处理程序。得到了相同的StackOverFlowException

  3. 尝试与不DataGridView.SuspendLayout/ResumeLayout

    private void dataGridView_CellValueNeeded(object sender, DataGridViewCellValueEventArgs e) 
    { 
        // ... Get cell value from cache 
        dataGridView.CellValueNeeded -= dataGridView_CellValueNeeded; 
        dataGridView.AutoResizeColumn(e.ColumnIndex, DataGridViewAutoSizeColumnMode.DisplayedCells); 
        dataGridView.CellValueNeeded += dataGridView_CellValueNeeded; 
    } 
    

    这让所有的空白单元格,所以没有用。

  4. 这实际上是一个有些工作,是有原因的,我不明白:

    private void dataGridView_CellValueNeeded(object sender, DataGridViewCellValueEventArgs e) 
    { 
        // ... Get cell value from cache 
        dataGridView.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.DisplayedCells; 
    } 
    

    它正确地调整大小列,但它似乎很奇怪有反复调用它需要每一个单元格的值。此外,我不能立即将它设置为。无权之后,或将再次StackOverFlowException。因此,我不能允许用户调整列的大小。

  5. 如我在.CellValueNeeded处理文章中提到的调用.UpdateCellValue()也会抛出StackOverFlowException

那么,是否可以调用.AutoResizeColumn()的地方,它不会提高.CellValueNeeded直到溢出?由于#4似乎有能力执行autosize函数,所以好像我也可以从某处手动调用它。

回答

2

我认为这可能是解决方案,尽管我仍然有兴趣听听其他人有什么要说的。

我继续看了DataGridView提出的一些其他事件,发现.RowPostPaint事件。我创建了以下处理:

private void dataGridView_RowPostPaint(object sender, DataGridViewRowPostPaintEventArgs e) 
{ 
    if (dataGridView.AllowUserToResizeColumns) //So not to run unnecessarily 
    { 
     return; 
    } 
    var lastIndex = dataGridView.Rows.GetLastRow(DataGridViewElementStates.Displayed); 
    if (e.RowIndex == lastIndex) //Only do on the last displayed row 
    { 
     dataGridView.AutoResizeColumns(DataGridViewAutoSizeColumnsMode.DisplayedCells); 
     dataGridView.AllowUserToResizeColumns = true; // User has control from here on 
    } 
} 

这实现在初始数据负载的列的自动调整大小,则允许用户从那里重新大小。它只做一次,比每个单元格所需要的更好。在初始数据加载之前,我必须设置dataGridView.AllowUserToResizeColumns = false

这似乎符合法案。用户在初始加载时会很好地看到列,并且可以从那里调整,并且大多数数据在行与行之间具有可比较的长度,所以在大多数情况下,不应该截断或浪费空间。

0

既然您声明您对其他人的说法感兴趣,那么我已经为如何在虚拟数据网格中使用自动列大小调整做了一个稍微不同的方法。

初始AutoResizeColumns调用放置在显示事件之后,以便表单和子组件被初始化并显示。此外,通过连接线调整大小,而不是RowPostPaint DataGridView的滚动事件,这应该是非常轻微更有效,因为此事件不那么频繁,我认为这很好地遵循连同参考MSDN你举:

using System.Collections.Generic; 
using System.Windows.Forms; 

namespace DataGridViewTest 
{ 
    public partial class DataGridViewForm : Form 
    { 
     private List<string> dataSource; 

     public DataGridViewForm() 
     { 
      InitializeComponent(); 

      // Enable VirtualMode for dataGridView1 
      dataGridView1.VirtualMode = true; 

      // Wire CellValueNeeded event handler 
      dataGridView1.CellValueNeeded += DataGridView1_CellValueNeeded; 

      // Wire Scroll event handler 
      dataGridView1.Scroll += DataGridView1_Scroll; 

      // Wire form Shown event handler 
      this.Shown += DataGridViewForm_Shown; 
     } 

     private void DataGridViewForm_Shown(object sender, System.EventArgs e) 
     { 
      // Populate dataGridView1 here to avoid perception of a long form startup time 
      populateDataGridView(); 

      // Resize columns after the form is initialized and displayed on screen, 
      // otherwise calling this method won't actually have an effect on column sizes 
      dataGridView1.AutoResizeColumns(DataGridViewAutoSizeColumnsMode.DisplayedCells); 
     } 

     private void DataGridView1_CellValueNeeded(object sender, DataGridViewCellValueEventArgs e) 
     { 
      // Set the triggering cell's value to the corresponding value from dataSource 
      e.Value = dataSource[e.RowIndex]; 
     } 

     private void DataGridView1_Scroll(object sender, ScrollEventArgs e) 
     { 
      // Resize columns again, but only if a vertical scroll just happened 
      if (e.ScrollOrientation == ScrollOrientation.VerticalScroll) 
      { 
       dataGridView1.AutoResizeColumns(DataGridViewAutoSizeColumnsMode.DisplayedCells); 
      } 
     } 

     private void populateDataGridView() 
     { 
      fetchIntoDataSource(); 

      associateDataSourceToDataGridView(); 
     } 

     private void fetchIntoDataSource() 
     { 
      dataSource = new List<string>(); 

      // Insert a test string into dataSource many times 
      for (int i = 0; i < 1000; i++) 
      { 
       dataSource.Add("test string"); 
      } 
     } 

     private void associateDataSourceToDataGridView() 
     { 
      // Synchronize dataGridView1.RowCount to dataSource.Count 
      // This is necessary for the CellValueNeeded event to fire 
      dataGridView1.RowCount = dataSource.Count; 
     } 
    } 
}