.NETで色々なコントロールをReadOnlyにしたいときどうすれば。

.NETの標準コントロール群は、ReadOnlyプロパティがTextBoxBaseにしかなく、全コントロールにあるEnabledは前景色/背景色が強制的に変わってしまう。なんという残念なAPI設計。


いろいろ調べてあれこれ考えて、方針をまとめたのでメモ。

  1. コントロールは継承せず、標準コントロールを使う。
  2. TextBoxBase系はReadOnlyプロパティで制御する。
  3. 上記以外はWndProcでメッセージを補足して、ReadOnlyにする時はフォーカス禁止・キー入力禁止・マウスクリック禁止とする。
  4. コントロールのReadOnlyは専用のコンテナで制御する。


これでテキストボックスはコピーができる。
年末に実装を書いてみる。

VB.NET+Quill+S2Dao.NETで、データベース接続文字列を動的に変更する

via. http://d.hatena.ne.jp/senbei3/20090410


app.configのquillセクションにはデータベース情報()を記述する。そこにはDB接続文字列を書く訳で、ユーザー名とパスワードが必要。
ASP.NETだとweb.configを暗号化すればよいらしいけれどWindowsフォームアプリケーションではそうもいかない。
というわけで、app.configのDB接続文字列にはユーザー名とパスワードは書かずにおいて、アプリ実行時に動的に差し込む方法。当然DBアクセス前に行う必要がある。例えばメインフォームのLoadイベントでやる場合。


メインフォームのメンバ変数に以下を追加。

Protected _dataSourceDict As SelectableDataSourceProxyWithDictionary


メインフォームのLoadイベントに以下を記述。

'DIでデータソース辞書を取得
QuillInjector.GetInstance.Inject(Me)
'app.configのquillセクションで定義したデータソースを取得
Dim ds As DataSourceImpl = _dataSourceDict.DataSourceCollection("sampleDb")
'データベース接続文字列にユーザー名とパスワードを追加
ds.ConnectionString &= ";user id=username;password=mypass"


これで実行時にユーザー名とパスワードを動的に設定できる。

.NETの継承フォームで、Anchorを固定したコントロールの位置がズレる件の対策

2011/12/09追記:Protected にすると継承先でコントロールがロックされないので、用途によってはいまいち。Anchorの利用を諦めて、継承元のOnResizeをOverridesし、コントロールのSizeから表示位置を計算してLocation設定→Invalidateとしたほうがよい場合もある。


