Thread Rating:
  • 469 Vote(s) - 2.77 Average
  • 1
  • 2
  • 3
  • 4
  • 5
セル単位での色指定
05-28-2013, 03:56 PM,
#1
セル単位での色指定
RecordGrid で、セル単位に色をつけたい場合、どのようにすればよいでしょうか。
入力エラーがあるセルだけ背景を赤くすることが目的です。

カスタムセルを作成する必要があると思っていますが、
セルクラスのインスタンスは、カラムの中で使いまわされる(という認識の)ため、
インスタンスの背景色を直接指定すると、エラーでないセルに再利用されたときにも
背景色がそのままになってしまうと思います。

どのようにエラー状態を保持させるのがよいのでしょうか。
05-31-2013, 12:46 AM,
#2
RE: セル単位での色指定
カスタムセルを使って値が不正なセルの背景色に色をつけるなら、セルの更新が必要な際に呼ばれる
カスタムセルのrefresh-dataメソッドでセルの値のエラーチェックをして背景色を変えるのが簡単そう
ですがどうでしょうか
Code:
|| レコードセット
{def rs =
    {RecordSet
        {RecordFields
            {RecordField "COL1", domain = String},
            {RecordField "COL2", domain = String},
            {RecordField "COL3", domain = String}
        },
        {RecordData COL1 = "NG", COL2 = "OK", COL3 = "OK"},
        {RecordData COL1 = "OK", COL2 = "NG", COL3 = "OK"},
        {RecordData COL1 = "OK", COL2 = "OK", COL3 = "NG"}
    }
}

|| 入力値の有効性チェック(OKという文字列でなければ無効)
{define-proc public {is-invalid-val val:String}:bool
    {return
        {if val.empty? != true and
            {val.to-upper-clone} != "OK"
         then
            true
         else
            false
        }
    }
}

|| カスタムセル
{define-class public MyCustomCell
  {inherits StandardStringCell}
  
  {constructor public {default}
    {construct-super}
  }
  
  ||スクロール等でセルに表示すべきデータが切り替わる場合に呼ばれる
  {method public {refresh-data}:void
    {super.refresh-data}
    
    def (data:String, valid?:bool) = {self.get-formatted-data}
    {if not valid? or  || 値が取得できないまたは
        self.selected? || 行が選択されている場合は何もしない
     then  
        {return}
    }
    
    || セルの値をチェック
    || 列毎にチェックメソッドを変更できるが、ここでは手抜きして
    || 全部同じチェックメソッドを呼んでいる
    def invalid? =
        {switch self.field.name
         case "COL1" do
            {is-invalid-val data}
         case "COL2" do
            {is-invalid-val data}
         case "COL3" do
            {is-invalid-val data}
         else
            true
        }
    
    || チェック結果により背景色を変更
    {if invalid? then
        || NG の場合は赤大文字
        set self.background = "red"
        set self.color = "pink"
     else
        || OK の場合は通常の文字
        {if not self.selected? then
            {unset self.background}
            {unset self.color}
        }
    }
  }
}

{value
    || レコードグリッドを表示
    {VBox
        margin=2mm,
        {bold OK以外の文字列を入力するとエラー},
        {Fill height=2mm},
        {RecordGrid
            record-source = rs,
            height = 3cm,
            width = 10cm,
            || record-source 内のフィールドに対するカラムの自動生成を無効に設定
            automatic-columns? = false,
            || 表示するカラムを明示的に生成
            {RecordGridColumn "COL1", cell-spec = MyCustomCell},
            {RecordGridColumn "COL2", cell-spec = MyCustomCell},
            {RecordGridColumn "COL3", cell-spec = MyCustomCell}
        }
    }
}

どのような状況が思いつかないのですが、「エラー状態を保持~」と書かれていることから、
たとえばエラーチェックに非常に時間がかかるため、エラーチェックは入力時に一度のみ
しか行いたくないという場合のため、レコードセットに隠しフィールドを用意して、そこへ
チェック結果を保存する案も考えてみました。

