ラベル GrapeCityTool の投稿を表示しています。 すべての投稿を表示
ラベル GrapeCityTool の投稿を表示しています。 すべての投稿を表示

GrapeCityTool MultiRow5.0J 数値型ユーザ定義セルを改造してハマった

以前に数値型セルを作成しました。
MultiRow 5.0J ユーザ定義セル
この数値セルは最小値~最大値の範囲外の値が入力または設定されると
セルクラスのOnCellFormattingメソッドでNothingを設定するようにしていました。

今回は、最小値~最大値の範囲外の値が「設定」された場合はNothingを設定し
範囲外の値が「入力」されたときは、入力前の値を表示するよう変更しようとしたのですが
大いにハマりました。

入力前の値はセルクラスのInitializeEditingControlメソッドの引数formattedValueでセルクラスのインスタンス変数に退避したのですが、
セルクラスのOnCellFormattingメソッドでこの退避した値を参照すると常にNothingが返ります。

結論
退避したインスタンス変数をCloneメソッドで作成したオブジェクトに設定しなければならない。


MultiRow5.0J…もう嫌だ。

GrapeCityTool MultiRow5.0J セルの値が変更された場合のみCellValidatingイベントを発生させるには

標準ではセルの移動を行っただけでCellValidatingイベントが発生します。
ユーザによりセルの値が変更された場合のみ、CellValidatingイベントを発生させるようにします。

CellValidatingイベント内で変更後の値は e.FormattedValueで取得できます。
問題は変更前の値の取得方法です。
GetValueメソッドで取得すればいいのですが
下記の1では変更後の値が返ります。2では変更前の値が返ります。

1.以下の操作で発生するイベント
Enterキーで編集状態にする
セルに入力
Enterキーで編集状態を終了する
Tabキーでセル移動

CellEnter
CellBeginEdit
CellEditingControlShowing
CellParsing
CellValueChanged ※セルの値が変更された場合のみ発生する。
CellEndEdit
CellLeave
CellValidating
CellValidated

2.以下の操作で発生するイベント
マウスダブルクリックで編集状態にする
セルに入力
マウスクリックでセル移動
CellEnter
CellBeginEdit
CellEditingControlShowing
CellLeave
CellValidating
CellParsing
CellValueChanged ※セルの値が変更された場合のみ発生する。
CellValidated
CellEndEdit



CellValidatingイベント内でGetValueメソッドで取得する値はあてにならないので
CellEnterイベントでインスタンス変数に変更前の値を退避しておき、
CellValidatingで退避した変更前の値と変更後の値を比較し、ユーザによる変更があったかどうかを判定します。

Public Class CustomMultiRowSheet
Inherits GrapeCity.Win.MultiRow.GcMultiRow

Private _OriginalValue As Object

Protected Overrides Sub OnCellEnter(ByVal e As GrapeCity.Win.MultiRow.CellEventArgs)
'変更前の値を記憶します。
Me._OriginalValue = Me.GetValue(e.RowIndex, e.CellIndex)
MyBase.OnCellEnter(e)
End Sub

Protected Overrides Sub OnCellValidating(ByVal e As GrapeCity.Win.MultiRow.CellValidatingEventArgs)
'OnCellEnterイベントで記憶した値と比較します。
Dim isCellModified As Boolean
Dim currentValue As Object = e.FormattedValue
If Me._originalValue Is Nothing Xor currentValue Is Nothing Then
isCellModified = True
ElseIf Me._originalValue IsNot Nothing And currentValue IsNot Nothing Then
If Me._originalValue.ToString <> currentValue.ToString Then
isCellModified = True
End If
End If

If isCellModified Then
MyBase.OnCellValidating(e)
End If
End Sub

End Class

GrapeCityTool MultiRow5.0J ユーザによるセルの変更を判定するには

Ver4.0にはユーザによるセルの変更を判定するModifiedプロパティがありましたが
Ver5.0にはありません。

実現するにはCellValueChanged イベント を監視し、IsCurrentCellDirtyで変更がコミットされた場合にフラグを立てます。
CellValueChangedイベントはユーザによりセルの値が変更された場合に発生します。
Public ClassCustomMultiRowSheet
Inherits GrapeCity.Win.MultiRow.GcMultiRow

Private _IsModified As Boolean

Public ReadOnly Property Modified() As Boolean
Get
Return Me._IsModified
End Get
End Property

Protected Overrides Sub OnCellValueChanged(ByVal e As GrapeCity.Win.MultiRow.CellEventArgs)
If Me.IsCurrentCellDirty Then
Me._IsModified = True
End If
MyBase.OnCellValueChanged(e)
End Sub

