フォームとバインド データ

RecordSet のデータを一度に 1 レコードずつページ形式で表示するための主要なクラスは RecordForm です。RecordForm は、RecordForm.record-source 内のフィールドとフォーム内の GUI Toolkit オブジェクトを関連付けることができます。この関連付けのことをデータ バインドといいます。この関連付けにより、エンド ユーザーは RecordForm を使用してデータを表示したり変更したりできるようになります。
RecordForm 内の GUI オブジェクトは、データ バインド ターゲットとして機能します。DataBindingTarget クラスはデータ バインド ターゲットの基本機能を定義します。Visual は、GraphicOptions を介して DataBindingTarget を継承し、ほとんどすべての GUI Toolkit オブジェクトはVisual を継承します。
主要なコンテキスト クラスは DataBindingContext です。RecordFormDataBindingContext を継承する唯一のクラスです。Curl® API には RecordForm のサブクラスがありません。そのため、データ バインド コンテキストとして機能するのは、RecordForm か、独自に作成されるサブクラスのみです。
DataBindingTarget のプロパティと DataBindingContext 内のデータ フィールド間の接続は、DataBinding により提供されます。Curl API には、DataBinding を簡単に作成できるように bind マクロが用意されています。
RecordFormCommandContext のサブクラス (RecordSetDisplay 経由) で、次のコマンドを提供します。

• "move-next"
• "move-previous"
• "move-first"
• "move-last"
• "move-to"
• "reload"
• "commit"
• "revert"

単純なフォームの作成

単純な RecordForm の例を次に示します。データ バインドは RecordForm コンストラクタで行われます。bind マクロは、フォーム用に定義されるレコード ソースをデータ バインド コンテキストとして使用します。また、bind 呼び出しを含む TextDisplay GUI オブジェクトが使われています。Age フィールドの整数値が TextDisplay に表示されるよう自動的に文字列にフォーマットされる点に注意してください。

例: 単純な RecordForm
{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}
    }
}
{let rf:RecordForm = 
    {RecordForm
        record-source = people,
        {VBox
            {TextDisplay {bind value to "First" }},
            {TextDisplay {bind value to "Last"}},
            {TextDisplay {bind value to "Age"}}
        }
    }
}
{value rf}

データの追加と変更

RecordGrid には、エンド ユーザーが行った変更内容で RecordSet ソース内のデータを更新する機能が組み込まれています。RecordForm にはこのような機能は組み込まれていません。DataBindingContext クラスには update メソッドがあり、エンド ユーザーが追加または変更したデータでデータ ソースを更新するには、これを明示的に呼び出す必要があります。次の例では、別のレコードに移動するか [update record] ボタンをクリックすると update が呼び出されます。また、RecordSet.append を呼び出して新規レコードを追加します。「RecordSet にデータを追加」を参照してください。
例では RecordSetDisplay のイベント CurrentRecordChangeRequest を使用してユーザーからのレコード変更要求に対応しています。「RecordSetDisplay のイベント」を参照してください。
RecordFormRecordGrid は両方とも RecordSetDisplay から多数のコマンド サポートを継承します。次の例では、commitrevert コマンドを使用して RecordSet への変更を処理します。RecordForm には commit というメソッドがありますが、このメソッドは ControlContainer から継承されたもので、データ ソース RecordSet ではなく Dialog への変更をコミットします。

例: RecordForm のデータの追加と変更
{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}
    }
}
{let rf:RecordForm = 
    {RecordForm
        record-source = people,
        {on crc:CurrentRecordChangeRequest at rf:RecordForm do
            {if-non-null {rf.update} then
                {if {popup-question 
                        {message
                            There were one or more errors on this form.
                            Continue to new record and lose changes?
                        }
                    } == Dialog.no
                 then
                    {crc.consume}
                }
            }
        },
        {Table
            {row-prototype "First Name:", 
                {TextField {bind value to "First"}}},
            {row-prototype "Last Name:", 
                {TextField {bind value to "Last"}}},
            {row-prototype "Age:", 
                {TextField {bind value to "Age"}}}
        }
    }
}
{value
    {VBox
        rf,
        {HBox
            {CommandButton 
                width = 3.25cm, label = "append record",
                {on Action do
                    {people.append {RecordData}}
                }
            },
            {CommandButton
                width = 3.25cm, 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 
                width = 3.25cm, label = "commit changes",
                bound-command = {rf.get-command "commit"}                
            },
            {CommandButton 
                width = 3.25cm, label = "revert records",
                bound-command = {rf.get-command "revert"}
            }
        }
    }
}

バインド データの解析

bind マクロの parse 句を使用して、データ レコードを更新する前にコントロール内のデータ値を変換することができます。次の例では、parse を使用して、[First Name] フィールドに入力されたテキスト文字列を大文字に変換します。
さらに、parse を使用してレコード更新の前にデータを検証します。[Email] に入力された文字列にアットマーク文字 (@) が含まれているかどうか調べます。

