FlexGrid for WinForms
パフォーマンスの強化
グリッド > パフォーマンスの強化

データの仮想化を使用する

大規模なデータセットを効率的にレンダリングするために、FlexGrid はデータの仮想化をサポートしており、ユーザーが下にスクロールすると、データはページ単位で取得されます。グリッドは、行の総数を把握していますが、ユーザーに表示される行のみをロードして表示します。たとえば、C1.DataCollection パッケージを使用して、仮想化可能なデータソースを実装できます。C1VirtualDataCollection を継承したクラスを作成し、GetPageAsync メソッドを実装します。このメソッドは、割り当てられたデータソースから 1 ページ分のデータを返します。次の GIF は、仮想スクロールモードで FlexGrid がどのように表示されるかを示しています。

データの仮想化

public class VirtualModeCollectionView : C1VirtualDataCollection<Customer>
{
   public int TotalCount { get; set; } = 1_000;
   protected override async Task<Tuple<int, IReadOnlyList<Customer>>> GetPageAsync(int pageIndex, int startingIndex, int count, IReadOnlyList<SortDescription> sortDescriptions = null, FilterExpression filterExpression = null, CancellationToken cancellationToken = default(CancellationToken))
   {
      await Task.Delay(500, cancellationToken);//Simulates network traffic.
      return new Tuple<int, IReadOnlyList<Customer>>(TotalCount, Enumerable.Range(startingIndex, count).Select(i => new Customer(i)).ToList());
   }
}              
Public Class VirtualModeCollectionView
    Inherits C1VirtualDataCollection(Of Customer)

    Public Property TotalCount As Integer = 1_000

    Protected Overrides Async Function GetPageAsync(ByVal pageIndex As Integer, ByVal startingIndex As Integer, ByVal count As Integer, ByVal Optional sortDescriptions As IReadOnlyList(Of SortDescription) = Nothing, ByVal Optional filterExpression As FilterExpression = Nothing, ByVal Optional cancellationToken As CancellationToken = DirectCast(Nothing, CancellationToken)) As Task(Of Tuple(Of Integer, IReadOnlyList(Of Customer)))
        Await Task.Delay(500, cancellationToken) 'Simulates network traffic.
        Return New Tuple(Of Integer, IReadOnlyList(Of Customer))(TotalCount, Enumerable.Range(CInt(startingIndex), CInt(count)).[Select](Function(i) New Customer(i)).ToList())
    End Function
End Class 

BeginUpdate メソッドと EndUpdate メソッドを使用する

BeginUpdate メソッドと EndUpdate メソッドを使用して、グリッドのパフォーマンスを最適化できます。大規模な変更を行う前に BeginUpdate を呼び出し、完了したら EndUpdate を呼び出すことで、再描画を保留します. これにより、画面のちらつきが減り、パフォーマンスも向上します。特にグリッドに大量の行を追加する場合は、1 行が追加されるたびに範囲を再計算し、スクロールバーを更新する必要があるため、この最適化が役立ちます。

以下のコードは、大量の行を効率的に WinForms FlexGrid に追加する方法を示します。EndUpdate メソッドを「finally」ブロック内で呼び出すことで、再描画が正しく復元されることに注目してください。

void UpdateGrid(C1FlexGrid c1FlexGrid1)
{
  try
  {
    c1FlexGrid1.BeginUpdate(); // ちらつきを避けるためにレンダリングを一時停止します
    c1FlexGrid1.Rows.Count = 1;
    for (int i = 1; i < 10000; i++)
      c1FlexGrid1.AddItem("Row " + i.ToString());
  }
  finally
  {
    c1FlexGrid1.EndUpdate(); // 常にレンダリングを復元します
  }
}  
                        
Private Sub UpdateGrid(ByVal c1FlexGrid1 As C1FlexGrid)
    Try
        c1FlexGrid1.BeginUpdate() ' ちらつきを避けるためにレンダリングを一時停止します
        c1FlexGrid1.Rows.Count = 1

        For i As Integer = 1 To 10000 - 1
            c1FlexGrid1.AddItem("Row " & i.ToString())
        Next

    Finally
        c1FlexGrid1.EndUpdate() ' 常にレンダリングを復元します
    End Try
End Sub
                        

AutoResize プロパティを False のままにする(デフォルト)

連結グリッドで AutoResize プロパティが true に設定されていると、データソースから新しいデータが読み取られるたびに、列が最大幅のエントリに合わせて自動的にサイズ変更されます。データソースに大量の行や列が含まれている場合は、自動的なサイズ変更に時間がかかる場合があります。このような場合は、AutoResizefalse に設定して、直接コード内で列幅を設定することを検討してください。

スタイルを動的に割り当てる