チェック結果を格納するフィールドを1つにするために各フィールドのチェック結果合否を
1ビットで表現しているのでちょっと面倒になってしまいましたが・・・
Code:
|| 各フィールドの入力値チェック結果を格納する隠しフィールド名
{def flags-field-name = "flags"}
|| レコードセット
{def rs =
    {RecordSet
        {RecordFields
            {RecordField "COL1", domain = String},
            {RecordField "COL2", domain = String},
            {RecordField "COL3", domain = String},
            {RecordField flags-field-name, domain = int}
        },
        {RecordData COL1 = "NG", COL2 = "OK", COL3 = "OK", flags = 1},
        {RecordData COL1 = "OK", COL2 = "NG", COL3 = "OK", flags = 2},
        {RecordData COL1 = "OK", COL2 = "OK", COL3 = "NG", flags = 4}
    }
}

|| チェック結果フラグから指定フィールドのチェック結果フラグを取り出すプロシージャ
{define-proc public {set-invalid-flag
                        invalid?:bool,
                        flags:int,
                        field-index:int
                    }:int
    || フィールド位置のチェック結果のビットを設定するためのビットフィールドを作成
    def bit-fields = {bit-sll 0x1, field-index}
    || 既存のフラグに指定位置のビットをセットした新しいフラグを作成
    def new-flags =
        {if invalid? then
            {bit-or flags, bit-fields}
         else
            {bit-and flags, {bit-not bit-fields}}
        }
    
    {return new-flags}
}
                        
|| チェック結果フラグから指定フィールドのチェック結果フラグを取り出すプロシージャ
{define-proc public {get-invalid-flag
                        flags:int,
                        field-index:int
                    }:bool
    || フィールド位置のチェック結果のビットを取り出すためのマスクを作成
    let bit-mask:int = {bit-sll 0x1, field-index}
    ||チェック結果フラグのビットフィールドから目的のフラグを取り出す
    def flg:int = {bit-and flags, bit-mask}
    def cell-valid? =
        {if flg != 0 then
            false
         else
            true
        }
    {return cell-valid?}
}

|| セルに格納されているカラムのチェック結果を取り出すプロシージャ
{define-proc public {validate-cell cell:StandardStringCell}:bool
    || セルに格納されている値のレコードセットにおけるフィールド位置を取得
    def rf:#RecordField = cell.field
    def field-name:String = rf.name
    
    def rec:#Record = cell.record
    def rs:#RecordSet = rec.record-set
    
    def rfs:#RecordFields = rs.fields
    def field-index:int = {rfs.get-index field-name}
    
    || チェック結果フラグを隠しフィールドから取得
    def v-flags = {rec.get flags-field-name} asa int
    ||チェック結果フラグのビットフィールドから目的のフラグを取り出す
    def cell-valid? = {get-invalid-flag
                          v-flags,
                          field-index
                      }
    
    {return cell-valid?}
}

|| 入力値の有効性チェック(OKという文字列でなければ無効)
{define-proc public {is-invalid-val val:String}:bool
    {return
        {if val.empty? != true and
            {val.to-upper-clone} != "OK"
         then
            true
         else
            false
        }
    }
}

|| カスタムセル
{define-class public MyCustomCell
  {inherits StandardStringCell}
  
  {constructor public {default}
    {construct-super}
  }
  
  ||スクロール等でセルに表示すべきデータが切り替わる場合に呼ばれる
  {method public {refresh-data}:void
    {super.refresh-data}
    
    def (data:String, valid?:bool) = {self.get-formatted-data}
    {if valid? and  
        not self.selected?         ||行が選択されている場合は何もしない
     then
        || セルの値のチェック結果を取得
        def valid-cell? = {validate-cell self}
        {if valid-cell? != true then
            || NG の場合は赤大文字
            set self.background = "red"
            set self.color = "pink"
         else
            || OK の場合は通常の文字
            {if not self.selected? then
                {unset self.background}
                {unset self.color}
            }
        }
    }
  }
}

|| セル更新時の入力値チェック用イベントハンドラ
{def rec-mod-event-handler =
    {on rm:RecordModified at rs:RecordSet do
        || 更新されたフィールド名を取得
        def field-name = rm.field.name
        || チェックフラグのフィールドが更新された場合はなにもしないで抜ける
        {if field-name == flags-field-name then
            {return}
        }
        || 更新された値を取得して無効か否かをチェックする
        def current-record = rm.record
        def val:String = {current-record.get field-name} asa String
        def invalid? = {is-invalid-val val}
        || 更新フィールドのインデックス位置を指定して、チェックフラグのフィールドへ
        || チェック結果のフラグを設定する
        def rfs:#RecordFields = rs.fields
        def field-index:int = {rfs.get-index field-name}
        def flags = {current-record.get flags-field-name} asa int
        def new-flags = {set-invalid-flag invalid?, flags, field-index}
        {current-record.set flags-field-name, new-flags}
    }
}