例: 電子メール アドレスの解析
{let people:RecordSet =
    {RecordSet
        {RecordFields
            {RecordField "First", domain = String},
            {RecordField "Last", domain = String},
            {RecordField "Email", domain = String}
        },
        {RecordData First = "John", Last = "Smith", 
            Email = "johnsmith@smith.com"},
        {RecordData First = "Jane", Last = "Smith", 
            Email = "janesmith@smith.com"},
        {RecordData First = "Jane", Last = "Jones", 
            Email = "janejones@jones.com"}
    }
}
{let rf:RecordForm = 
    {RecordForm
        record-source = people,
        {on crc:CurrentRecordChangeRequest at rf:RecordForm do
            let err:any = {rf.update} 
            {if-non-null err then
                {if {popup-question err.value} == Dialog.no
                 then {crc.consume}
                }
            }
        },
        {Table 
            {row-prototype "First Name:", 
                {TextField 
                    {bind value to "First",
                        {parse str:String as{str.to-upper-clone}}
                    }
                }
            },
            {row-prototype "Last Name:",
                {TextField 
                    {bind value to "Last",
                        {parse str:String as{str.to-upper-clone}}
                    }
                }
            },
            {row-prototype "Email:",
                {TextField 
                    {bind value to "Email",
                        {parse str:String as
                            {if {str.find '@'} == -1 then
                                {DataBindingValidationFailure 
                                    "Doesn't look like an email address. Continue?"}
                             else str}
                        }
                    }
                }
            }
        }
    }
}
{value
    {VBox
        rf,
        {HBox
            {CommandButton 
                width = 3.25cm, label = "append record",
                {on Action do
                    {people.append {RecordData}}
                }
            },
            {CommandButton
                width = 3.25cm, label = "update record",
                {on ac:Action do
                    let err:any = {rf.update} 
                    {if-non-null err then
                        {if {popup-question err.value} == Dialog.no
                         then  {ac.consume}
                        }
                    }
                }
            },
            {CommandButton 
                width = 3.25cm, label = "commit changes",
                bound-command = {rf.get-command "commit"}                
            },
            {CommandButton 
                width = 3.25cm, label = "revert records",
                bound-command = {rf.get-command "revert"}
            }
        }
    }
}

バインド データのフォーマット

bind マクロの format 句を使用して、コントロール値にバインドする前にデータ値を変換できます。次の例では、format を使用して、flag フィールドの文字列データを画像ファイルの url に変換します。

例: 画像 URL のフォーマット
{let maritime-signal-flags:RecordSet =
    {evaluate
        {url "../../default/support/flag-data.scurl"}
    }
}
{let rv:RecordView = 
    {RecordView
        maritime-signal-flags,
        sort = "letter"
    }
}
{value
    {RecordForm
        record-source = rv,
        {VBox
            {TextDisplay
                {bind value to "letter"}
            },
            {TextDisplay
                {bind value to "phonetic"}
            },
            {Frame
                height = 42px,
                width = 53px,
                {bind background to "flag",
                    {format data:String as
                        {if data != "" then
                            {url data}
                         else
                            DataBinding.unset
                        }
                    }
                }
            }
        }
    }
}

レコード フォームでのリッチ テキストの使用

RichTextArea コントロールの value プロパティを RecordSet のフィールドにバインドして、RecordForm にリッチ テキストを含めることができます。RecordSet フィールドを any ドメインとして定義する必要があります。これは、RichTextString が既知の RecordField ドメインではないためです。生成されるフォームは、RecordSetRichTextString データを格納したり表示したりできます。「レコード グリッドでのリッチ テキストの使用」で説明されているように、カスタム RecordGridCell を使用して、RecordGrid にリッチ テキスト データを表示できます。
次の例では、RichTextAreavalue プロパティを messages という RecordSetMessage フィールドにバインドします。Message#any 型で、RichTextString.from-string#factory を使用して初期値に設定されます。RichTextArea によって提供される書式設定パネルを使用して、入力テキストの書式を設定できます。書式設定などの変更内容を格納するよう、基となるレコード セットのレコードを更新する必要があります。

例: RecordFormRichTextString のバインド
{let messages:RecordSet =
    {RecordSet
        {RecordFields
            {RecordField "Recipient", domain = String},
            {RecordField "Message", domain = #any}
        },
        {RecordData Recipient = "Recipient", 
            Message = {RichTextString.from-string "Message"}}
    }
}
{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 
                width = 3.25cm, label = "append record",
                {on Action do
                    {messages.append 
                        {RecordData Recipient = "Recipient", 
                            Message = {RichTextString.from-string "Message"}}}
                }
            },
            {CommandButton
                width = 3.25cm, 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 
                width = 3.25cm, label = "commit changes",
                bound-command = {rf.get-command "commit"}                
            },
            {CommandButton 
                width = 3.25cm, label = "revert records",
                bound-command = {rf.get-command "revert"}
            }
        }
    }
}

RecordSetDisplay のイベント

RecordSetDisplay はそれ自体でイベントを発生させて、現在表示されているレコードの変更を通知します。CurrentRecordChangeRequest は現在のレコードの変更が要求されると発生し、CurrentRecordChanged は現在のレコードが変更されると発生します。
次の例では、CurrentRecordChangeRequest を使用して、新しいレコードに移動する前に変更を保存するようにユーザーに要求します。