FlexGrid では、セルのスタイルを作成して、行、列、および任意のセル範囲に割り当てることができます。この機能を使用して、グリッドセルを条件付きで書式設定できます。通常、これは SetCellStyle() メソッドを使用して行います。ただし、その場合は、セルの値が変わるたびにスタイルを更新する必要があります。また、グリッドがデータソースに連結されている場合は、ソートやフィルタ処理などの操作後にデータソースがリセットされるたびにスタイルは失われます。このような場合に優れた方法は、OwnerDraw 機能を使用して、セルの値に基づいて動的にスタイルを選択することです。たとえば、このサンプルコードは、WinForms FlexGrid で負の値を赤色で、1,000 を超える値を緑色で表示する方法を示しています。

private void Form1_Load(object sender, EventArgs e)
{
   // 列にランダムな値を入力します
      c1FlexGrid1.Cols[1].DataType = typeof(int);
      Random rnd = new Random();
      for (int r = 1; r < c1FlexGrid1.Rows.Count; r++)
       {
          c1FlexGrid1[r, 1] = rnd.Next(-10000, 10000);
       }
 
   // 負の値を表示するために使用されるスタイルを作成します
      c1FlexGrid1.Styles.Add("Red").ForeColor = Color.Red;
 
   // DrawModeプロパティを設定して、OwnerDrawを有効にします
      c1FlexGrid1.DrawMode = C1.Win.C1FlexGrid.DrawModeEnum.OwnerDraw;
      c1FlexGrid1.OwnerDrawCell += new C1.Win.C1FlexGrid.OwnerDrawCellEventHandler(C1FlexGrid1_OwnerDrawCell);
}

private void C1FlexGrid1_OwnerDrawCell(object sender, OwnerDrawCellEventArgs e)
{
    if(!e.Measuring)
    {
        // 行と列に整数データが??含まれていることを確認します
        if (e.Row > 0 && c1FlexGrid1.Cols[e.Col].DataType == typeof(int))
        {
           // スタイル「Red」を適用します 
           e.Style = c1FlexGrid1.Styles["Red"];
        }
    }
}       
                        
    Private Sub Form1_Load(ByVal sender As Object, ByVal e As EventArgs)
        ' 列にランダムな値を入力します
        c1FlexGrid1.Cols(1).DataType = GetType(Integer)
        Dim rnd As Random = New Random()

        For r As Integer = 1 To c1FlexGrid1.Rows.Count - 1
            c1FlexGrid1(r, 1) = rnd.[Next](-10000, 10000)
        Next


        ' 負の値を表示するために使用されるスタイルを作成します
        c1FlexGrid1.Styles.Add("Red").ForeColor = Color.Red

        ' DrawModeプロパティを設定して、OwnerDrawを有効にします
        c1FlexGrid1.DrawMode = C1.Win.C1FlexGrid.DrawModeEnum.OwnerDraw
        c1FlexGrid1.OwnerDrawCell += New C1.Win.C1FlexGrid.OwnerDrawCellEventHandler(AddressOf C1FlexGrid1_OwnerDrawCell)
    End Sub

    Private Sub C1FlexGrid1_OwnerDrawCell(ByVal sender As Object, ByVal e As OwnerDrawCellEventArgs)
        If Not e.Measuring Then

            ' 行と列に整数データが??含まれていることを確認します
            If e.Row > 0 AndAlso c1FlexGrid1.Cols(e.Col).DataType Is GetType(Integer) Then
                ' スタイル「Red」を適用します  
                e.Style = c1FlexGrid1.Styles("Red")
            End If
        End If
    End Sub     

OwnerDrawCell イベントでスタイルが変更されないようにする

パフォーマンスを改善する別の方法としては、OwnerDrawCell イベントのパラメータとして渡される CellStyle オブジェクトを変更しないことがあります。代わりに、e.Style パラメータに新しい値を割り当てます。イベントハンドラに渡される CellStyle は別のセルからもよく使用されるため、これは重要です。たとえば、意図せず WinForms FlexGrid の標準スタイルを変更してしまうと、グリッドの他の類似のセルにも影響を与えます。

// ** 正しい方法:
private void C1FlexGrid1_OwnerDrawCell(object sender, C1.Win.C1FlexGrid.OwnerDrawCellEventArgs e)
{
    // このセルをペイントするときに使用するスタイルを選択します
    e.Style = MyStyleSelector(e.Row, e.Col);
}
                                                
// ** 正しくない方法:
private void C1FlexGrid1_OwnerDrawCell(object sender, C1.Win.C1FlexGrid.OwnerDrawCellEventArgs e)
{
    // このセルをペイントするときに使用するスタイルを選択します
    // CellStyleオブジェクトを変更するとグリッドが無効になり、
    // このイベントハンドラーが何度も呼び出されるため、
    // このメソッドは正しくありません。
    e.Style.Color = MyColorSelector(e.Row, e.Col);
}                               
                        
' ** 正しい方法:
Private Sub C1FlexGrid1_OwnerDrawCellMethod(ByVal sender As Object, ByVal e As C1.Win.C1FlexGrid.OwnerDrawCellEventArgs)
' このセルをペイントするときに使用するスタイルを選択します
e.Style = MyStyleSelector(e.Row, e.Col)
End Sub


