データ レコードとグリッド

概要

Curl ® のデータ アクセス API には、データをフィールドやレコードに編成するための RecordSet と関連クラスが用意されています。GUI Toolkit には、RecordSet データの表示と操作専用のコントロールがあります。抽象基本クラス RecordSetDisplay は、RecordSet を表示するための基本機能の多くを定義します。データを表形式で表示する RecordGrid とフォーム形式で表示する RecordForm は、どちらも RecordSetDisplay を継承します。RecordSetDisplay で定義される重要な機能を次に説明します。
つまり、いずれも RecordSetDisplay のサブクラスですが、RecordGrid は多数のレコードを一度に表示するように設計された Control で、RecordForm はコントロールを含み、一度に 1 レコードずつ表示するように設計された Dialog です。RecordForm の詳細については、「フォームでのデータの表示」を参照してください。

RecordGrid と関連クラス

RecordGridRecordSetDisplay の特定の実装で、RecordSet データを表形式で表示し、表示と表示データを管理するための機能が組み込まれています。RecordSet 内のデータの使用については、「データの管理」を参照してください。
次の図は RecordGrid の主なコンポーネントを示しています。
Figure: レコード グリッド
行と列はソース RecordSet のレコードとフィールドに対応しています。 RecordGridColumn で列を明示的に作成して、列の外観と機能を制御するプロパティを設定できます。列は自動的に作成することもできます。列をカスタマイズする場合は、列を自分で追加します。
RecordGrid を使用すると、エンド ユーザーは SelectionContextSelection インターフェイスを使用して RecordSet のデータを選択できます。 RecordGridSelectionContext を継承し、 RecordGridSelectionSelection のサブクラスです。 RecordGrid.selection プロパティは現在の選択を保持する RecordGridSelection です。 RecordGrid.select-current-record? プロパティは、選択の概念と現在のレコードの概念を 1 つにまとめたものです。このプロパティが true の場合は、現在のレコードも選択されます。
RecordGridOptions クラスは、多くの重要なローカル オプションを定義します。 RecordGridOptions で定義されたオプションは、特に設定しなければ RecordGrid から RecordGridColumn、そして RecordGridCell に渡されます。
RecordGridCell は特殊なセルを定義します。セルは、 RecordGridRecordGridColumn、または RecordGridCell レベルで指定できます。 RecordGridOptions.cell-spec では、 RecordGridCell を返すプロシージャ、または RecordGridCell のサブクラスを使用して、セルを指定できます。
RecordGrid は、GUI Toolkit の多数の基本オプションを ControlFrameGraphic から継承します。

RecordGrid の表示

RecordGrid には充実した機能が既定で含まれています。例を使用して以下の機能を確認してみましょう。
さらに、グリッド セルを右クリックすると、コンテキスト メニューが表示され、以下の選択肢を選択できます。
[フィルタの追加] メニューは、カーソルが RecordGrid のセルの上にある場合のみ使用できます。[昇順ソート][降順ソート] コマンドは、カーソルが列の上にある場合に使用できます。[フィルタを取り除く][変更をコミット]、および [変更を元に戻す] コマンドは、カーソルが RecordGrid 内にある場合に使用できます。

例: RecordGrid の既定の動作
{include "../../default/support/data-long.scurl"}
{RecordGrid
    record-source = records,
    height = 10cm,
    width = 13cm
}

RecordGrid の外観の変更

RecordGrid には、次のようなグリッドの外観を変更するためのオプションが多数用意されています。
次の例では、これらのプロパティを既定値以外の値に設定して RecordGrid の外観を変更しています。

例: RecordGrid の外観の変更
{let people:RecordSet =
    {RecordSet
        {RecordFields
            {RecordField "First", domain = String},
            {RecordField "Last", domain = String},
            {RecordField "Age", domain = int}
        },
        {RecordData First = "John", Last = "Smith", Age = 25},
        {RecordData First = "Jane", Last = "Smith", Age = 29},
        {RecordData First = "Jane", Last = "Jones", Age = 28}
    }
}
{value
    {RecordGrid
        record-source = people,
        height = 3cm,
        display-column-headers? = false,
        display-filler-column? = true,
        alternate-row-background = "#cceecc",
        grid-line-color = "#ccccff",
        horizontal-grid-line-width = 2px,
        vertical-grid-line-width = 4px
    }
}

計算による行の背景

