fc2ブログ
 

Technology へようこそ
ここは技術者の「経験」と「ノウハウ」のブログです


2010年11月08日

DataGridViewの表示連動

久々のスクラッチ開発は、Windows Formsベースの小さなツール。
一覧データの表示にDataGridViewコントロールを利用しているのですが、意外と色々できて思っていたよか便利だったり。
というわけで、2つのDataGridViewコントロールの表示(カラム幅と横スクロール位置)を連動させる方法をメモっときます。
明細と合計のグリッドを別々に配置したようなシーンで使えるかも、です。
'グリッドビューカラム幅変更イベント
Private Sub dgvData_ColumnWidthChanged( _
ByVal sender As Object, _
ByVal e As System.Windows.Forms.DataGridViewColumnEventArgs) _
Handles dgvData.ColumnWidthChanged

If dgvTotal.Columns.Count > e.Column.Index Then
dgvTotal.Columns(e.Column.Index).Width = e.Column.Width
End If

End Sub

'グリッドビュースクロールイベント
Private Sub dgvData_Scroll( _
ByVal sender As Object, _
ByVal e As System.Windows.Forms.ScrollEventArgs) _
Handles dgvData.Scroll

If e.ScrollOrientation = ScrollOrientation.HorizontalScroll Then
dgvTotal.HorizontalScrollingOffset = e.NewValue
End If

End Sub

[ posted by ken ]
スポンサーサイト





2010年04月28日

If 演算子

Visual Basic 2008 では
If 演算子はショートサーキット評価を使用し、2つの値のうちの1つを選択して返します。

例えば
Dim value As String = Nothing
Dim dt As DateTime = Nothing

'If 演算子の場合、システム日付が出力されます
dt = If(value Is Nothing, Now, CType(value, DateTime))
Console.WriteLine(dt.ToString("yyyy/MM/dd HH:mm:ss"))

'IIf 関数の場合、エラーとなります
dt = IIf(value Is Nothing, Now, CType(value, DateTime))
Console.WriteLine(dt.ToString("yyyy/MM/dd HH:mm:ss"))
If 演算子は IIf 関数と同じように機能しますが、If 演算子はショートサーキット評価により、
上記の If 演算子では、1番目 If 引数「value Is Nothing」が評価されて True の場合は
2番目の引数が評価されて値が返され、3番目の引数が評価されません。
1番目の引数の評価が False の場合は、2番目の引数を評価せずに3番目の引数を評価して
値が返されます。IIf 関数の場合は3つの引数をすべて評価する為、上記の IIf 関数では
3番目の引数「Ctype(value, DateTime)」がエラーとなってしまいます。

上記例のようなどちらかの値を取る場合で If 演算子と IIf 関数のどちらかを
使う場面が結構ありそうですが、問答無用で If 演算子を選ぶ気がします。

[ posted by kami ]


2010年02月06日

VBアプリの多言語対応

VB 2005で作成したWindowsフォームアプリの多言語対応化の作業をしています。
その昔、英語版業務パッケージの日本語ローカライズにちょっぴり関わったことはあるものの、当時はVB6の時代。しかも、その逆パターンはやったことがない上、今回のメインターゲットは中国語という代物なだけにプチハマり中。

まず最初に調べたのは、少し前にこのブログでも書いたことがある、中国語版WinXPでフォームやコントロールのサイズが変わってしまうという今回最大の懸念事項。
が、これが「フォームのAutoScaleModeプロパティをNone設定にする」という簡単な対策であっさり回避できることが判明して一安心。

次に多言語対応化のメイン作業となる、各種リテラル(固定文字列)の動的設定の方法。
リテラルというのは、いわゆるフォームやコントロールに設定されたキャプション群や、コード内にハードコーディングされているメッセージやログなどに使われる文字列群のことです。
元々日本語で記述されているこれらの文言をターゲット環境の言語に応じて切り替えてやる必要があるわけですが、.NET Frameworkには元々多言語対応の仕組みが用意されているので、その方法に従うというポリシーで調査開始。