End Class



しかし、ユーザー定義セルでOnCellFormattingメソッドで書式設定などを行うとCellValueChangedイベントが発生するため、
上記の方法ではユーザによるセルの値の変更を判定できません。
そのため、OnCellEnterメソッドで変更前の値を記憶しておき、OnCellEndEditで変更後の値と比較すると
ユーザによるセルの変更を判定できます。
Public ClassCustomMultiRowSheet
Inherits GrapeCity.Win.MultiRow.GcMultiRow

Private _OriginalValue As Object
Private _IsModified As Boolean

Public ReadOnly Property Modified() As Boolean
Get
Return Me._IsModified
End Get
End Property

Protected Overrides Sub OnCellEnter(ByVal e As GrapeCity.Win.MultiRow.CellEventArgs)
'変更前の値を記憶します。
Me._OriginalValue = Me.GetValue(e.RowIndex, e.CellIndex)
MyBase.OnCellEnter(e)
End Sub

Protected Overrides Sub OnCellEndEdit(ByVal e As GrapeCity.Win.MultiRow.CellEndEditEventArgs)
'OnCellEnterイベントで記憶した値と比較し、セルの値が変更されていれば
'ユーザがセルの値を変更したかどうかのフラグを立てます。
'すでにフラグがたっている場合は比較を行いません。
If Me._isModified = False Then
Dim currentValue As Object = Me.GetValue(e.RowIndex, e.CellIndex)
If Me._originalValue Is Nothing Xor currentValue Is Nothing Then
Me._isModified = True
ElseIf Me._originalValue IsNot Nothing And currentValue IsNot Nothing Then
If Me._originalValue.ToString <> currentValue.ToString Then
Me._isModified = True
End If
End If
End If

MyBase.OnCellEndEdit(e)

End Sub

End Class

GrapeCityTool MultiRow5.0J ユーザ定義セル

MultiRow5.0j 数値型セルぐらいは用意して欲しかったなぁ
それともInputManが抱き合わせで売れるようにワザと用意しないのかい?

InputManを買うお金もないので、数値型セルを自分で作りました。


数値型セルの要件


・マイナス値は赤字で表示。
・入力は数値のみ。貼り付けも数値のみ。
・最小値と最大値の範囲でのみ入力可。
・編集状態が終了したタイミングで3桁ごとにカンマ区切りにする。
・小数点以下桁数の設定。


準備


・ユーザー定義セルをツールボックスに登録するには、MultiRow for Windows Forms 5.0J Service Pack 1 (5.0.2008.0930)が必要です。
Sp1を適用していない場合は適用します。

・ヘルプの以下の箇所を読みましょう。
[製品の概要]
-[Service Pack 1 の変更点]
-[デザイナのツールボックスのカスタマイズ]
-[ユーザー定義セル]
-[デザイナのツールボックスに登録する]



ユーザ定義セルを作成するクラスライブラリの作成


以下に記述したカスタム編集エディタクラスとカスタムセルクラスは、クラスライブラリとして作成しますので
まずは新規プロジェクトでクラスライブラリを作成します。


数値型編集エディタクラスの作成


エディタクラスはカスタムコントロールの作成要領とほとんど同じです。
数値型編集エディタはGrapecity.Win.MultiRow.TextBoxEditingControlを継承して作りました。
「数値型セルの要件」で必要なプロパティなどを実装します。
KeyPressなどで数字以外の文字を弾く処理を記述します。
ペーストで数字以外を貼り付けできないよう処理を記述します。


数値型セルクラスの作成


数値型編集エディタクラスを編集エディタにもつ、数値型セルクラスです。

・Grapecity.Win.MultiRow.TextBoxCellを継承します。

・EditTypeプロパティで数値型編集エディタクラスのTypeオブジェクトを返します。
Public Overrides ReadOnly Property EditType() As System.Type
Get
Return GetType(CustomNemericTextBoxEditingControl)
End Get
End Property


・数値型編集エディタクラスに作成したカスタムプロパティは、数値型セルにも同じプロパティを用意します。

・セルのCloneメソッドでカスタムプロパティの値を複製します。
よくある質問:ID11562より
実行時に GcMultiRow コントロールに追加されるセルは、GcMultiRow.Template.Row.Cells プロパティのセルのインスタンスのクローンとなります。
このため、ユーザー定義型セルを作成してカスタムプロパティを実装している場合、Clone メソッドでプロパティの値を複製する必要があります。
Public Overrides Function Clone() As Object
Dim cell As CustomNumericTextBoxCell 
cell = DirectCast(MyBase.Clone, CustomNumericTextBoxCell )
cell.CustomProperty1 = Me._CustomProperty1 
Return cell
End Function