RecordGrid.row-background-spec プロパティを使用して、各行の背景色を計算するプロシージャを指定することができます。プロシージャはレコード グリッド、表示されるレコード、および RecordGrid.records のレコードのインデックスを受け取ります。プロシージャはこの情報に基づいて背景を計算します。プロシージャは頻繁に呼び出される可能性があり、したがってこのプロシージャでの冗長な計算はパフォーマンスに影響を与える可能性があることに注意してください。
次の例では、指定されたレコードのデータに基づいて行の背景を計算します。Notified? フィールドの値が true のレコードの背景は銀色です。この場合、背景色はレコードのグリッド内での位置とは無関係です。

例: データドリブン型の行の背景
{include "../../default/support/data-long.scurl"}

{define-proc public {notified rg:RecordGrid, r:Record, i:int}:#Background
    {if {r.compare-field "Notified?", true} == 0 then
        {return  {Background.from-string "silver"}}
     else
        {return null}
    }
}

{RecordGrid
    record-source = records,
    height = 10cm,
    width = 13cm,
    row-background-spec = notified
}
次の例では、レコード インデックスを使用して、グリッドに表示される 5 および 10 レコードごとに色づけされた背景を返します。背景は完全に位置に依存していて、レコードのデータとは無関係です。

例: 位置に基づいたの行の背景
{include "../../default/support/data-long.scurl"}

{define-proc public {notified rg:RecordGrid, r:Record, i:int}:#Background
    {if ((i + 1) mod 10) == 0 then
        {return  {Background.from-string "#ccccff"}}
     elseif ((i + 1) mod 5) == 0 then
        {return  {Background.from-string "#eeeeff"}}
     else
        {return null}
    }
}

{RecordGrid
    record-source = records,
    height = 10cm,
    width = 13cm,
    row-background-spec = notified
}

カスタム見出し行の作成

RecordGrid.header-options プロパティでいくつかのオプションを設定して、1 つの RecordGridRowOptions オブジェクトの見出し行の外観を制御できます。
次の例のカスタム見出しでは、 control-actuator-color を設定して、現在のソート方向を示す見出しセルの矢印の色を制御しています。
また、レコード グリッドの key-spec プロパティを一意のフィールド ID に設定しています。 key-spec を設定すると、レコードのソートなどの一括変更を行っても、レコード グリッドで現在のレコードを保持できます。レコードを選択し、いずれかの列見出しをクリックしてレコードをソートしてください。グリッドの左端にある矢印が、レコードのソート後もレコードとの関連付けを保持していることに注意してください。次の行をコメントにして、例を実行してみてください。
key-spec = "id",
ソートを実行すると、矢印がグリッドの先頭のレコードに移動します。

例: 見出し行のカスタマイズ
{let staff:RecordSet =
    {evaluate
        {url "../../default/support/data.scurl"}
    }
}
{let gradient-header:RecordGridRowOptions = 
    {RecordGridRowOptions
        background = 
            {LinearGradientFillPattern
                {Fraction2d 0, 1}, 
                {Fraction2d 0, 0}, 
                {Spectrum.from-endpoints
                    {FillPattern.get-silver},
                    {FillPattern.get-white}
                }
            },
        valign = "origin",
        halign = "left",
        control-actuator-color = "gray",
        color = "gray",
        font-size = 14pt,
        font-family = "serif"
    }
}
{let rg:RecordGrid = 
    {RecordGrid height = 10cm,
        record-source = staff,
        column-movable?= false,
        header-options = gradient-header,
        key-spec = "id",
        width = 16cm
    }
}
{value rg}

カスタム列の作成

RecordGrid.automatic-columns? は、グリッドでレコード ソース内の各フィールドに対応する列を自動的に生成するかどうかを制御します。既定値は true で、列を自動的に生成します。生成された各列では、 RecordGridColumn.automatic? プロパティが true に設定されます。 automatic?false に設定すると、列の管理を独自に行うことができます。
次の例では、カスタム列を作成して、基本データにある 2 つのフィールドのデータを 1 列に表示します。 automatic-columns?false に設定して列が自動生成されないようにし、 RecordGrid.format-spec を、データ セットから姓と名の両方を含む単一の文字列を返すプロシージャに設定しています。また、この例では年齢の列を明示的に作成する必要があります。