結果的に重要キーワードとして浮かび上がってきたのは「カルチャ」と「リソース」。
普段から聞いたことはあるものの、あまりきちんと押さえていなかったりしがち、と思われる領域です。
あまりダラダラ書いても長くなってしまうので、これらのキーワードをベースに、具体的な操作を以下に挙げておきます。
ちなみに前述のコントロールキャプションなどは「UIリソース」と呼び、その他は「テキストリソース」と呼ぶことにします。

・UIリソースの多言語対応

まず、「フォームのLocalizableプロパティをTrue」にして、ローカライズ可能なフォームとします。次にLanguageプロパティでカルチャを変更することによって、UIリソースを各国語ごとに保存することが出来るようになります。普段フォームごとに自動生成される「*.resx」ファイルというのが実際にUIリソースが納められているファイルで、ローカライズした言語(カルチャ)の分だけresxファイルが作られることとなります。ローカライズの作業自体はVSのデザイナが使えるので特に難しいことはありません。(実際に各国語を翻訳できるかどうかは別問題w) また、こうして作られたリソースファイルはプロジェクトをビルドするとbinフォルダ配下にカルチャごとのサブフォルダに「*.resources.dll」という名前のファイルとなって配置されることになります。(サテライトアセンブリ)

・テキストリソースの多言語対応

前述のリソースファイルにはテキストリソースも含めることができます。リソースファイルに含まれたテキストはResourceManagerクラスを介して参照することが可能となります。ちなみにテキストリソースの元ネタは「Key=Value」の形式で記述されたプレーンなテキストファイルです。テキストリソースはVS上からプロジェクトに追加することも可能だと思いますが、今回は誰でも簡単にテキストリソースの変更が可能となるように、リソースビルド用のバッチファイルを用意しました。内容は.NETランタイムに付属するリソースファイルジェネレータ(Resgen.exe)というツールを使って、テキストファイルから*.resourcesファイルを作成、その後アセンブリリンカ(Al.exe・SDKに付属)でサテライトアセンブリ(*.resources.dll)を作成する、といった感じです。サテライトアセンブリはカルチャごとに存在しますので、これをカルチャごとに行なう必要があります。(本当はUIカルチャとテキストカルチャを別々のアセンブリに分離したかったのですが、どうにもうまくいかないようなので、UIリソースとテキストリソースをひとまとめにリンクしてビルドするようにしました) 前準備としてはこれだけですが、当然ソースコード内のリテラルを扱っている箇所はリソースから文字列を取得するような作りに変更しなければならないのは言うまでもありません。

と、まぁ、とりあえずこれだけやれば同一のバイナリが各言語版のWindowsでローカライズされた状態で動作するようになるはずです。
肝の部分は思っていたよりも簡単にできて若干拍子抜けな感はありますが、一緒に使っているCrystal Reportの文字列リソースなどはここまで賢い形にはなっておらず、かなり泥臭いことをする必要があったりするようなので、モノによっては枝葉の部分でハマることはあるかもしれません。

[ posted by ken ]


2009年09月11日

匿名型

Visual Studio 2008 VBでは匿名型が導入されています。
データ型のクラス定義を記述せずにオブジェクトを作成できます。
Objectから直接継承され、使用可能なクラス名がありません。

例) オブジェクト宣言時に指定プロパティを格納します。
Dim sample = New With {Key .code = 1, .note = "さんぷる"}

' Key を指定した code は読み取り専用
Console.WriteLine(sample.code)
Console.WriteLine(sample.note)

' Key 未指定の note は変更可能
sample.note = "変更できます"
Console.WriteLine(sample.note)
[出力結果]
1
さんぷる
変更できます
クラス定義の記述が不要なので便利そうです、
ただ複数のインスタンスの宣言が必要な場合は
あらかじめクラス定義を記述した方がよさそうです。
詳しくはMSDNを参照ください。

[ posted by kami ]


2009年07月01日

WindowsOSのバージョンを取得するAPI

Windows2000で使っていたVBのプログラムをWindowsXPで実行したところ、正しく動かない箇所が見つかり、修正が必要になりました。
しばらくの間は、Windows2000でもWindowsXPでも使用するので、どちらでも実行できるようなプログラムにする必要があります。

なかなか複雑なプログラムで、修正した時に何処に影響がでるか判らなかったので、OSのバージョンを判定し、Windows2000ならば既存の処理のままとし、WindowsXPの時には修正したロジックを通るように改修することになりました。