' ** 正しくない方法:
Private Sub C1FlexGrid1_OwnerDrawCell(ByVal sender As Object, ByVal e As C1.Win.C1FlexGrid.OwnerDrawCellEventArgs)
' このセルをペイントするときに使用するスタイルを選択します
' CellStyleオブジェクトを変更するとグリッドが無効になり、
' このイベントハンドラーが何度も呼び出されるため、
' このメソッドは正しくありません。
e.Style.Color = MyColorSelector(e.Row, e.Col)
End Sub         
                        

列に省略符を表示する

グリッドの 1 つの列に省略記号を表示するには、Trimming プロパティを使用する必要があります。セルに収まるように文字列をトリミングする長さを決定するには、Trimming プロパティを NoneCharacterWordEllipsisCharacterEllipsisWord、または EllipsisPath に設定します。トリミングの詳細については、「トリミングされたテキストの表示」を参照してください。

次のコードは、一番近い文字までテキストをトリミングして、WinForms Flexgrid の 2 番目の列の最後に省略記号を表示するように Trimming プロパティを設定します。

c1FlexGrid1.Cols[1].StyleNew.Trimming =StringTrimming.EllipsisCharacter;
c1FlexGrid1.Cols(1).StyleNew.Trimming = StringTrimming.EllipsisCharacter
                        

1 つのセルに複数行のテキストを表示する

1 つのセル内に複数のテキスト行を表示するには、WordWrap プロパティと Height プロパティを使用します。WordWrap プロパティは、スペースが含まれる長い文字列を自動的に改行して複数行に表示するどうかを決定します。強制改行(vbCrLf または "\n\r")が含まれる文字列は常に複数行に表示されます。複数行テキストは、固定セルにもスクロール可能なセルにも表示できます。テキストの折り返しについては、「テキストの折り返し」を参照してください。

WinForms FlexGrid で複数行テキストを効率的に表示する方法については、以下のコードを参照してください。

// WordWrapプロパティを設定します
c1FlexGrid1.Styles["Normal"].WordWrap = true;
 
// 行の高さを設定します
c1FlexGrid1.Rows[1].Height = 2 * c1FlexGrid1.Rows.DefaultSize;
 
// セルにテキストを追加します
c1FlexGrid1[1, 2] = "This is the first line. \r\n This is the second line.";                            
    ' WordWrapプロパティを設定します
    c1FlexGrid1.Styles("Normal").WordWrap = True

    ' 行の高さを設定します
    c1FlexGrid1.Rows(1).Height = 2 * c1FlexGrid1.Rows.DefaultSize

    ' セルにテキストを追加します
    c1FlexGrid1(1, 2) = "This is the first line. " & vbCrLf & " This is the second line."       
                        

データテーブルへの連結時のデータソート順を取得する

データがリフレッシュされたときにグリッドのソート方法を保持するには、デフォルトビューの Sort プロパティとソート式を使用します。Sort プロパティは、列名の後に ASC(デフォルト)または DESC を付けた文字列を使用して、昇順または降順で列をソートします。複数の列は、列名をカンマで区切って指定することでソートできます。1 つのソート式には、複数のグリッド列の名前または 1 つの計算値を含めることができます。実行時にソート式を設定すると、変更がデータビューに即座に反映されます。

次のコードは、WinForms FlexGrid の Sort プロパティでソート式を使用する方法を示しています。

// UnitsInStock列、次にProductID列でデータを並べ替えます
   this.productsBindingSource.Sort = "UnitsInStock ASC, ProductID ASC";                         
                        
' UnitsInStock列、次にProductID列でデータを並べ替えます
Me.productsBindingSource.Sort = "UnitsInStock ASC, ProductID ASC"                               
                        

列に文字数の制限を指定する

任意の列にユーザーが入力できる最大の文字数を設定するには、SetupEditor イベントを使用します。C1FlexGrid クラスの StartEdit イベントで C1TextBox などの外部エディタを宣言する必要があります。次に、SetupEditor イベントで、1 つの列セルに格納できる最大文字数を設定できます。

WinForms FlexGrid 列に文字数制限を設定するには、次のコードを使用します。

private void C1FlexGrid1_StartEdit(object sender, C1.Win.C1FlexGrid.RowColEventArgs e)
{
    c1FlexGrid1.Editor = c1TextBox;
}
                                                        
private void C1FlexGrid1_SetupEditor(object sender, RowColEventArgs e)
{
 
   // 3番目の列を20文字に設定し、残りは10文字のみにします
   if (e.Col == 2)
     c1TextBox.MaxLength = 20;
   else
     c1TextBox.MaxLength = 10;
}
                                
                        
    Private Sub C1FlexGrid1_StartEdit(ByVal sender As Object, ByVal e As C1.Win.C1FlexGrid.RowColEventArgs)
        c1FlexGrid1.Editor = c1TextBox
    End Sub

    Private Sub C1FlexGrid1_SetupEditor(ByVal sender As Object, ByVal e As RowColEventArgs)

        ' 3番目の列を20文字に設定し、残りは10文字のみにします
        If e.Col = 2 Then
            c1TextBox.MaxLength = 20
        Else
            c1TextBox.MaxLength = 10
        End If
    End Sub