{value
    || レコードセットにセル入力値チェックのイベントハンドラをセット
    {rs.add-event-handler rec-mod-event-handler}
    
    || レコードグリッドを表示
    {VBox
        margin=2mm,
        {bold OK以外の文字列を入力するとエラー},
        {Fill height=2mm},
        {RecordGrid
            record-source = rs,
            height = 3cm,
            width = 10cm,
            || record-source 内のフィールドに対するカラムの自動生成を無効に設定
            automatic-columns? = false,
            || 表示するカラムを明示的に生成
            {RecordGridColumn "COL1", cell-spec = MyCustomCell},
            {RecordGridColumn "COL2", cell-spec = MyCustomCell},
            {RecordGridColumn "COL3", cell-spec = MyCustomCell}
        }
    }
}
05-31-2013, 10:49 AM, (This post was last modified: 05-31-2013, 10:51 AM by umemura.)
#3
RE: セル単位での色指定
現在私が想定しているエラー状態を保持、というのは、2つ目にご提示いただいた方法がより合うと思っています。

というのも、値に対するチェックロジックが、クライアントだけで完結すればよいのですが、
サーバーのチェックロジックの結果を保持したい、というような場合がありえるからです。
たとえば、マスタコードの存在チェックなどがそれに当たります。

エラー時のツールチップメッセージや、入力可否なども同様に隠しフィールドで持つのがよさそうだと思っています。

ただ、エラー有無や入力可否、ツールチップメッセージなど、
セルの状態をすべて個別に隠しフィールドで持とうとすると、
フィールド数がそれだけ増えていってしまうので、
セルの状態を保持するクラスを用意して、ひとつのフィールドに対して、
ひとつのセル状態保持用隠しフィールドで済むような実装を考えて見ます。

05-31-2013, 12:38 PM,
#4
RE: セル単位での色指定
状況のご説明ありがとうございます。

たしかにチェックに外部DBとの接続が必要だとセルの表示準備毎に実行するわけにはいかないですね・・・
06-13-2013, 01:43 PM,
#5
RE: セル単位での色指定
この短い形式についての素晴らしい驚き。それはあなたが話して/について書いているあるものを教えてできますか?
すべての情報は持っています。この短い形式について前に投稿さ(と私はちょうどそれを逃した)されていますか?
06-17-2013, 07:57 PM,
#6
RE: セル単位での色指定
とりあえず、フィールドで持ってみました

Code:
||セルの振舞いを定義する Mixinクラス
{define-class public abstract CellReacterMixin
  field public cell-reacter-proc:#{proc-type {RecordGridCell}:void}
  {constructor public {default
                          cell-reacter-proc:#{proc-type {RecordGridCell}:void} = null,
                          ... }
      set self.cell-reacter-proc = cell-reacter-proc
  }
  ||セルの振舞いを処理する 振舞いは独自に指定できるようにプロシージャで定義
  {method public {set-custom-react cell:RecordGridCell}:void
    {if-non-null react-proc = self.cell-reacter-proc then
        {react-proc cell}
    }
  }
  {getter public {as-RecordGridCell}:#RecordGridCell
    {type-switch self
     case rgc:RecordGridCell do
        {return rgc}
     else
        {return null}
    }
  }
  {method public {get-cell-react-option option-name:String}:any
    {if-non-null cell = self.as-RecordGridCell then
        {if-non-null record = cell.record, rf = cell.field then
            {return {cell.record.get rf.name & option-name}}
        }
    }
    {return null}
  }
}