そこで、GetVersionEXというWindowsのAPI関数を利用しました。
この関数を使うとWindowsのバージョン番号が取得できます。
Windows2000のメジャーバージョンは”5”、マイナーバージョンは”0”。
WindowsXPのメジャーバージョンは”5”、マイナーバージョンは”1”。
この値を利用して、処理を分岐して対応完了!

Windows2003やVistaでもバージョン番号が取得できるので、さまざまなWindowsOSで使用する必要があるプログラムを作るのに重宝するかと思いアップしました。


[ posted by h.i ]


2008年12月28日

DPI設定を変えてみた


WindowsのDPI設定の違いによるWindowsフォームアプリケーションの挙動を調べてみました。

素材は日本語版Vista上のVB2005で適当に作ったフォームで、320x240の固定サイズにしています。
デフォルトの96dpiで動かすとこんな感じ。
Original

これをVPC上の英語版XPで動かしてみます。おなじく96dpi。
AutoScaleMode=None 96dpi
まぁ、ほぼ同じイメージですが、日本語版Vistaと英語版XPではシステムフォントが異なるため、若干サイズが変化しています。
画面上部のFormSizeをみると、縦サイズが微妙に広がっています。

それでは、DPI設定を変えてみましょう。120dpi(1.25倍)にしてみます。
AutoScaleMode=None 120dpi
見事にレイアウトが崩れました。
コントロールのサイズはモノによって変わったり変らなかったりですが、フォントサイズがことごとく大きくなっているため、はみだしまくりです。
フォームデザイン時に何も考えずデフォルトのままビルドするとこのようになります。

この現象を回避するために使うのが.NET Framework 2.0から追加された「AutoScaleMode」というフォームのプロパティ。
デフォルトでは「Inherit (=None)」になっているので、まずはこれを「Dpi」に変更してみます。
AutoScaleMode=DPI 120dpi
フォントだけ大きくなるのではなく、DPI設定を大きくしたぶん、フォームやコントロールも大きく表示されるようになりました。
320x240 でデザインしたはずのフォームが 398x304 になっています。
フォームいっぱいのサイズで背景イメージなどを貼る場合はストレッチ設定にしておかないとまずそうです。
でもって、よくみると、左上のラベルのフォントが1文字欠けているようです。

AutoScaleModeプロパティを「Font」にしてみましょう。
AutoScaleMode=Font 120dpi
フォントサイズに応じてスケーリングされる、というMSDNリファレンスの説明通り、今度はラベルの文字も欠けていません。
フォームサイズはさらに大きくなって 425x322 になりました。

DPIを変えても画面解像度自体は変わらず、アプリケーションのサイズのみが変わりますので、業務アプリにありがちな「画面解像度に合わせてフォームサイズを決める」といった設計はDPIを変更した環境においては全く意味を成さないということになります。

中国語版WindowsXPでは、DPI設定を変えていないにもかかわらず、フォームサイズが巨大化したりする現象も見られるなど、このあたりの挙動はちょっぴり謎めいた部分もありそうですが、
今までおざなり、というか、ほとんど意識されてこなかったであろうDPI変更によるフォームレイアウトの変化。
業務アプリにおいても、ある程度DPIを意識したデザイン、実装が必要なのかもしれません。

参考) MSDN - ContainerControl.AutoScaleMode プロパティ

[ posted by ken ]


2008年06月06日

DBNull

最近気づいたことです。
VB.NET(ASP.NET)からDBアクセスしてデータを画面へ
表示するなんてことは頻繁にあることだと思います。
DBから読込んだデータ内にはNULL値もあります。
こんなときSQL側で予めNULL値を返さないようにしたり
VB側でDBNullを判定して空文字に置き換えたりしていましたが、
ToStringを通すとNULL値(IsDBNull(値)=True)は
空文字に置き換わってくれるようです。
Dim dt As DataTable

'~データ取得処理~

'中身がNULL値だとエラー
txtName.Text = dt.Rows(0).Item("NAME")

'中身がNULL値でもエラーは発生せず
txtName.Text = dt.Rows(0).Item("NAME").ToString()
といった感じです。