・InitializeEditingControlメソッドでエディタに設定する値やプロパティを設定します。
InitializeEditingControlメソッドはセルが編集状態になる場合に発生します。
このメソッドの引数ByVal formattedValue As Objectがエディタの値になります。
この値はセルに表示されている書式変換後の値なので、この値よりカンマ区切りなどの書式を除去したり、エディタに表示する値を加工したりします。
またここでセルに設定されているカスタムプロパティの値を、エディタの同プロパティに設定します。

・OnCellFormattingメソッドでセルに表示する値の書式を設定します。
OnCellFormattingメソッドは表示用の文字列に変換される際に発生します。
このメソッドの引数ByVal e As GrapeCity.Win.MultiRow.CellFormattingEventArgsのValueプロパティの値が、セルに表示される値になります。
この値に対して、カンマ区切りなどの書式設定やマイナス値の前景色の設定を行います。


数値型セルをツールボックスに登録する


・数値型セルを含むdllを「マイ ドキュメント\GrapeCity MultiRow\MultiRow 5.0 CellTypes」に配置します。
Windows Vista/2008 の場合、
C:\Users\<ユーザー名>\Documents\GrapeCity MultiRow\MultiRow 5.0 CellTypes
Windows 2000/XP/2003 の場合、
C:\Documents and Settings\<ユーザー名>\My Documents\GrapeCity MultiRow\MultiRow 5.0 CellTypes

・Visual Studio でプロジェクトに「MultiRow 5.0 テンプレート」を追加するか、または既存のテンプレートを開いてデザイナを表示する。
デザイナのツールボックスを右クリックし、「ユーザー定義セルを再読み込み」を実行する。
ツールボックスの「ユーザー定義セル」グループにユーザー定義セルが表示されます。


ユーザ定義セルのdll参照


ツールボックスに表示されたユーザ定義セルをドラッグ&ドロップでテンプレートを作成しビルドすると、
bin/debuまたはbin/releaseにユーザ定期セルdllがコピーされます。
もしdllに変更があったら、プロジェクトのbin配下のdllを手動で変更しなければなりません。
それは面倒なので、参照設定でユーザ定義セルdllの参照先を変更しておくことをお勧めします。


以上 間違っていたらご指摘ください。


MultiRow5.0Jの調査はまだまだ山積みです。
使用感としてはVer4.0より確実に面倒が増えたと感じます。
慣れとかではなく、あきらかにコード量が増えました。
Ver4.0はカスタムセルの作成など複雑なことはできなかったけれど、その分簡単でした。
Ver5.0では複雑なことができるようになったけれど、簡単だったことまでが複雑になった感じがします。

ですがその前に…
VerUpするなら互換性をキープしてほしい!!
まじで勘弁して欲しい。
あとヘルプ。
ユーザ定義セルをサポートしてるんだからInitializeEditingControlメソッドとかちゃんとヘルプに載せて下さい。

余談ですが
GcMultiRowクラス、その他編集エディタを使用するセルはすべてラップクラスを用意することをお勧めします。
出たばっかりですからね。やっぱ色々と細かい問題があります。

GrapeCityTool MultiRow5.0J 注意点

Enabled=Falseに設定するときの注意点


Enabled=Falseに設定しても、そのセルは選択できる。
セルを選択できないようにしたい場合は、Enabledプロパティ設定時にSelectableプロパティも設定する。


ボタンセルをEnabled=Falseに設定するときの注意点


Enabled=FalseかつSelectable=Falseにしているボタンセルで、セルをクリックすると
CellContentClickが発生する!Σ( ̄ロ ̄lll)
CellContentClickではそのセルのEnabledをチェックしてからボタンのクリック処理を記述すること。

GrapeCityTool MultiRow5.0J 基本

セルに値を設定するには


Dim iCurRow As Integer = Me.GcMultiRowSheet.CurrentRow.Index
Dim value As Object = "XXX"
Me.GcMultiRowSheet.SetValue(iCurRow,"textBoxCell1", value )
※Itemプロパティを使用して値を設定するとパフォーマンスが悪い。
Me.GcMultiRowSheet.Item(iCurRow,"textBoxCell1").Value = value

セルの値を取得するには


Dim iCurRow As Integer = Me.GcMultiRowSheet.CurrentRow.Index
Dim value As Object = Me.GcMultiRowSheet.GetValue(iCurRow,"textBoxCell1")
※Itemプロパティを使用して値を設定するとパフォーマンスが悪い。
Dim value As Object = Me.GcMultiRowSheet.Item(iCurRow,"textBoxCell1").Value