||読み取り専用背景色
{def public READONLY_BACKGROUND_COLOR :String =  "#D0D0D0"}
||エラー用背景色
{def public ERROR_BACKGROUND_COLOR:String  = "#FFB0B0"}
||必須入力用背景色
{def public REQUIRED_BACKGROUND_COLOR:String  = "#FFFFCC"}
||カスタムレコードフィールドのコード名
{def public ERROR-FIELD-NAME:String = "_error?"}
{def public READ-ONLY-FIELD-NAME:String = "_read-only?"}
{def public REQUIRED-FIELD-NAME:String = "_required?"}
||エラー、表示のみ、必須 のプロパティを指定するための実装
{define-class public DefaultCellReactMixin {inherits CellReacterMixin}
  field constant error?:String = ERROR-FIELD-NAME
  field constant read-only?:String = READ-ONLY-FIELD-NAME
  field constant required?:String = REQUIRED-FIELD-NAME
  {constructor public {default ...}
    {construct-super {splice...}}
  }
  ||セルの振舞いを定義するためのフィールドを追加するプロシージャ
  {define-proc public {get-cusotmed-field fields:RecordFields}:RecordFields
    def ary-rf:{Array-of RecordField} = {{Array-of RecordField}}
    {for rf in fields do
        {ary-rf.append rf}
        let read-only-def-val:bool = false
        let required-def-val:bool = false
        {ary-rf.append {RecordField rf.name & ERROR-FIELD-NAME,
                           domain = bool, default-value = false}}
        {ary-rf.append {RecordField rf.name & READ-ONLY-FIELD-NAME,
                           domain = bool, default-value = read-only-def-val}}
        {ary-rf.append {RecordField rf.name & REQUIRED-FIELD-NAME,
                           domain = bool, default-value = required-def-val}}
    }
    {return {RecordFields {splice ary-rf}}}
  }

  {define-proc public {default-cell-reacter cell:RecordGridCell}:void
    {type-switch cell
     case react-mxin:DefaultCellReactMixin do
        ||選択されている場合は、背景色、文字色が設定済みなので、後続の処理を行わない
        {if not cell.selected?  then
            {if-non-null record = cell.record then
                || 表示のみの反映が高位なので、最初に判断
                {if {react-mxin.get-cell-react-option react-mxin.read-only?}  then
                    set cell.editable? = false
                    set cell.background = READONLY_BACKGROUND_COLOR
                 elseif {react-mxin.get-cell-react-option react-mxin.error?} then
                    ||エラー時は背景を赤色にする
                    set cell.background = ERROR_BACKGROUND_COLOR
                 elseif {react-mxin.get-cell-react-option react-mxin.required?} then
                    ||必須時は背景を黄色にする
                    set cell.background = REQUIRED_BACKGROUND_COLOR
                 else
                    {unset cell.background}
                }
            }
        }
    }
  }
}
||セルの振舞いを独自に指定したセル
{define-class public open CustomReactCell
  {inherits StandardStringCell, DefaultCellReactMixin }
  {constructor public {default ...}
    {construct-super.StandardStringCell}
    {construct-super.DefaultCellReactMixin
        cell-reacter-proc = DefaultCellReactMixin.default-cell-reacter,
        {splice ...}}
  }
  {method public open {refresh-data}:void
    {super.refresh-data}
    {self.set-custom-react self}
  }
}

{let people:RecordSet =
    {RecordSet
        {DefaultCellReactMixin.get-cusotmed-field
            {RecordFields  {RecordField "First", domain = String} }
        },
        {RecordData First = "John"},
        {RecordData First = "Jane"},
        {RecordData First = "Jane"}
    }
}
{def grid =
    {RecordGrid
        height = 1.5in,
        automatic-columns? = false,
        {RecordGridColumn cell-spec = CustomReactCell, "First" },
        record-source = people
    }
}
{def real-grid =
    {RecordGrid
        width = 6in,
        height = 1in,
        record-source = people
    }
}
{VBox
    grid,
    real-grid
}



10-28-2013, 01:11 PM,
#7
RE: セル単位での色指定
レコードの修正のたびに refresh-data が呼び出されるので、
まとまった量のレコードを変更する処理が走ると、
描画のパフォーマンスが低下します。

通常、そういう場合は、レコードセットのバッチイベントを true にして実行するので、
バッチイベント状態を見てセルの描画をするかどうかを判断すると、かなりパフォーマンスが良くなりました。

別スレッドで投稿した、グリッドの行番号を表示するためのセルにも同様のことが言えます。


Forum Jump:


Users browsing this thread:
2 Guest(s)

MyBB SQL Error

MyBB has experienced an internal SQL error and cannot continue.

SQL Error:
1017 - Can't find file: 'mybb_threadviews' (errno: 2)
Query:
INSERT INTO mybb_threadviews (tid) VALUES('905')