酒井法雄
norio@int21.co.jp
VisualBasic 6.0の新機能といえば,Webアプリケーション作成や,ADOとOLE DBの採用といった大きなところに目が行ってしまう.しかし,細かい言語仕様も使いやすく仕上がっている.ここでは,Visual Basic 6.0で強化された言語仕様を実際に使ってみて,その方向性を述べる.
| Visual Studio製品版の内容 |
|---|
先月号で,Visual Basic 6.0の新機能について概要をお知らせした.この時点では,TechEdで配布されたVisual Studio 6.0(なぜ97から91もバージョンが落ちたのか謎だが)プレリリース版を基にしており,製品版の構成についてはっきりとした内容がわからないままだった.
Visual Studio製品版が販売されているので,すでにお手になさった方も多いと思うが,あらためて製品版(Enterprise Edition,市販版が手に入らなかったので,筆者のもとに届いたUpgrade版)の内容を示しておく.
CD-ROM
マニュアル
オブジェクト階層表
Visual C++のMFC,Visual J++のWFC,サーバーおよびクライアントで使われるオブジェクトモデルの階層を示す表が,3枚付属している.
参考書?
セットアップ時の注意
セットアップ時には,IE4 SP1をインストールしてからということになる.Windows 95やWindows NT などのSP,DCOM for Windows 98などのモジュールも含めると,本当に何度もリブートを繰り返さないとインストールができないのは,大きな問題だ.
また,DLLなどの関係からと思われるが,既存のVisual Studio 97と同一環境にインストールできないことも気をつけなくてはならない.
ただし,一般に言われるほどIE4は調子悪くはない.怪しいActiveX技術を使わないで,単にWebブラウザとして考えれば,Netscape 3.xあるいは4.xより調子がよいくらいである.ただし,Active Desktopだけはインストールしない方がよい.この原稿を書いているマシンでも,たったさっきおかしなことが起き,原稿が飛んで書き直している.
早くもSP1
ところでこの製品版だが,TechEd版で発見した数々のおかしな動作がまったく直っていなかった.バイナリコンペアしたところ,実はほとんど同じモジュールであった.一体この数ヶ月,Microsoftは何をしていたのかと思ってしまう.しかし,現在では国際的にソースコードが共用されており,過去のように発売の遅れた日本語版のみバグフィックスしてあるというようなことはやりにくくなっているのだろう.
そういうわけで,すでにMicrosoftのWebサイトには,SP1の告知が出ている.本誌が発売された頃には,正式にリリースされているかもしれないので,ご注意いただきたい(特集末を参照:編集部).では,今回の本筋である言語仕様について述べてゆこう.
| 言語機能の新機能と強化点 |
|---|
VisualBasic 6.0の言語仕様には,今までにないとても素晴らしい斬新な新機能があるわけではない.しかし,現実的な開発を考えると,なかなかシブい改良がなされているといえる.大きく分けると,次のような新機能や強化点がある.
以下に,それぞれの項目について,具体的なサンプルとともに紹介しよう.
| データ妥当性の検査関係 |
|---|
従来,ダイアログの上に配置されたテキストボックスなどの内容が変更されたとき,その内容やフォーマットが妥当なものであるかを調べるには,基本的には[OK]ボタンが押されたときにチェックするのが一般的だった.しかし,ものによってはそうではなく,リアルタイムにチェックし,誤った内容のときにはメッセージを出し,再入力を促した方が操作性が向上するものもあった.そのようなときには,LostFocusイベントで,次のようなコードを記述するのが一般的であった.
Private Sub txtName_LostFocus()
If txtName.Text = "" Then
MsgBox "名前を入れてください.", vbCritical, "入力項目不足(LostFocus)"
txtName.SetFocus
End If
End Sub
SetFocusメソッドが必要なのは,実際にはLoatFocusイベントはフォーカスが移動後に発生するからである.つまり,一度次のコントロールにフォーカスが移動してから,戻るということになる.すると,移動先のコントロールでもこのようなチェックをしていると,チェックがお互いに働いて無限ループ状態になってしまう.これを避けるためには,LostFocusでフラグを立てて,GotFocusで実際のチェックをするなど,複雑怪奇になことになってしまう.これはスマートではない.また,こういったフォーカス移動時にメッセージボックスを出すと,IMEModeが正しく動作しないなどの弊害が発生することもあったし,ある程度以上の数のコントロールでこれをやり出すと,動作がおかしくなることもあった.そこで,私たちはフォーカス移動に関わるイベントに依存したコードはなるべく書かないようになったのである.
Visual Basic 6.0では,こういった妥当性チェックをするために,Validateイベントが用意された.このイベントは,CausesValidationプロパティがTrue(デフォルト)のとき,LostFocusイベントが発生する直前に起きる.したがって,フォーカス移動に入る前に美しくチェックができるというわけだ(図1).
また,FormのValidateControlsメソッドを実行すると,フォームを終了する前に,フォームに配置された最後のコントロールの内容を妥当性検査する.ということになっているが,この意味がわからなかった.たぶんそういうことかなというコードをリスト1のように書いてみたが,どうも正しく動作しているようには思えない.見つけられなかっただけかもしれないが,新機能については,親切なヘルプやドキュメントがあった方がよいと感じた.
リスト1:Validateのサンプルコード
Private Sub cmdOK_Click()
Me.ValidateControls
Unload Me
End Sub
Private Sub txtAddr_Validate(Cancel As Boolean)
If txtAddr.Text = "" Then
MsgBox "住所を入れてください.", vbCritical, "入力項目不足"
Cancel = True
End If
End Sub
Private Sub txtFAX_Validate(Cancel As Boolean)
If txtFAX.Text = "" Then
MsgBox "FAX番号を入れてください.", vbCritical, "入力項目不足"
Cancel = True
End If
End Sub
' txtName のみ CausesValidation プロパティがFalse.古い書き方
Private Sub txtName_LostFocus()
If txtName.Text = "" Then
MsgBox "名前を入れてください.", vbCritical, "入力項目不足(LostFocus)"
txtName.SetFocus
End If
End Sub
Private Sub txtTEL_Validate(Cancel As Boolean)
If txtTEL.Text = "" Then
MsgBox "電話番号を入れてください.", vbCritical, "入力項目不足"
Cancel = True
End If
End Sub
|
|
コントロール動的な追加とプロパティ/ メソッドへの文字列でのアクセス |
|---|
従来は,コントロール配列を使って動的にコントロールをフォーム上に配置することができた.しかし,そのためにはまったく何もないのではダメで,元となるコントロール配列,すなわちindexプロパティが「0」などに設定されたものがひとつはなくてはならなかった.どんなコントロールでも動的に配置するというわけにはいかなかったのである.
コントロールの動的追加
Visual Basic 6.0からはこれが可能になっている.一般的には,次のような手順で行なう. まずコントロールのオブジェクトを宣言する.
Private oText As Object
Controlsコレクションに新たに追加されたAddメソッドを実行して,コントロールをインスタンシングする.このとき,コントロールのタイプとコントロールのオブジェクト名を指定する.コントロールのタイプは,[ライブラリ名].[クラス名]のカタチになる.コマンドボタンであれば,「VB.CommandButton」となるが,RichTextコントロールなどの外部コントロールのときには,「RichText.RichTextCtl」のような名前になる.
Set oText = Controls.Add("VB.TextBox", "txtHello")
プロパティは,次のように設定する.
With oText
.Left = 200
.Top = 100
.Width = 2000
.Height = 400
.Text = "Hello World!"
.Visible = True
End With
標準コントロールのときにはこれでもよいが,カスタムコントロールのときには,ライセンスキーが必要となるものがある.これは,事前にライセンスキーを取得しておく必要がある.次に,ライセンスキーをファイルに書き込む例を示す.これは,Licenses.Addメソッドの戻り値がそのままライセンスキーを返すことを利用している.
Private Sub GenerateLicenseKey() Dim intFile As Integer intFile = FreeFile ' ライセンスキーを書き込む ' ファイルを開きます Open "c:\Temp\Ctl_Licenses.txt" For Output As #intFile Dim strLicense As String strLicense = Licenses.Add( "prjWeeks.WeeksCtl") ' ファイルにライセンスキーを ' 書き込みます Write #intFile, strLicense Close #intFile End Sub
ライセンスキーを実行時に指定するには,次のようにLicensesコレクションにコントロールのタイプとライセンスを指定する.
Licenses.Add "RichText.RichTextCtrl", "qhj ZtuQha;jdfn[iaetr"
また,このような外部コンポーネントを使用するときに注意しなくてはならないのは,プロジェクトのプロパティの「実行可能ファイルの作成」タブにある「使用しないActiveXコントロールについての情報を削除する」という項目のチェックを外しておくことだ.この機能はVisual Basic 6.0からの新機能であり,チェックしておくと非常に便利な機能なのだが,このように動的にローディングすることができなくなる.もちろん,使用するコンポーネントはあらかじめ[Ctrl+T]でプロジェクトに加えておく必要がある.
これですべて解決できたような気がするが,実はまだ問題はある.というのも,このままではコントロールは表示できたものの,そのイベントを取得できないのである.
このようなときには,With Events キーワードを使ってスペシフィックなオブジェクト変数を宣言すればよい.しかし,せっかく動的に読み込めるというのに,イベントに対応できるようにコードはあくまでもスペシフィックな型をハードコーディングしておかなくてはならないというのは,いささか片手落ちの感がある.
Private WithEvents oDtpX As DTPicker
' DTPicker
Set oDtpX = Controls.Add("MSComCtl2.DTPicker", "dtpX")
With oDtpX
.Left = 200
.Top = 600
.Width = 1500
.Height = 400
.Visible = True
End With
Private Sub oDtpX_Change()
oText.Text = WeekdayName(oDtpX.DayOfWeek)
End Sub
なお,コントロールのインスタンスを削除するためには,ControlsコレクションのRemoveメソッドを使う.
Controls.Remove oControl
同時にライセンス情報も削除する.
Licenses.Remove vControlType
これらの機能を使ったサンプルのコードと実行例(リスト2,図2)を示す.
リスト2:コントロールの動的追加を行なうサンプルのコード
Option Explicit
Private WithEvents oCmdOK As CommandButton
Private WithEvents oDtpX As DTPicker
Private oText As Object
Private Sub Form_Load()
' TextBox
Set oText = Controls.Add("VB.TextBox", "txtHello")
With oText
.Left = 200
.Top = 100
.Width = 2000
.Height = 400
.Text = "Hello World!"
.Visible = True
End With
' DTPicker
Set oDtpX = Controls.Add("MSComCtl2.DTPicker", "dtpX")
With oDtpX
.Left = 200
.Top = 600
.Width = 1500
.Height = 400
.Visible = True
End With
' Command Button
Set oCmdOK = Controls.Add("VB.CommandButton", "cmdOK")
With oCmdOK
.Left = 3000
.Top = 100
.Width = 1000
.Height = 400
.Caption = "OK"
.Default = True
.Visible = True
End With
End Sub
Private Sub oCmdOK_Click()
MsgBox "Clicked!"
End Sub
Private Sub oDtpX_Change()
oText.Text = WeekdayName(oDtpX.DayOfWeek)
End Sub
|
図2:動的なコントロールの追加を行なうサンプル
|
|
CallByName関数
動的に読み込んだコントロールに限らず,ハードコーディングしないでプロパティにアクセスしたいことがある.このようなときに威力を発揮するのが,CallByName関数である.これは,プロパティやメソッドの名前を示す文字列を使って,そのプロパティまたはメソッドにアクセスできるというものだ.
Private Sub Form_Load()
Dim i As Integer
lstProps.AddItem "BackStyle"
lstProps.AddItem "BorderStyle"
lstProps.AddItem "BorderWidth"
lstProps.AddItem "FillStyle"
lstProps.AddItem "Shape"
lstProps.AddItem "Move"
For i = 0 To 10
lstValues.AddItem CStr(i)
Next i
End Sub
[Get Prop][Let Prop][Method]ボタンが押されると,それぞれ次のようなコードが走る.
Private Sub cmdGetProp_Click()
On Error Resume Next
lstValues.ListIndex = CallByName(Shape1, lstProps.Text, VbGet)
If Err Then
MsgBox Err.Description, vbCritical, "Error"
End If
End Sub
Private Sub cmdLetProp_Click()
On Error Resume Next
CallByName Shape1, lstProps.Text, VbLet, lstValues.ListIndex
If Err Then
MsgBox Err.Description, vbCritical, "Error"
End If
End Sub
Private Sub cmdMethod_Click()
On Error Resume Next
CallByName Shape1, lstProps.Text, VbMethod, 0, 0, 4000, 2000
If Err Then
MsgBox Err.Description, vbCritical, "Error"
End If
End Sub
|
図3:CallByName関数を使用するサンプル
|
CallByName関数は,コントロール名,プロパティ名,vbLet/vbGet/vbMethod,設定値を引数としてもち,プロパティやメソッドにアクセスすることができる.このコードでは,Shapeコントロールの【みかけ】を変化させるプロパティやメソッドをリストボックスの内容から設定できるようになっている. 少々残念なのは,メソッドの引数は可変であり,この部分はリストとなる文字列や配列をなんらかのカタチで渡せるとよいのだが,それができないことだ.
|
パブリックメソッドでの ユーザー定義型のサポート |
|---|
|
従来,COMの制限から,クラスおよびActiveXコンポーネントのメソッドにはユーザー定義型を渡すことができない.ところがどうしたわけか,Visual Basic 6.0からはこれが可能になっている. ただし,制限事項として,すべてのプロジェクトのすべてのモジュールのどのプロシージャからも参照できるユーザー定義型を宣言するときのみ,Publicキーワードを指定することができる.これは,同一プロジェクト内のクラスで定義されたユーザー定義型を使うことはできないということだ.Publicではエラーになってしまうからだ.ただし,少々トリッキーだが,外部のモジュールでPublicに定義されたユーザー定義型のクラスを使うことはできる. また,外部のコンポーネントのユーザー定義型を知るためには,CreateObjectなどでレイトバインドしたのではいけない.必ず参照設定をしておく必要がある.参照設定さえしてあれば,MultiUseだろうがGlobal MultiUseだろうが問題なく動作する. ここでは,外部でユーザー定義された型を使うローカルクラスのメソッド,参照設定されたGlobal MultiUseの外部コンポーネント,参照設定されていない外部コンポーネント,参照設定されているMulti Useコンポーネントについて動作を検証した(図4). 結論からいえば,ユーザー定義型は外部でPublicで定義されていることと,参照設定されていることの2つの条件さえそろえば,きちんと動作させることができた.リスト3にコードを示す. 何はともあれ,ユーザー定義型をCOMコンポーネント間で使えるのは現実的な改良点である. |
図4:ユーザー定義型サポートを検証するサンプル
|
リスト3:ユーザー定義型サポートを検証するサンプル
|
|
関数の戻り値としての配列型,配列の代入, 配列を扱う文字列操作関数の強化 |
|---|
|
Functionプロシージャ,Propertyプロシージャなどで,配列を返すことができるようになった.これは,まとまったデータをやり取りするときには,非常に便利である.また,これにともなって,左辺に可変サイズの配列を指定し,1行で配列全体を代入することができるようになった. 図5は,標準モジュール中に作成した月名を格納した配列を返す関数呼び出し,同じくユーザー定義型の配列を返す関数呼び出し,クラスに作成した配列型プロパティのLetおよびGet,クラスに作成した配列を引数とするSub型メソッドの呼び出し,クラスに作成した配列を返すFunctionメソッドの呼び出しをそれぞれコーディングしたものである. このうち,プロパティのLetプロシージャのみ「配列には割り当てられません」というエラーが発生して動作しなかった.ほかについてはまったく問題なく動作した.リスト4にコードを示す. このソースでも,「sMN = s()」のように配列を1行で代入できるのは快感だ.ただし,配列型を返すFunctionでは,「GetMonthNames() = sMN()」のように記述すると,「代入式の左辺の関数呼び出しは,バリアント型またはオブジェクト型の値を返さなければなりません」というエラーになってしまう.このときには,「GetMonthNames = sMN()」のように左辺にカッコをつけなければOKだ. |
図5:配列の拡張
|
Filter関数
Filter関数は,元となる文字列配列中から,指定されたフィルタ条件に合致した要素だけの配列を返すものだ.これも配列を1行で代入できるようになったことと組み合わせると強力だ.
' 初期化
Dim i As Integer
For i = 1 To 12
sSrc(i) = Format(DateSerial(0, i, 0), "mmmm")
Next I
' 抜きだし
Dim sRet() As String
sRet = Filter(sSrc(), txtValue.Text, -chkInclude.Value, cboCompare.ListIndex)
Filter関数の引数は,元になる配列,キーワード,合致するかしないかの指定,コンペアモードである.ちなみに,バイナリモードでは「BER」は合致せず,テキストモードでのみ合致する,データベースに格納されている設定は,このように素のVisual Basicで使ったときには無効でありエラーになる.すべてのコードをリスト5に示す.
|
図6:Filter関数を利用するサンプル
|
リスト5:Filter関数を利用するサンプルコード
Option Explicit
Dim sSrc(1 To 12) As String
Private Sub cmdFilter_Click()
Dim sRet() As String
Dim i As Integer
lstResult.Clear
On Error Resume Next
sRet = Filter(sSrc(), txtValue.Text, -chkInclude.Value, cboCompare.ListIndex)
If Err Then MsgBox Err.Description, vbCritical, "Error"
For i = 0 To UBound(sRet)
lstResult.AddItem sRet(i)
Next i
End Sub
Private Sub Form_Load()
Dim i As Integer
For i = 1 To 12
sSrc(i) = Format(DateSerial(0, i, 0), "mmmm")
lstMonth.AddItem sSrc(i)
Next i
'cboCompare.AddItem "Option Comareの設定"
cboCompare.AddItem "バイナリーモード"
cboCompare.AddItem "テキストモード"
cboCompare.AddItem "データベースに格納されている設定"
cboCompare.ListIndex = 1
End Sub
|
Replace関数/Split関数/Join関数
これらの関数は,それぞれ,次のような機能をもつ.
【Replace関数】 指定された文字列を1対1対応に置換する
【Split関数】 文字列を指定された1文字のデリミタでトークンの配列に切り出す
【Join関数】 Splitとは逆に,配列の各要素となる文字列を繋げてひとつの文字列にする
|
ここでは,これら3つの関数を使って,次のようなプログラムを作ってみた(図7). 一番上のテキストボックスには,「,,スペース().」などが含まれる文章がある.ここから,これらの文字をデリミタとして文章を単語に分割したいとしよう. まずは,Replace関数で,これらのデリミタ文字をすべて「,」に置換する.これは次のようなコードになる.Replace関数の引数は,元の文字列,検索文字列,置換文字列,文字列の検索スタート位置,置換する文字列の数(-1で全部),コンペアモードである.
Private Sub cmdReplace_Click()
Dim i As Integer
For i = 1 To Len(txtConv.Text)
txtSrc.Text = Replace( _
txtSrc.Text, Mid(txtConv.Text, i, 1), _
txtDelm.Text, 1, -1, vbTextCompare)
Next i
End Sub
次に,Split関数を使って,「,」をデリミタとして文字列を単語(?)に分解し,配列に格納する.ちょっと間抜けかもしれないのは,「x,,y」のようなものは,「x」「」「y」といった具合に空白文字列の要素にされてしまうことだ.しかし,考えてみればこれは正しい仕様である.
Private Sub cmdSplit_Click()
Dim sTmp() As String
Dim i As Integer
lstItems.Clear
sTmp() = Split(txtSrc.Text, txtDelm.Text)
' デリミタ文字は1文字しか
' 指定できないので,次の行はNG
'sTmp() = Split(txtSrc.Text, "()", _
' txtDelm.Text, -1, vbTextCompare)
For i = 0 To UBound(sTmp)
lstItems.AddItem sTmp(i)
Next i
' デリミタ文字が続くと空白の要素が返る
End Sub
こうして分解されたsTmp()配列の中身を,今度はJoin関数を使い,区切り文字「,」を使って繋げてひとつの文字列にする.
Private Sub cmdJoin_Click()
Dim i As Integer
Dim sTmp() As String
For i = 0 To lstItems.ListCount - 1
ReDim Preserve sTmp(i)
sTmp(i) = lstItems.List(i)
Next i
txtJoin.Text = Join(sTmp, txtDelm2.Text)
End Sub
|
図7:Replace, Split, Joinの3つの関数を利用したサンプル
|
このように,これら3つの関数は,文字列と配列をうまく使うためのもので,他の関数などをさらにうまく組み合わせれば,文の解析など文字列を扱う上で非常に便利な機能である.
|
柔軟なファイル操作を実現するFileSystemObjectオブジェクト/ 連想配列を扱うDictionaryオブジェクト |
|---|
ファイルシステムの情報を得たり,アクセスしたりするほか,テキストファイルへのアクセスができるようになった.Dictionaryオブジェクトは,FileSystemObjectと同じモジュールに含まれているもので,連想配列を扱うためのオブジェクトである.これらはWSH(WindowsScriptingHost)に含まれていたオブジェクトであり,詳細は本誌10月号で紹介したのでご参照いただきたい.
| 逆さま系文字列操作関数 |
|---|
InstrRev関数文字列の最後から指定した文字列位置を得るための関数だ.フルパス名からのファイル名部分だけの取得などに使うと大変便利である.実行例を図8に示す.
Private Sub cmdInstrRev_Click()
Dim iPos As Integer
iPos = InStrRev(txtFullPath.Text, "\", -1, vbBinaryCompare)
lblPath.Caption = Left(txtFullPath.Text, iPos - 1)
lblFile.Caption = Mid(txtFullPath.Text, iPos + 1)
End Sub
Private Sub cmdSelect_Click()
On Error Resume Next
cmdlgX.ShowOpen
If Not Err Then
txtFullPath.Text = cmdlgX.FileName
Else
txtFullPath.Text = ""
End If
End Sub
|
図8:InstrRev関数を使用したサンプル例
|
StrReverse関数文字列を逆さまにする関数である.何に使うのがいまいち謎であるが,とりあえずは図9のような回文チェックプログラムはスグに作れるから便利だ.
Option Explicit
Private Sub Form_Load()
txtSrc.Text = "たけやぶやけたかな"
End Sub
Private Sub cmdReverse_Click()
txtDst.Text = StrReverse(txtSrc.Text)
If txtSrc.Text = txtDst.Text Then
txtDst.ForeColor = QBColor(9)
Else
txtDst.ForeColor = QBColor(12)
End If
End Sub
|
図9:StrRevによる回文チェック
|
|
数値および日付型に対応した FormatXXX関数 |
|---|
数値や日付型は,コントロールパネルの設定通りに出すための関数が,次のような名前で新たに追加されている.
FormatCurrency関数:コントロールパネルで設定されたCurrency型にフォーマットする
| 構文:FormatCurrency(Expression[,NumDigitsAfterDecimal [,IncludeLeadingDigit [,UseParensForNegativeNumbers [,GroupDigits]]]]) |
FormatNumber関数:数値のフォーマットをする
| 構文:FormatNumber(Expression[,NumDigitsAfterDecimal [,IncludeLeadingDigit [,UseParensForNegativeNumbers [,GroupDigits]]]]) |
FormatPercent関数:パーセンテージのフォーマットをする
| 構文:FormatPercent(Expression[,NumDigitsAfterDecimal [,IncludeLeadingDigit [,UseParensForNegativeNumbers [,GroupDigits]]]]) |
このへんの関数はたいして難しいことはないので,詳しくはヘルプを参照してほしい(図10,リスト6参照).
FormatDateTime関数:日付や時刻のフォーマットをする
| 構文:FormatDateTime(Date[,NamedFormat]) |
MonthName関数:指定した月名を得る
| 構文:MonthName(month[, abbreviate]) |
WeekdayName関数:曜日を得る
| 構文:WeekdayName(weekday, abbreviate, firstdayofweek) |
実行例とコードを図11,リスト7に示す.
図11:FormatXXX(日付系)のサンプル
| DCOM対応やLoadPictureなど細かい改良 |
|---|
CreateObject関数の機能拡張CreateObject関数では,マシン名を省略可能な第2引数として指定できるようになった.APIのCoCreateInstanceExに対応したものであり,起動先のサーバーを指定できるので,DCOMなどの使用時には便利だ(図12). なお,リモートサーバーは設定によってサーバー上のウィンドウとして表面に見えてこないことが多い.本当に実行されているかは,タスクマネージャなどでプロセスがあることを確認して知ることができる.
Dim obj As Object
Private Sub cmdCreate_Click()
Set obj = CreateObject(txtClass.Text, txtMachine.Text)
End Sub
Private Sub cmdDestroy_Click()
Set obj = Nothing
End Sub
|
図12:CreateObjectの拡張.起動先サーバを指定できる
|
LoadPictureの機能拡張
サイズとカラーデプス(色数)を指定して読み込むことができるようになった.サムネールなど大量の絵を表示するときにはリソースを使わず便利だ… と一瞬思ったのだが,実はこれは.ICOと.CURファイルのときのみの機能であった.つまり大きいアイコン,小さいアイコン,Exprolerなどに表示されるアイコンなどの複数の大きさが選べるのである(リスト8,図13).
|
図13:LoadPictureの拡張
|
Round関数指定した小数点位置で丸める関数だ.図14では,指定された位置で丸める.なお,これは四捨五入ではなく,最近丸めなので注意されたい.
Option Explicit
Private Sub cmdRound_Click()
txtDst.Text = Round(Val(txtSrc.Text), _
Val(txtDecimalplace.Text))
End Sub
|
図14:Round関数の使用例
|
StrConv関数の機能拡張
LCID引数を指定し,システムのロケールIDと異なるロケールIDの文字列を指定できる.
|
StrConv関数:変換した文字列をバリアント型(内部処理形式StringのVariant)で返します 構文:StrConv(string, conversion, LCID) |
| わかりやすく面白い言語仕様の拡張 |
|---|
ざっと,Visual Basic 6.0で採用された新しい言語仕様を眺めてきたが,いかがだっただろうか.他の妙に新しい押しつけがましい概念や,インターフェイスや手続きに比較すると,実にわかりやすい.
実際,とくに役立つのが配列を返すようなタイプの関数であろう.プロパティプロシージャでLetできないといった難はあるが,サンプルで示したようにメソッドとして代入するメンバーを作れば逃げることができる.
もっとも,今までこんな機能はなくても確かにプログラミングできてはいたのではあるが… しかし,これらの新機能,とくに配列やユーザー定義型系の新機能は,ぜひいろいろと応用してみたいものである.ちょっとした心がけで,コーディングの効率や,コンポーネント化したときの効率を上げることができるハズだ.
|
Option Explicit
Private Sub cmdFormatCurrency_Click()
txtDst.Text = FormatCurrency(txtSrc.Text, Val(txtDecimal.Text), _
cboOptions(0).ListIndex - 2, _
cboOptions(1).ListIndex - 2, _
cboOptions(2).ListIndex - 2)
End Sub
Private Sub cmdFormatNumber_Click()
txtDst.Text = FormatNumber(txtSrc.Text, Val(txtDecimal.Text), _
cboOptions(0).ListIndex - 2, _
cboOptions(1).ListIndex - 2, _
cboOptions(2).ListIndex - 2)
End Sub
Private Sub cmdFormatPercent_Click()
txtDst.Text = FormatPercent(txtSrc.Text, Val(txtDecimal.Text), _
cboOptions(0).ListIndex - 2, _
cboOptions(1).ListIndex - 2, _
cboOptions(2).ListIndex - 2)
End Sub
Private Sub Form_Load()
Dim i As Integer
For i = 0 To 2
cboOptions(i).AddItem "[地域のプロパティ]を使用 (-2)"
cboOptions(i).AddItem "True (-1)"
cboOptions(i).AddItem "False (0)"
cboOptions(i).ListIndex = 0
Next i
End Sub
|
Private Sub cboFirstDay_Click()
On Error Resume Next
txtWeekDay.Text = WeekdayName(cboWeekDay.ListIndex + 1, -chkAbbreviate.Value, cboFirstDay.ListIndex)
End Sub
Private Sub cboMonth_Click()
txtMonth.Text = MonthName(cboMonth.ListIndex + 1, -chkAbbreviate.Value)
End Sub
Private Sub cboNamedFormat_Click()
txtFormatDate.Text = FormatDateTime(Now, cboNamedFormat.ListIndex)
End Sub
Private Sub cboWeekDay_Click()
On Error Resume Next
txtWeekDay.Text = WeekdayName(cboWeekDay.ListIndex + 1, -chkAbbreviate.Value, cboFirstDay.ListIndex)
End Sub
Private Sub chkAbbreviate_Click()
cboMonth_Click
cboWeekDay_Click
End Sub
Private Sub Form_Initialize()
Dim i As Integer
lblDateTime.Caption = FormatDateTime(Now) & " (" & _
MonthName(Month(Now)) & ") (" & _
WeekdayName(Weekday(Now)) & ")"
cboNamedFormat.AddItem "日付か時刻 or 両方"
cboNamedFormat.AddItem "長い形式の日付"
cboNamedFormat.AddItem "短い形式の日付"
cboNamedFormat.AddItem "時刻"
cboNamedFormat.AddItem "24時間形式時刻 (hh:mm)"
cboNamedFormat.ListIndex = 0
cboFirstDay.AddItem "NLS APIの設定値"
On Error Resume Next
For i = 1 To 12
cboMonth.AddItem MonthName(i)
cboWeekDay.AddItem WeekdayName(i) & " (" & CStr(i) & ")"
cboFirstDay.AddItem WeekdayName(i) & " (" & CStr(i) & ")"
Next i
cboMonth.ListIndex = 0
cboWeekDay.ListIndex = 0
cboFirstDay.ListIndex = 0
End Sub
|
リスト8:拡張されたLoadPictureを利用する
Option Explicit
Private Sub cboColor_Click()
File1_Click
End Sub
Private Sub cboSize_Click()
File1_Click
End Sub
Private Sub Dir1_Change()
File1.Path = Dir1.Path
End Sub
Private Sub Drive1_Change()
On Error Resume Next
Dir1.Path = Drive1.Drive
If Err Then
Beep
Drive1.Drive = Left(Dir1.Path, 2)
End If
End Sub
Private Sub File1_Click()
Dim sFileName As String
On Error GoTo trapLoadFile
If Right(File1.Path, 1) = "\" Then
sFileName = File1.Path & File1.FileName
Else
sFileName = File1.Path & "\" & File1.FileName
End If
' .CUR, .ICO ファイルでのみ有効だが,カスタムサイズなどうまくいかない
picDisp.Picture = LoadPicture(sFileName, cboSize.ListIndex, cboColor.ListIndex, Val(txtX.Text), Val(txtY.Text))
resLoadFile:
Exit Sub
trapLoadFile:
picDisp.Picture = LoadPicture()
MsgBox Err.Description, vbCritical, "Error"
Resume resLoadFile
End Sub
Private Sub Form_Load()
On Error Resume Next
cboSize.AddItem "vbLPSmall"
cboSize.AddItem "vbLPLarge"
cboSize.AddItem "vbLPSmallShell"
cboSize.AddItem "vbLPLargeShell"
cboSize.AddItem "vbLPCustom"
cboSize.ListIndex = 0
cboColor.AddItem "vbLPDefault (near)"
cboColor.AddItem "vbLPMonochrome (2)"
cboColor.AddItem "vbLPVGAColor (16)"
cboColor.AddItem "vbLPColor (256)"
cboColor.ListIndex = 0
End Sub
Private Sub txtX_Change()
File1_Click
End Sub
Private Sub txtY_Change()
File1_Click
End Sub