ボタンセルの背景色を変更するには


ボタンセルのFlatStyleをSystem以外に設定する。
注意
FlatStyleをSystem以外に設定した場合、SelectionBackColor(選択色)をTransparentに設定しないと
ボタンセルにフォーカスした際に背景色が選択色(デフォルト紺)になってしまいます。


セルのイベント発生順序


1.以下の操作で発生するイベント
Enterキーで編集状態にする
セルに入力
Enterキーで編集状態を終了する
Tabキーでセル移動

CellEnter
CellBeginEdit
CellEditingControlShowing
CellParsing
CellValueChanged ※セルの値が変更された場合のみ発生する。
CellEndEdit
CellLeave
CellValidating
CellValidated

2.以下の操作で発生するイベント
マウスダブルクリックで編集状態にする
セルに入力
マウスクリックでセル移動
CellEnter
CellBeginEdit
CellEditingControlShowing
CellLeave
CellValidating
CellParsing
CellValueChanged ※セルの値が変更された場合のみ発生する。
CellValidated
CellEndEdit

CellValueChanged以降のイベントでセルの入力値をGetValueで取得できます。


セルの値を検証するには


CellValidatingイベントを使用します。
入力値はe.FormattedValueを使用します。
※上記のイベント発生順序の2番ではGetValueメソッドを使用して入力値を取得できません。
入力値が異常でセルを移動させたくない場合はe.CancelプロパティにTrueを設定します。
注意
WindowsControlのValidating同様にフォームの×ボタンやMe.CloseでCellValidatingイベントが発生します。
フォームを閉じるときにValidatingイベントを走らせないようにするにはなどの対策が必要です。

GrapeCityTool ElTanelle 4.0J

GrapeCity製のWorkSheet4.0の拡張コンボボックスエディタについてのメモです。


拡張コンボボックスをValueAsIndex=Falseにしていた場合、ValueMemberの「型」とDataFieldに設定するMappingの「型」を合わせないとエラーになります。

具体的に説明すると
拡張コンボボックスのValueMemberにはListItemクラスのCd(Object)が設定されています。
拡張コンボボックスを設定した1列目にはDataTableの「FIELD1」DataColumnが表示されます。
拡張コンボボックスのValueAsIndex=Falseを設定しないと拡張コンボボックスの選択値はIndexで選択されてしまいます。例えばFIELD1の値が「3」のとき、拡張コンボボックスのドロップダウンアイテムからインデックス3(=4つ目のアイテム)を選択しようとします。CD=3に該当するアイテムを選択したい場合はValueAsIndex=Falseを設定します。

'Form1クラスです。
Public Class Form1
  Private Sub Form1_Load(ByVal sender As Object, ByVal e As System.EventArgs) _
  Handles Me.Load
    '拡張コンボボックスのデータを作成します。
    Dim list As New List(Of ListItem)
    For idx As Integer = 1 To 10
      list.Add(New ListItem(i,"List_" & i.ToString)
    Next
    '拡張コンボボックスエディタを作成します。
    Dim cbo As New GrapeCity.Win.ElTabelle.Editors.SuperiorComboEditor
    With cbo 
      .DataSource = list
      .DisplayMember = "Name"
      .ValueMember = "Cd"
      .ValueAsIndex = False
    End With
    '拡張コンボボックスエディタをシートの1列目に設定します。
    Me.Sheet1.Columns(0).Editor = cbo  
    'シートに表示するデータを作成します。
    Dim sql As String = "SELECT FIELD1 FROM TABLE1"
    Dim connection As New System.Data.OleDb.OleDbConnection("接続文字列")  
    Dim adapter As New System.Data.OleDb.OleDbDataAdapter(sql, connection)
    Dim table As New DataTable
'このコードを入れないとエラーになります。
    table.Columns.Add(New DataColumn("FIELD1",GetType(Integer)))
adapter.Fill(table)
    'シートにデータを設定します。
    Me.Sheet1.DataSource = table
    Me.Sheet1.Columns(0).DataField = "FIELD1"
  End Sub
End Class


'コードとコードに対応する名前を保持するクラスです。
Public Class ListItem
Private _Cd As Object
private _Name As String
Public Sub New(cd As Object,name As String)
Me._Cd = cd
Me._Name = name
End Sub
Public Property Cd() As Object
Get
Return Me._Cd
End Get
Set(ByVal value As Object)
Me._Cd = value
End Set
End Property
Public Property Name() As String
Get
Return Me._Name
End Get
Set(ByVal value As String)
Me._Name = value
End Set
End Property
End Class