いつもの癖で共通関数を通してアイテムを使用していたので
恥ずかしながら全くそんなことには気づきませんでした。

[ posted by kami ]


2008年05月30日

グラフコントロール

先日、Windowsフォームアプリで簡単なグラフを表示したいという要望を伺ったのですが、
悲しいことにVisualStudio標準付属のコントロールではグラフ表示はサポートされていません。

サードパーティー製のグラフコントロールはいくつも販売されているようですが、
個人的に「使ったら負け」的な想いが強かったりするので、はなから対象外。

以前、試しに少しだけ.NETアプリから触ってみたことがある「MS Chartコントロール」は
所詮レガシーなActiveXコントロールなので今更積極的に使いたくありません。

オープンソースのコンポーネント「ZedGraph」は、
以前他社のプログラマさんが利用しているのを見せてもらい好印象だったこともあり、
筆頭候補だったのですが、オープンソースは利用不可というお達しが出ているとのことであえなく却下。

では、VisualStudioに付属してくるCrystalReportはどうなのか、ということで
試しにチャートを出力させてみたのですが、あまりにも難しいデザイナの操作に挫折。
レポートビューア上にしか表示させられないのもしっくりこないので、これも却下。

比較的単純な棒グラフが表示できれば良いという話だったこともあって、
結局、グラフ描画を行なうコントロールを自作する方向で考えるという結論に至った次第。
試しにPictureBoxから派生させたカスタムコントロールを書いてみたわけですが、
案外簡単に実現できそうなことが分かって一安心。

グラフィック操作が比較的簡単に出来るようになったのも.NET Frameworkの恩恵のひとつですね。

20080530.jpg

[ posted by ken ]


2008年03月07日

VBによるExcelシートへの値設定

VBからExcelワークシートを作成するアプリを作成する仕事がしばしばあります。
DBからデータを取得してワークシートに展開していくわけですが、
セルに対して値をセットしていくやり方ひとつで処理速度が大きく変化します。

単純に1セルずつ値をセットしていく方法は時間がかかり、
配列を用いて一括でセットする方法が速い、ということは経験上分かっているのですが
はたしてどのくらい違いが出るのか、試してみました。

1) 100 行 x 10 列 = 1000セル
   セル単位12797ms
   行単位1391ms
   複数行列一括985ms
2) 1000 行 x 50 列 = 50000セル
   行単位14500ms
   複数行列一括3688ms
3) 10000 行 x 10 列 = 100000セル
   行単位154156ms
   複数行列一括12421ms
やはりセル数が多くなればなるほど速度差が顕著になるようです。
ちなみに「複数行列一括」でセットする場合はVB側は2次元配列(行, 列)のObject変数となります。
データ量にもよりますが、最低でも行単位でのデータセットを行なうべきでしょう。
値の代入だけではなく、セル参照が増えるとそれだけで遅くなる傾向があるので
極力セルを範囲指定で扱う形の実装を心がける必要があるようです。

[ posted by ken ]


2008年02月15日

ウィンドウサイズと位置の復元

ウィンドウサイズ可変の業務アプリケーションが増えることに伴い、アプリケーション終了時のウィンドウサイズ、ポジションなどを次回起動時に復元する、という仕組みを暗黙のうちに求められるケースが多くなってきました。

以前はINIファイルやレジストリにそのような情報を記録しておくことが多かったわけですが、.NET 2.0以降はアプリケーション構成ファイル(.config)と呼ばれるXMLファイルへの記録が推奨されています。

入れ物が変わってもやることは一緒なので、これらの違いについては触れませんが、ウィンドウ復元に関しては少しだけ注意する必要があります。
それは「WindowState」、つまり最小化や最大化といったウィンドウの状態です。
最小化、最大化された状態ではClientSizeやLocationといったFormのプロパティが特殊な値に変わってしまいますので、それをそのまま記録して復元しようとするとおかしなことになるわけです。
対策としては一旦WindowStateをNormalに戻してから記録するのが無難だと思いますが、最大化状態で終了させた場合には次に起動したときも最大化状態で再開させる、などといったより細かい制御が必要になるケースもあります。

[ posted by ken ]