MENU > HOME VB.NET VB6 VBA ソフトウェア リンク サイト情報
VB.NET> VB.NET 突撃レポート > 1  2  3  4  5  6  7  8  9  10  11  12 
PART11: なんと!オーナードローを標準サポート(2002年10月18日〜)

前回からかな〜りの月日が流れてしまいました。
もうすでにVS.NETを購入された方も多かったり、VS.NETでの案件も出てきているのではないかと思います。
私はというとあれから半年ほど本業の方に追われまくり、なんとか空いた時間を普段会えない息子たちとのコミュニケーションに当てていたため、ホームページの更新もサボりまくっていました。
最近やっと少し自由な時間もできはじめ、また今月からホームページの更新を再開したのでした。
なお、今回からちょっと気分を変えてTahomaフォントを使用してみることにしました。

更新が停滞していた言い訳はこんなところにして、前回まで標準コントロールの変更点を何点か挙げてきました。
要するに従来まで、コントロール(クラス)毎にばらばらだったプロパティやらメソッドやらイベントやらをすべて整理し、見直したということでしょうか。
例えば、リスト系のコントロールのComboBoxやListBox、ListView等ではリストアイテムを扱う場合、すべて同じItemコレクション の取り扱いとなり、1つ使用方法を憶えればすべてのコントロールで同じような記述ができるようになっていたりします。
さらに新しく便利な機能としてAnchorやDock(Part.10参照)なども追加されたと...。

で、今回のオーナードローも新しく追加された機能の1つです。
VB6でもオーナードローができないというわけではなかったのですが、ウィンドウのサブクラス化を行ったり、グラフィック系の処理だなんだかんだでWin32APIを使いまくる必要があり、それなりの知識を要求されていました。
VB.NETではComboBoxやListBox、Menu等でオーナードローを標準サポートしており、これらはAPIの知識はほとんど必要なく、すべて標準機能のみで行うことができ るようになっています。

今回はComboBoxでオーナードローに挑戦してみることにします。

ComboBoxのオーナードローといえば、Officeなんかで見られるようなフォント選択コンボでしょう!

みたいに各フォントがそれぞれのフォントで描画されているやつです。
(実は私はこれをVB6で試みたことがありますが、面倒で玉砕しかけたことがあります。)

とにもかくにも、これと同等のコンボボックスを目指してがんばってみることにしましょう。

まずはフォームにComboBoxを貼り付けて、リストにフォント名を格納することが先決です。

VB6ではFontの列挙はこんな感じでやっていましたね。
Private Sub Form_Load()
    Dim i As Integer
    For i = 0 To Screen.FontCount
        Combo1.AddItem Screen.Fonts(i)
    Next
End Sub

じゃあVB.NETではどうかというと、Screenオブジェクトの仕様が変更されており、同じようなコードではFont名を取得することができません。

Microsoft Visual Studio .NET ドキュメント(MSDNライブラリ)で関連情報を検索してみると、「Screenオブジェクトの変更点」というトピックを見つけました。
VB.NETでVB6のScreen.Fontsに対応する項目はSystem.Drawing.FontFamiliesと書いてあります。
このSystem.Drawing.FontFamiliesのFamiliesメンバ配列に使用可能なFontの一覧が格納されているとのこと。

早速コードを書いてみます。

 
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
    Dim ff As FontFamily
    For Each ff In FontFamily.Families
        ComboBox1.Items.Add(ff.Name)
    Next
End Sub
それでは実行してみましょう!

きちんと動作しましたね。(実は「Visual Basic.NETにおけるフォントの変更点」ってトピックにそのままの例が掲載されている。)

次にいよいよ本題のオーナードローを行う手順ですが、
まずはプロパティウィンドウでDrawModeプロパティをNormalからOwnerDrawFixedに変更します。
(Win32APIでリストウィンドウに対し CBS_OWNERDRAWFIXEDフラグを付加していたのと似ています。)

実は前準備はこれで完了です。
このままプロジェクトを実行してみると、リストに何も表示されない状態になります。
はいそうです。そこに文字を書くのは自分(オーナー)のお仕事です。

VB6ならばこれからウィンドウプロシージャを作成して WM_DRAWITEM メッセージを拾って〜〜ってことになるんですが、
VB.NETでは先ほどのDrawModeプロパティを変更することによって、DrawItemイベントが使用可能になります。
このイベント内で描画を行うことで、オーナードローが可能になるわけです。
ここまではCBS_OWNERDRAWFIXED→OwnerDrawFixed、WM_DRAWITEMメッセージ→DrawItemイベントのようにWin32APIでの扱いとよく似ていて、私にはとっても馴染みやす くて、なんかうれしい。
なんてったって今まで面倒だったオーナードローが普段どおりイベント処理で実現するんですよ!

さてさて、当初の目的を達成するために何をしなければいけないか...。