例: 2 つのフィールドを 1 列に表示
{let people:RecordSet =
    {RecordSet
        {RecordFields
            {RecordField 
                "First", caption = "First Name", domain = String
            },
            {RecordField 
                "Last", caption = "Last Name", domain = String
            },
            {RecordField "Age", domain = int}
        },
        {RecordData First = "John", Last = "Smith", Age = 25},
        {RecordData First = "Jane", Last = "Smith", Age = 29},
        {RecordData First = "Jane", Last = "Jones", Age = 28}
    }
}
{let rg:RecordGrid = 
    {RecordGrid
        record-source = people,
        height = 3cm,
        automatic-columns? = false,
        {RecordGridColumn 
            "First",
            editable? = false,
            format-spec = 
                {proc {data:any, r:Record}:String
                    {return r["First"] & " "&
                        r["Last"]}
                },
            header-spec = "Full Name"
        },
        {RecordGridColumn "Age"}
    }
}
{let rg-real:RecordGrid = 
    {RecordGrid
        record-source = people,
        height = 3cm
    }
}
{value rg}
前の例では RecordGridOptions.header-spec を "Full Name" という文字列に設定しましたが、次の例では、 RecordGridOptions.header-spec をより高度な列見出しを作成するプロシージャに設定します。
また、1 列おきに適用する背景色を設定しています。ドラッグ操作で列の順序を変えることができないように、 RecordGridcolumn-movable? オプションは false に設定されていることにも注目してください。

例: カスタム列見出しの作成
{let staff:RecordSet =
    {evaluate
        {url "../../default/support/data.scurl"}
    }
}
{define-proc public {make-header rgc:RecordGridColumn}:Graphic
    {return
        {TextFlowBox
            font-weight = "bold",
            font-size = 12pt,
            color = "green",
            rgc.field.caption
        }
    }
}   
{let rg:RecordGrid = 
    {RecordGrid height = 10cm,
        record-source = staff,
        width = 16cm,
        column-movable? = false,
        header-spec = make-header
    }
}
{for i:int = 0 to rg.columns.size - 1 do
    {if (i mod 2 == 0) then
        set rg.columns[i].background = "#cceecc"
    }
}
{value rg}

列のグループ化

RecordGridColumnGroup を使用して、 RecordGrid の列をグループ化できます。グループのキャプションを指定したり、グループに最初に表示される順番に列をリストしたりできます。 RecordGridOptions.enclose-header-label? オプションは、グループ キャプションと列キャプションの間にセパレータを表示するかどうかを決定します。
グループ内で列をドラッグアンドドロップできますが、列をグループにドラッグしたり、グループから列をドラッグしたりすることはできません。また、グループそのものをドラッグアンドドロップすることもできません。
グループは、[フレームの固定] 操作に影響します。フレームの固定は、グループの境界でのみ有効です。グループ内の任意の位置で [フレームの固定] を適用すると、固定の境界はグループの右端になります。