サポート情報にも書いてある(http://support.microsoft.com/kb/316560/ja)が、機械翻訳でわかりづらいのでVB.NETを例にメモ。

  1. ソリューションエクスプローラで「すべてのファイルを表示」をクリック。
  2. 継承元フォームのDesigner.vbを開く。
  3. ズレては困るコントロールのメンバをFriendからProtectedに変更する。

つまりButton1であった場合は、

    Friend WithEvents Button1 As System.Windows.Forms.Button

    Protected WithEvents Button1 As System.Windows.Forms.Button

と変更する。これで継承先フォームのサイズを変更しても、コントロール位置がズレなくなる。

DataGridViewにEntity/DTO入りのListを繋げてSort/Filterしたいので、ライブラリ探し&サンプル作成

S2Dao.NETを使ってて悩んだのはここ。
せっかくモデルクラスに閉じ込めて固くしたデータを、表示/編集のためだけにDataTableに展開するのは勿体ない。

.NETフレームワークには、ソート/フィルターするためのIBindingListViewインターフェースはあるが、これを実装したクラスはDataViewだけ。そしてDataViewはDataTable専用。標準フレームワークにDataObjectViewとか用意しといて欲しいぞ。

定石ライブラリはあるかと探したが見つからず。フル装備っぽいBindingListViewというクラスを提供するライブラリもある。が、ちょっと大きいか。
http://blw.sourceforge.net/


以下は最低限の機能を実装したサンプルのよう。
http://lemoncake.wordpress.com/2007/06/13/filtering-on-a-list-bindingsource/
これをVB.NET用に移植してみた。このままでは実用にはならない(BindingListから継承してるのでItems周りの制御を奪うのが大変。IBindingListを自分で実装すべきなんだろう)がサンプルとして。

続きを読む

Seasar.NETのQuillで独自インターセプターを書いてAOPしてみる

via. http://banban55.dip.jp/~ragnarok/blog/archives/cat3/index.html


AbstractInterceptorを継承して自前の差し込み処理クラスを作る。

Imports Seasar.Framework.Aop.Interceptors

Public Class MyInterceptor
    Inherits AbstractInterceptor

    Public Overrides Function Invoke(ByVal aInvocation As Seasar.Framework.Aop.IMethodInvocation) As Object
        Try
            '前処理をここに書く
            MsgBox("Before")

            'メソッドを呼び出す
            Dim result As Object = aInvocation.Proceed

            '後処理をここに書く
            MsgBox("After")
            Return result
        Catch ex As Exception
            Throw
        Finally
        End Try
    End Function

End Class


サービスクラスに差し込みクラスをアスペクトする。

Imports Seasar.Quill.Attrs

Public Class AbcServiceImpl
    Implements IAbcServices

    Protected dao As IAbcDao

    <Aspect(GetType(MyInterceptor))>
    Public Overridable Function GetAll(ByVal aEntity As AbcEntity) As System.Collections.IDictionary Implements IAbcServices.Execute
        Return dao.SelectAll()
    End Function

End Class


これでGetAll()を実行すると処理の前後にメッセージが表示される。とっても簡単。使いどころは色々ありそう。

S2Dao.NETで、EntityをCSVに出す程度ならS2DxoよりCallByName関数がいいかも?

件名通り。VB.NETをごりごり書き出して3ヶ月、今までCallByNameがあるなんて知らなかった・・・


CSVの見出しはEntityのプロパティとは違うので、そのマッピングはどうせ持つ。
それならCallByNameでEntityから値を切り出してCSVにすればいいじゃん。CSV出力程度の仕事で、Hashtableをわんさか作る必要はないよね。

VB.NET+Quill+S2Dxoにて、PONO->IDictionaryの変換でハマったのでメモ

S2DxoのドキュメントだとDXOインターフェースに定義するメソッドでIDictionaryをそのまま使うように見えるが、これだとインスタンスが生成できずに例外が出る。Hashtableを使えばOK。.NETに精通してれば当たり前なのかも。
以下サンプル。


エンティティが、

Public Class AbcEntity
    Property Field1 As Integer
    Property Field2 As String
End Class

データ交換オブジェクトインターフェースが、

Imports Seasar.Quill.Attrs

<Implementation()>
Public Interface IAbcEntityDxo
    <Aspect(GetType(Seasar.Dxo.Interceptor.DxoInterceptor))>
    Function ConvertToDictionary(ByVal aEntity As AbcEntity) As Hashtable
End Interface

サービスインターフェースが、

Imports Seasar.Quill.Attrs

<Implementation(GetType(AbcServiceImpl))>
Public Interface IAbcServices
    Function Execute(ByVal aEntity As AbcEntity) As Hashtable
End Interface

サービスクラスが、

Public Class AbcServiceImpl
    Implements IAbcServices

    Protected dxo As IAbcEntityDxo

    Public Overridable Function Execute(ByVal aEntity As AbcEntity) As IDictionary Implements IAbcServices.Execute
        Return dxo.ConvertToDictionary(aEntity)
    End Function
End Class

フォームにQuillControlとButtonを1つおいて、

Public Class Form1

    Protected service As IAbcServices

    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
        'エンティティを作って、
        Dim entity As New AbcEntity With {.Field1 = 1234, .Field2 = "Hello, world"}
        'プロパティ名=値、のハッシュテーブルに変換し、
        Dim dict As Hashtable = service.Execute(entity)
        'ハッシュテーブルの中身を表示してみる。
        For Each item As DictionaryEntry In dict
            MessageBox.Show(String.Format("Key={0}, Value={1}", item.Key, item.Value))
        Next
    End Sub
End Class

うまく動作しました。これで3時間も・・・くやしい!