「フォント名称からFontオブジェクトを作成し、そのFontオブジェクトで自らの名前を文字列として書く。」とまあ日本語で書くとこんなところですね。

ではでは、DrawItemイベント。
DrawItemイベントは各Itemが描画の必要があるたびに発生し、イベントに関するデータつまり描画対象のItemに関する情報(Indexなど)はすべて引数であるDrawItemEventArgs型の変数eにより返されます。
ItemのIndexが取得できれば必然とリストのテキストが取得できるわけで、
ComboBox1.Items.Item(e.Index).ToString といった感じで 描画対象のフォント名が取得できます。
このフォント名称からFontオブジェクトを作成します。


「えいっ!」

Dim
f As New Font(ComboBox1.Items.Item(e.Index).ToString, 9)

と、こんな感じです。
(上記はComboBox自体のFontサイズと同じく、9ptの標準スタイルのFontを作成しています。)
ではこのFontオブジェクトを使用し、イベントデータとして返された引数eのGraphicsクラスに文字列を描画します。

「でゃっ!」(ウルトラマン風に)

e.Graphics.DrawString(ComboBox1.Items.Item(e.Index).ToString, font, New SolidBrush(Color.Black), e.Bounds.X, e.Bounds.Y)

(とりあえず黒色の文字で描画)で、実行。



「なんだ、すげぇ簡単ぢゃん♪ ちょっと文字が小さくて見た目ショボいけど、いやあなかなか良い出来。」
(と、スクロールボタンをくるくるしていると...)

「ふぎゃっ!」


なにやらこんなダイアログが...。
どうやらフォントには標準スタイルを持たず、斜体しかサポートしていないものがもあったりするようです。
ということで、Fontオブジェクトの作成時には事前に指定したいスタイルをサポートしているかどうかをチェックしてから作成するようにしましょう。(これはオーナードローに限らず、Fontを扱う上では必須かな)

またまたMSDNライブラリにて「FontFamilyメンバ」について調べてみると、パブリックメソッドに IsStyleAvailableってのがあり、”指定したFonStyle列挙体が使用かのうかどうかを示します。”といった説明があります。
メソッド名そのままですね。
このメソッドを使用し、先程のFontオブジェクト作成時の記述を以下のように変更してみましょう。
(聞くところによると、Boldスタイルのみサポートするフォントってのも存在するらしいです。)

 
    Dim ff As FontFamily
    Dim f As Font
    Dim fs As FontStyle

    ff = New FontFamily(ComboBox1.Items.Item(e.Index).ToString)

    If ff.IsStyleAvailable(FontStyle.Regular) Then
        fs = FontStyle.Regular
    ElseIf ff.IsStyleAvailable(FontStyle.Italic) Then
        fs = FontStyle.Italic
    Else
        fs = FontStyle.Bold
    End If

    f = New Font(ff, 9, fs)

じゃあついでなので、先程は文字の描画は黒いブラシを使用していたところを、システムカラー、つまりリスト項目が選択されている場合等は選択色を使用するように変更してみます。
選択されているかどうかはイベント引数eのStateプロパティで取得することができます。
 
Private Sub ComboBox1_DrawItem(ByVal sender As Object, ByVal e As System.Windows.Forms.DrawItemEventArgs) Handles ComboBox1.DrawItem

    Dim ff As FontFamily
    Dim f As Font
    Dim fs As FontStyle
    Dim b As Brush

    ff = New FontFamily(ComboBox1.Items.Item(e.Index).ToString)

    If ff.IsStyleAvailable(FontStyle.Regular) Then
        fs = FontStyle.Regular
    ElseIf ff.IsStyleAvailable(FontStyle.Italic) Then
        fs = FontStyle.Italic
    Else
        fs = FontStyle.Bold
    End If

    f = New Font(ff, 9, fs)  

    e.DrawBackground()    '背景の塗りつぶしはVBにお任せしてしまう。

    If e.State = DrawItemState.Selected Then
        b = SystemBrushes.HighlightText
    Else
        b = SystemBrushes.WindowText
    End If

    e.Graphics.DrawString(ComboBox1.Items.Item(e.Index).ToString, f, b, e.Bounds.X, e.Bounds.Y)
    
    '-- フォントの破棄
    f.Dispose()
    ff.Dispose()

End Sub
上記がそんなこんなを含めたDrawItemイベントのコードの最終形です。

これを実行すると、



となり、選択リストがハイライト表示になっていることが確認できますね。

でもやはりフォントサイズが小さいせいか、見た目がショボい。

その欲求不満解消は次回!
 
 

今後の予定
次回はこのプロジェクトでもう少し遊んで見た目を美しくします。
その次はメニューをオーナードローしてアイコンメニューを作成。
さらにその次はウィンドウプロシージャで遊びます。
お楽しみに!!

Copyright©Sugi. All rights reserved.