例: CurrentRecordChangeRequest の使用
{let rd:RecordData =
    {RecordData Recipient = "Recipient", 
        Message = "Message"
    }
}
{let messages:RecordSet =
    {RecordSet
        {RecordFields
            {RecordField "Recipient", domain = String},
            {RecordField "Message", domain = #any}
        },
        rd
    }
}
{let rm:bool = false}

{let rf:RecordForm = 
    {RecordForm
        record-source = messages,
        {VBox
            {TextArea 
                {bind value to "Recipient"}
            },
            {TextArea 
                height = 2cm,
                {bind value to "Message"}
            }
        },
        {on e:CurrentRecordChangeRequest at rf:RecordForm do
            {if rf.pending-update? then
                {if {popup-message 
                        cancel? =  true,
                        "Save changes to this record?"
                    } == Dialog.ok
                 then
                    {rf.update}
                 else
                    {e.consume}
                }
            }
        }
    }
}
{value
    {VBox
        rf,
        {HBox
            {CommandButton 
                width = 3.25cm, label = "append record",
                {on Action do
                    {messages.append rd}
                }
            },
            {CommandButton
                width = 3.25cm, 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 
                width = 3.25cm, label = "commit changes",
                bound-command = {rf.get-command "commit"}                
            },
            {CommandButton 
                width = 3.25cm, label = "revert records",
                bound-command = {rf.get-command "revert"}
            }
        }
    }
}

データ アクセス クラスの階層

以下のリストは、Curl® データ アクセス システムで使用されるクラス階層とクラスを示しています。

イベント ターゲットの階層

EventTarget
RecordSet
EventManagingRecordSet
BasicRecordSet
LocalRecordSet
EmptyRecordSet
RecordView
DefaultRecordView
EmptyRecordView

イベントの階層

Event
RecordSetEvent
RecordSetLoadStateChanged
RecordsChanged
RecordAdded
RecordRemoved
RecordModified
RecordsReordered
RecordsBulkChanges
RecordFieldsChanged
RecordFieldAdded
RecordFieldRemoved
RecordFieldModified
RecordFieldsReordered
RecordFieldsBulkChanges

データの階層

RecordSet
ConnectedRecordSet  — BasicRecordSet
BasicConnectedRecordSet
Connection
BasicConnection
FileConnection
RecordSet
BasicRecordSet
CsvRecordSet  — ConnectedRecordSet
ConnectedFileRecordSet
TextDataReader
ColumnDataReader
CsvDataReader
TextDataWriter
CsvDataWriter
CsvImportDataConverter
CsvImportDateConverter
CsvImportNumberConverter
Domain
StandardDomain-of
TransientAnyDomain

その他のクラス

Domain
EnumDomain-of
StandardDomain-of
StandardAnyDomain
StandardBoolDomain
StandardCharDomain
StandardDateDomain
StandardDateTimeDomain
StandardDoubleDomain
StandardFloatDomain
StandardInt64Domain
StandardIntDomain
StandardStringDomain
StandardTimeDomain
TransientAnyDomain
DomainDetector
DomainTable
Record
RecordData
RecordField
RecordFields
RecordFilter
RecordSort
RecordState

列挙体

RecordSetLoadState
RecordFieldIndexType

レコード ディスプレイ クラスの階層

Visual  — CommandContext
RecordSetDisplay
GuiEvent
CurrentRecordChanged
CurrentRecordChangeRequest
GuiWindowEvent
GuiInputEvent
KeyEvent
KeyPress
RecordGridKeyPress

レコード フォームの階層

Dialog  — RecordSetDisplay — DataBindingContext
RecordForm
Dialog
RecordSetDisplayNavigationPanel

データ バインド クラス

DataBinding
DataBindingTarget
DataBindingContext
Exception
DataException
CommitFailed

選択機能の階層

SelectionContextCommand
DeleteCommand
RecordGridDelete
SelectionContextCommand
SelectAllCommand
RecordGridSelectAll
Selection
RecordGridSelection
DataException
RecordFieldNotFound
RecordGridRegion

コード グリッドの階層

SelectionContext  — RecordSetDisplay — MultiUIControlFrame — RecordGridOptions
RecordGrid
OptionListInterface
RecordGridOptions
Frame  — ControlUI — RecordGridOptions
RecordGridUI
StandardRecordGridUI
BaseFrame  — RecordGridOptions
RecordGridCell
StandardRecordGridCell
GraphicOptionList  — RecordGridOptions
RecordGridColumn

コマンド階層

CommandImpl
SelectionContextCommand
CopyCommand
RecordGridCopy
PasteCommand
RecordGridPaste
SelectAllCommand
RecordGridSelectAll
RecordSetDisplayCommand
RecordSetDisplayCommitCommand
RecordSetDisplayReloadCommand
RecordSetDisplayRevertCommand
RecordSetDisplayMoveCommand
RecordSetDisplayMoveFirstCommand
RecordSetDisplayMoveLastCommand
RecordSetDisplayMoveNextCommand
RecordSetDisplayMovePreviousCommand