例: RecordGrid 列のグループ化
{RecordGrid
    record-source = 
        {evaluate
            {url "../../default/support/data.scurl"}
        },
    height = 10cm,
    width = 13cm,
    {RecordGridColumn "id"},
    {RecordGridColumnGroup
        "Name",
        || ƒRƒƒ“ƒg‚ð휂µ‚ăwƒbƒ_[‚̃ZƒpƒŒ[ƒ^[‚ð휂µ‚Ü‚·
||--        enclose-header-label? = false,
        {RecordGridColumn "First"},
        {RecordGridColumn "Last"}
    },
    {RecordGridColumnGroup
        "Address",
        || ƒRƒƒ“ƒg‚ð휂µ‚ăwƒbƒ_[‚̃ZƒpƒŒ[ƒ^[‚ð휂µ‚Ü‚·
||--        enclose-header-label? = false,
        {RecordGridColumn "City"},
        {RecordGridColumn "State"}
    }
}

RecordGrid の UI 動作の変更

RecordGrid とエンド ユーザーのやりとりを変更するオプションは、次のとおりです。

表示プロパティの設定

これらのオプションを既定値以外の値に設定する例を次に示します。その結果、グリッドはユーザー操作に反応を示さないことが多くなります。これらのプロパティ設定をさまざまに組み合わせて例を実行し、グリッドの動作を確認してください。

例: UI 動作の変更
{let people:RecordSet =
    {RecordSet
        {RecordFields
            {RecordField "First", domain = String},
            {RecordField "Last", domain = String},
            {RecordField "Age", domain = int}
        },
        {RecordData First = "John", Last = "Smith", Age = 25},
        {RecordData First = "Jane", Last = "Smith", Age = 29},
        {RecordData First = "Jane", Last = "Jones", Age = 28}
    }
}
{value
    {RecordGrid
        cells-take-focus? = false,
        column-movable? = false,
        column-resizable? = false,
        editable? = false,
        display-record-selectors? = false,
        display-navigation-panel? = false,
        record-source = people,
        height = 3cm

    }
}

ユーザー入力の解析

RecordGridOptions.parse-spec オプションを使用して、 RecordGrid がエンド ユーザーによって入力されたテキストを RecordSet の値に変換する方法を指定できます。次の例では、ユーザーは単純なドメイン名を入力します。完全な URL を記述するために parse-spec によって http://www. が追加されます。

例: parse-spec の使用
{let urls:RecordSet =
    {RecordSet
        {RecordFields
            {RecordField 
                "Company", domain = String
            },
            {RecordField 
                "URL", domain = String
            }
        },
        {RecordData Company = "Curl", URL = "http://www.curl.com"},
        {RecordData Company = "Google", URL = "http://www.google.com"},
        {RecordData Company = "EBay", URL = "http://www.ebay.com"}
    }
}

{let rg:RecordGrid = 
    {RecordGrid
        record-source = urls,
        height = 3cm,
        automatic-columns? = false,
        {RecordGridColumn "Company"},
        {RecordGridColumn "URL",
            width = {make-elastic},
            parse-spec = 
                {proc {v:String, r:Record}:any
                    let nv:String = 
                        {if {v.prefix? "http://www."} then v
                         else "http://www." & v
                        }
                    {return nv}
                }
        }
    }
}
{value
    {VBox
        rg,
        {HBox
            {CommandButton 
                width = {make-elastic}, label = "append record",
                {on Action do
                    {urls.append {RecordData}}
                }
            },
            {CommandButton 
                width = {make-elastic}, label = "commit records",
                bound-command = {rg.get-command "commit"}                
            },
            {CommandButton 
                width = {make-elastic}, label = "revert records",
                bound-command = {rg.get-command "revert"}                
            }
        }
    }        
}

表示領域の固定

RecordGrid には、フレームの固定機能が用意されています。この機能を使用すると、指定した数の行と列をスクロールできないように設定できます。 RecordGrid の次のプロパティとメソッドは、スクロールしない行と列の数を管理します。
オフセット値では、上側と左側のスクロールして見えなくなる行と列で固定領域を指定できます。
RecordGrid はスクロールしない領域を指定しますが、 RecordGridUIRecordGrid による固定された列と行の表示方法を実装します。 RecordGridUI のサブクラスを作成する必要がある場合は、 RecordGridUI.note-frozen-count-changed メソッドを使用して、サブクラスがスクロールしない領域の変更に反応できるようにすることができます。
SkinnableRecordGridUI によって実装される既定の動作を次の例に示します。先頭のデータ レコードに見出し情報が含まれていることに注意してください。この例では、 RecordGrid.set-frozen-region を使用して見出しレコードと id 列を固定します。

例: 見出し行の固定
{let staff:CsvRecordSet =
    {CsvRecordSet
        {url "../../default/support/data.csv"}
    }
}
{let rg:RecordGrid = 
    {RecordGrid
        record-source = staff,
        display-column-headers? = false,
        display-record-selectors? = false
    }
}
{rg.set-frozen-region 1, 1}
{value rg}

RecordGrid の表示データの変更

レコードのソートとフィルタ

RecordGrid から RecordSet ソースをフィルタできます。グリッドでコンテキスト メニューを開き、セル値に基づいてフィルタできます。 RecordGrid.filter プロパティを設定することもできます。設定した値は、 RecordGrid.records RecordViewfilter プロパティの設定に使用されます。
ソートされたレコードも同じ方法で表示できます。グリッドでコンテキスト メニューを開いて 1 つの列をソートできます。 RecordGrid.sort プロパティを設定することもできます。設定した値は、 RecordGrid.records RecordViewsort プロパティの設定に使用されます。
ソートとフィルタの操作性を変更するには、セルまたは列見出しを右クリックし、表示されるコンテキスト メニューから項目を選択します。 RecordGrid.filter-menu-proc プロパティを使用すると、このメニューをカスタマイズできます。次の例は、メニューの項目の順序を逆にする単純な filter-menu-proc を示しています。

例: RecordGrid.filter および RecordGrid.sort の使用
{let staff:RecordSet =
    {evaluate
        {url "../../default/support/data.scurl"}
    }
}
{let rg:RecordGrid = 
    {RecordGrid
        record-source = staff, height = 5cm, width = 16cm,
        column-selection-enabled? = true,
        filter-menu-proc =
            {proc {array:{Array-of MenuItem}, cell:RecordGridCell}:{Array-of MenuItem}
                {array.reverse}
                {return array}
            } 
    }
}
{VBox
    rg,
    {HBox
        {CommandButton label = "Sort by City, State", 
            width = {make-elastic},
                {on Action do
                    set rg.sort = "City, State"
                }
        },
        {CommandButton 
            width = {make-elastic},
            label = "Filter for Cambridge, MA",
            {on Action do
                set rg.filter = 
                    {RecordData City = "Cambridge", State = "MA"}
            }
        }
    }
}

RecordGrid からの列の削除

次の例は、エンド ユーザーが RecordGrid に列のサブセットを表示できるようにします。
この例では、"columns" と呼ばれる 2 つのプロパティを使用しています。1 つは RecordGrid.columns で、現在表示されている列です。もう 1 つは RecordGridSelection.columns で、現在選択されている列です。この例では RecordGrid.columns から RecordGridSelection.columns を削除します。
選択された列を削除するために、この例では RecordGrid.selection プロパティを使用します。 RecordGridSelectioncolumns プロパティは Iterator-of RecordGridColumn であり、これにより選択された列を順番に 1 列ずつループ処理することができます。 RecordGrid.automatic-columns?false に設定して、列の初期セットを明示的に作成する必要があります。 automatic-columns?true の場合、列は削除されてもすぐに自動的に再生成されます。

例: 列の削除と復元
{let staff:RecordSet =
    {evaluate
        {url "../../default/support/data.scurl"}
    }
}
{let rg:RecordGrid = 
    {RecordGrid
        record-source = staff, height = 5cm, width = 16cm,
        column-selection-enabled? = true
    }
}
{let original-columns:{Array-of RecordGridColumn} = {rg.columns.clone}}
{VBox
    rg,
    {HBox
        {CommandButton label = "Remove Selected Column(s)", width = 8cm,
            {on Action do
                {let new-columns:{Array-of RecordGridColumn} = {rg.columns.clone}}
                {for col:RecordGridColumn in rg.selection.columns do
                    {new-columns.remove {new-columns.find col}}
                }
                set rg.columns = new-columns
            }
        },
        {CommandButton label = "Restore Original Columns", width = 8cm,
            {on Action do
                set rg.columns = original-columns
            }
        }
    }
}

RecordGrid でのデータ選択

次のプロパティは、さまざまな種類の選択を可能にします。
RecordGrid には、 RecordGrid 内のデータを選択するメソッドが多数用意されています。次のようなメソッドがあります。
RecordGrid.selection は現在の選択を保持します。 RecordGrid には、選択範囲に対して機能するメソッドが多数用意されています。次のようなメソッドがあります。

行と列の選択

RecordGrid で行や列を選択すると、 RecordGrid オブジェクトは選択された内容を RecordGrid.selection プロパティに記録します。 RecordGrid.selectionRecordGridSelection で、選択されている列またはレコードの数を指定するプロパティがあり、選択列およびレコードの繰り返し処理が可能です。
次の例では、 RecordGridSelection.record-countRecordGridSelection.records を使用して選択されているレコードについてレポートし、 RecordGridSelection.column-countRecordGridSelection.columns を使用して選択されている列についてレポートします。列やレコードを選択して、コマンド ボタンを押してみてください。選択内容に追加するには、Ctrl キーを押しながら左クリックします。
また、この例では RecordGrid.select-current-record?true に設定しているため、レコードのセルをクリックしてレコードを選択できます。 select-current-record? を使用する場合は、 RecordGridOptions.cells-take-focus?false に設定すると便利です。このように設定すると、セルのクリックによってセルが選択され、フォーカスはそのセルに移動しません。

例: RecordGrid による列とレコードの選択
{let staff:RecordSet =
    {evaluate
        {url "../../default/support/data.scurl"}
    }
}
{let rg:RecordGrid = 
    {RecordGrid height = 10cm,
        column-selection-enabled? = true,
        select-current-record? = true,
        cells-take-focus? = false,
        record-source = staff,
        width = 16cm
    }
}
{let msg:VBox = {VBox}}
{VBox
    rg,
    {HBox
        {CommandButton 
            width = 8cm,
            label = "Process records",
            {on Action do
                {if rg.selection.record-count == 0 then
                    {popup-message "No records selected"}
                 else
                    {for i:int in rg.selection.records do
                        let r:Record = rg.records[i]
                        {msg.add {format "%s %s", 
                                     r["First"],
                                     r["Last"]
                                 }
                        }
                    }
                    {popup-message
                        msg
                    }
                    {msg.clear}
                }
            }
        },
        {CommandButton 
            width = 8cm,
            label = "Process columns",
            {on Action do
                {if rg.selection.column-count == 0 then
                    {popup-message "No columns selected"}
                 else
                    {for c:RecordGridColumn in rg.selection.columns do
                        {msg.add c.field.name}
                    }
                    {popup-message
                        msg
                    }
                    {msg.clear}
                }
            }
        }
    }
}

領域の選択

クリック アンド ドラッグで列と行を含む四角形の領域を選択できます。複数の領域を選択するには、Ctrl キーを使用します。 RecordGrid は、選択した領域に関する情報を RecordGrid.selection に保持します。
次の例では、 RecordGridSelection.region-countRecordGridSelection.regions を使用して、選択した領域についてレポートします。1 つ以上の領域を選択して、コマンド ボタンを押してみてください。

例: RecordGrid での領域の選択
{let staff:RecordSet =
    {evaluate
        {url "../../default/support/data.scurl"}
    }
}
{let rg:RecordGrid = 
    {RecordGrid height = 10cm,
        region-selection-enabled? = true,
        record-selection-enabled? = false,
        record-source = staff,
        width = 16cm
    }
}
{let msg:VBox = {VBox}}
{VBox
    rg,
    {HBox
        {CommandButton 
            width = {make-elastic},
            label = "Process regions",
            {on Action do
                {if rg.selection.region-count == 0 then
                    {popup-message "No regions selected"}
                 else
                    {msg.add "regions: " & rg.selection.region-count}
                    {for r:RecordGridRegion in rg.selection.regions do
                        {msg.add "columns: " & r.column-count 
                            & " First column index: " & r.first-column}
                        {msg.add "rows: " & r.row-count 
                            & " First row index: " & r.first-row}
                        {msg.add "---"}
                    }
                    {popup-message
                        msg
                    }
                    {msg.clear}
                }
            }
        }
    }
}

データのコピーと貼り付け

RecordGrid では、次の Command を使用して、選択した行、列、および領域をコピーしたり貼り付けたりできます。
標準の UI では、これらのコマンドには標準のキーボード アクセラレータ (たとえば、コピーの場合は Ctrl + C) または右クリックで表示されるコンテキスト メニューを使用してアクセスできます。
選択、コピー、および貼り付けコマンドは、グリッドに表示されている行と列に対して機能します。フィルタを適用するか列をグリッドから削除した場合は、グリッドに表示されたまま残っているフィールドとレコードに対してのみ機能します。
次の例では、これらのコマンドの使用方法を示しています。次の点に注意してください。

例: 選択、コピー、貼り付け
{let staff:RecordSet =
    {evaluate
        {url "../../default/support/data.scurl"}
    }
}
{let rg:RecordGrid = 
    {RecordGrid height = 10cm,
        region-selection-enabled? = true,
        record-selection-enabled? = false,
        select-current-record? = true,
        cells-take-focus? = false,
        record-source = staff,
        width = 16cm
    }
}
{let msg:VBox = {VBox}}
{let fixed-region:RecordGridRegion = 
    {RecordGridRegion
        1, 1, 2, 2
    }
}
{VBox
    rg,
    {HBox
        {CommandButton 
            width = {make-elastic}, 
            label = "Select All",
            bound-command = {rg.get-command "select-all"}                
        },
        {CommandButton 
            width = {make-elastic}, 
            label = "Select Nothing",
            bound-command = {rg.get-command "select-nothing"}                
        },
        {CommandButton
            width = {make-elastic}, 
            label = "Select Region",
            {on Action do
                {rg.select-region fixed-region, additive? = true}
            }
        },
        {CommandButton
            width = {make-elastic}, 
            label = "Deselect Region",
            {on Action do
                {rg.deselect-region fixed-region}
            }
        },
        {CommandButton 
            width = {make-elastic}, 
            label = "Copy Selection",
            bound-command = {rg.get-command "copy"}                
        },
        {CommandButton 
            width = {make-elastic}, 
            label = "Paste",
            bound-command = {rg.get-command "paste"}                
        }
    }
}

カスタム セルの作成

次の例では、 maritime-signal-flags という RecordSetflag というフィールドがあります。このフィールドはグラフィック ファイルを識別する文字列を格納します。 RecordGrid でこれらのファイルをグラフィック画像として表示するには、特別な RecordGridCell が必要です。
次の例では FlagCell という StandardRecordGridCell のサブクラスを作成して refresh-data メソッドをオーバーライドし、旗の画像を表示します。また、セルでは RecordGridCell.can-update? を使用して RecordGridCell.cells-take-focus? を設定し、セルが更新できない場合にフォーカスを取得しないようにしています。
RecordGridColumn を使用してグリッド内に列も作成しています。旗のデータを表示する列では、 cell-spec として FlagCell を使用します。また、列幅を画像幅に適した値に設定し、 column-resizable? を false に設定してユーザーが列幅を変更したり画像を破棄できないようにしています。
さらに、 RecordGrideditable? オプションは false に設定されていて、グリッド全体が読み取り専用になっていることにも注意してください。

例: RecordGrid を使用した RecordSet の表示
{let maritime-signal-flags:RecordSet =
    {evaluate
        {url "../../default/support/flag-data.scurl"}
    }
}
{define-class public open FlagCell
  {inherits StandardRecordGridCell}
  
  field private flag:Frame = 
      {Frame width={make-elastic}, height={make-elastic}}

  {constructor public {default}
    {construct-super}
    set self.height = 42px
    {self.add-internal self.flag}
    set self.cells-take-focus? = self.can-update?
  }
  
  {method public open {refresh-data}:void
    let (data:String, valid?:bool) = {self.get-formatted-data}
    {if valid? and data != ""  then
        set self.flag.background = {url data}
     else
        {unset self.flag.background}
    }
  }
}
{let rg:RecordGrid = 
        {RecordGrid 
            width = 14cm,
            height = 14cm,
            record-source = maritime-signal-flags,
            editable? = false,
            automatic-columns? = false,
            {RecordGridColumn  width = 1.3cm, "letter"},
            {RecordGridColumn  width = 2cm, "phonetic"},
            {RecordGridColumn  width = 1.3cm, "colors"},
            {RecordGridColumn  width = 1.3cm, "red?"},
            {RecordGridColumn  width = 1.3cm, "white?"},
            {RecordGridColumn  width = 1.3cm, "blue?"},
            {RecordGridColumn  width = 1.3cm, "yellow?"},
            {RecordGridColumn  width = 1.3cm, "black?"},
            {RecordGridColumn  "flag", width = 53px, 
                column-resizable? = false, cell-spec = FlagCell}
        }
}
{value rg}

RecordSetDisplay のコマンド

RecordSetDisplay クラスは CommandContext で、元のデータの操作するコマンドを多数サポートしています。 RecordGridRecordForm はどちらも、 RecordSetDisplay のサブクラスを作成した場合と同様にこれらのコマンドを継承します。これらのコマンドを使用して、メニュー項目やコントロール機能を提供することができます。次の例では、[commit records] と [revert records] ボタンに commitrevert コマンドが使われています。

例: ユーザーによるデータ追加
{let people:RecordSet =
    {RecordSet
        {RecordFields
            {RecordField 
                "First", caption = "first name", domain = String
            },
            {RecordField 
                "Last", caption = "last name", domain = String
            },
            {RecordField 
                "Age", caption = "age", domain = int
            }
        },
        {RecordData First = "John", Last = "Smith", Age = 25},
        {RecordData First = "Jane", Last = "Smith", Age = 29},
        {RecordData First = "Jane", Last = "Jones", Age = 28}
    }
}
{let rg:RecordGrid = 
    {RecordGrid
        height = 3cm, width = 9.75cm, record-source = people
    }
}
{value
    {VBox
        rg,
        {HBox
            {CommandButton 
                width = {make-elastic}, label = "append record",
                {on Action do
                    {people.append {RecordData}}
                }
            },
            {CommandButton 
                width = {make-elastic}, label = "commit records",
                bound-command = {rg.get-command "commit"}                
            },
            {CommandButton 
                width = {make-elastic}, label = "revert records",
                bound-command = {rg.get-command "revert"}                
            }
        }
    }        
}

レコード グリッドでのリッチ テキストの使用

RichTextArea コントロールでは、リッチ テキストを入力したり書式設定したりできます。 RichTextArea のリッチ テキストを RecordGrid に組み込むには次の 2 つの方法があります。
次の例は RecordSet にメッセージを追加します。 TextArea を使用して個人の名前を入力し、 RichTextArea を使用してメッセージを入力します。テキストには書式を設定できます。[Add Messages] をクリックして、 RecordSet にレコードを追加してください。この例では RecordForm にコントロールを配置しないため、追加したレコードをナビゲーション パネルを使用して表示することはできません。 RecordGrid に追加したすべてのレコードを表示するには、[Show Messages] をクリックします。メッセージを格納する format-as-curl-source-fragment と、特殊な用途の RecordGridCell である CodeCell を使用して、メッセージを表示していることに注意してください。

例: コード文字列としてのリッチ テキストの格納
{let messages:RecordSet =
    {RecordSet
        {RecordFields
            {RecordField "Recipient", domain = String},
            {RecordField "Message", domain = String}
        }
    }
}
{define-class public open CodeCell
  {inherits StandardRecordGridCell}
  {constructor public {default}
    {construct-super}
    {self.add-internal {TextFlowBox}}
  }
  {method public open {refresh-data}:void
    {super.refresh-data}
    let (data:String, valid?:bool) = {self.get-formatted-data}
    {self.child.graphic.clear}
    {if valid? then
        {self.child.graphic.add
            {evaluate data}
        }
    }
  }
}
{let rg:RecordGrid = 
    {RecordGrid
        record-source = messages,
        automatic-columns? = false,
        {RecordGridColumn "Recipient"},
        {RecordGridColumn "Message", cell-spec = CodeCell}
    }
}
{let rcp:String = ""}
{let msgstr:String = ""}
{let ta:TextArea = {TextArea height = 1cm}}
{let rta:RichTextArea = {RichTextArea height = 2cm}}
{VBox
    {Table
        {row-prototype "Message recipient: ", ta},
        {row-prototype "Message text: ", rta}
    },
    {HBox
        {CommandButton label = "Add Message",
            {on e:Action do
                set rcp = ta.value
                set msgstr = {rta.format-as-curl-source-fragment}
                {messages.append {RecordData Recipient = rcp, Message = msgstr}}
                {e.consume}
            }
        },
        {CommandButton label = "Show Messages",
            {on e:Action do
                {{View {value rg}}.show}
                {e.consume}
            }
        }
    }
}
次の例では、 RecordForm を使用して RichTextStringRecordSet に格納しています。 RecordForm でリッチ テキストを使用する方法については、「レコード フォームでのリッチ テキストの使用」を参照してください。[show grid] をクリックすると、特殊な目的の RecordGridCell である RichTextAreaCell によって、 RichTextString がグリッドに表示されます。この例では RichTextArea を読み取り専用で使用するため、 display-formatting-panel?false に設定して、書式設定コントロールが表示されないようにしています。 RichTextAreaCell では新しい ActiveTraversalContainer も設定する必要があります。

例: RichTextString としてのリッチ テキストの格納
{let messages:RecordSet =
    {RecordSet
        {RecordFields
            {RecordField "Recipient", domain = String},
            {RecordField "Message", domain = #any}
        },
        {RecordData Recipient = "Recipient", 
            Message = {RichTextString.from-string "Message"}}

    }
}
{define-class public open RichTextAreaCell
  {inherits StandardRecordGridCell}
  {doc-next {purpose Construct this object.}}
  {constructor public {default}   
    {construct-super}
    
    {self.add-internal 
        {RichTextArea height = 2cm,
            active-traversal-container = null,
            display-formatting-panel? = false}
    }
  }
  {method public open {refresh-data}:void
    {super.refresh-data}
    let rta:RichTextArea = self.child.graphic asa RichTextArea
    let (val:any, valid?:bool) = {self.get-data}
    {if valid? then
        set rta.value = val
     else
        set rta.ui-object.control-content-background = "pink" 
    }
  }
}
{let rg:RecordGrid = 
    {RecordGrid
        record-source = messages,
        editable? = false,
        automatic-columns? = false,
        {RecordGridColumn "Recipient"},
        {RecordGridColumn "Message", cell-spec = RichTextAreaCell}
    }
}
{let rf:RecordForm = 
    {RecordForm
        record-source = messages,
        {VBox
            {TextArea {bind value to "Recipient"}},
            {RichTextArea 
                height = 2cm,
                {bind value to "Message"}
            }
        }
    }
}
{value
    {VBox
        rf,
        {HBox
            {CommandButton 
                label = "append record",
                {on Action do
                    {messages.append 
                        {RecordData Recipient = "Recipient", 
                            Message = {RichTextString.from-string "Message"}}}
                }
            },
            {CommandButton
                label = "update record",
                {on ac:Action do
                    {if-non-null {rf.update} then
                        {if {popup-question 
                                {message
                                    There were one or more errors on this form.
                                    Continue?
                                }
                            } == Dialog.no
                         then
                            {ac.consume}
                        }
                    }
                }
            },
            {CommandButton 
                label = "commit changes",
                bound-command = {rf.get-command "commit"}                
            },
            {CommandButton 
                label = "revert records",
                bound-command = {rf.get-command "revert"}
            }
            ,
            {CommandButton 
                label = "show grid",
                {on ac:Action do
                    {{View
                         {value
                             rg
                         }
                     }.show}
                    {ac.consume}
                }
            }
        }
    }
}