.NETプログラミング研究 第51号

.NET質問箱

「.NET質問箱」では、「どぼん!のプログラミング掲示板」に書き込まれた.NETプログラミングに関する投稿を基に、さらに考察を加え、Q&A形式にまとめて紹介します。

コンボボックスの高さを変えるのは?

注意

この記事の最新版は「ComboBoxの高さを変える」で公開しています。

【質問】

System.Windows.Forms.ComboBoxコントロールの高さを変えることは出来ますか?Heightプロパティの値を変えても高さが変わらないようですが。

【回答】

ComboBoxの高さを変えるには、ComboBoxをオーナードローする必要があります。ComboBoxのオーナードローについては、次のページで紹介しています。

以下にサンプルを示します。ここではComboBox1の項目の高さ(ItemHeightプロパティ)を20としています。

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
'フォームのLoadイベントハンドラ
Private Sub Form1_Load(ByVal sender As Object, _
        ByVal e As System.EventArgs) Handles MyBase.Load
    'オーナードローを指定
    ComboBox1.DrawMode = DrawMode.OwnerDrawFixed
    '項目の高さを設定
    ComboBox1.ItemHeight = 20
    'DrawItemイベントハンドラの追加
    AddHandler ComboBox1.DrawItem, AddressOf ComboBox1_DrawItem
 
    'ComboBox1のリストに項目を追加する
    ComboBox1.Items.Add("おはよう。")
    ComboBox1.Items.Add("こんにちは。")
    ComboBox1.Items.Add("こんばんは。")
End Sub
 
'DrawItemイベントハンドラ
'項目を描画する
Private Sub ComboBox1_DrawItem(ByVal sender As Object, _
    ByVal e As System.Windows.Forms.DrawItemEventArgs)
    '背景を描画する
    '項目が選択されている時は強調表示される
    e.DrawBackground()
 
    Dim cmb As ComboBox = CType(sender, ComboBox)
    '項目に表示する文字列
    Dim txt As String
    If (e.Index > -1) Then
        txt = cmb.Items(e.Index).ToString()
    Else
        txt = cmb.Text
    End If
 
    '使用するブラシ
    Dim b As Brush = New SolidBrush(e.ForeColor)
    '文字列を描画する
    Dim ym As Single = (e.Bounds.Height - _
        e.Graphics.MeasureString(txt, cmb.Font).Height) / 2
    e.Graphics.DrawString(txt, cmb.Font, b, _
        e.Bounds.X, e.Bounds.Y + ym)
    b.Dispose()
 
    'フォーカスを示す四角形を描画
    e.DrawFocusRectangle()
End Sub
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
//Form1のLoadイベントハンドラ
private void Form1_Load(object sender, System.EventArgs e)
{
    //オーナードローを指定
    ComboBox1.DrawMode = DrawMode.OwnerDrawFixed;
    //項目の高さを設定
    ComboBox1.ItemHeight = 20;
    //DrawItemイベントハンドラの追加
    ComboBox1.DrawItem +=
        new DrawItemEventHandler(ComboBox1_DrawItem);
 
    //ComboBox1のリストに項目を追加する
    ComboBox1.Items.Add("おはよう。");
    ComboBox1.Items.Add("こんにちは。");
    ComboBox1.Items.Add("こんばんは。");
}
 
//DrawItemイベントハンドラ
//項目を描画する
private void ComboBox1_DrawItem(object sender, 
    System.Windows.Forms.DrawItemEventArgs e)
{
    //背景を描画する
    //項目が選択されている時は強調表示される
    e.DrawBackground();
 
    ComboBox cmb = (ComboBox) sender;
    //項目に表示する文字列
    string txt = e.Index > -1 ?
        cmb.Items[e.Index].ToString() : cmb.Text;
    //使用するブラシ
    Brush b = new SolidBrush(e.ForeColor);
    //文字列を描画する
    float ym = 
        (e.Bounds.Height - e.Graphics.MeasureString(
            txt, cmb.Font).Height) / 2;
    e.Graphics.DrawString(
        txt, cmb.Font, b, e.Bounds.X, e.Bounds.Y + ym);
    b.Dispose();
 
    //フォーカスを示す四角形を描画
    e.DrawFocusRectangle();
}

○この記事の基になった掲示板のスレッド

埋め込まれたHTMLを表示するには?

注意

この記事の最新版は「埋め込まれたHTMLを表示する」で公開しています。

【質問】

アセンブリファイルにリソースとしてHTMLファイルを埋め込み、表示したいのですが、いい方法はないでしょうか?

【回答】

埋め込まれたHTMLファイルを一時ファイルに書き込み、それを表示するようにすれば済む話ですが、ここでは直接表示する方法を紹介します。

Internet Explore 4以降(?)では、res://プロトコルがサポートされています。これは、埋め込まれているリソースを表示できるようにするものですので、これを使えば埋め込んだHTMLファイルを表示できます。ところが、res://で対応しているリソースはWin32リソースで、これは通常.NET Frameworkで使用されるリソースとは異なります。

.NETでWin32リソースを埋め込む方法はちょっと厄介です。Visual Studio .NETでは、次のようにして埋め込むことができます。

1.リソースを埋め込むアセンブリファイルをメニューの「ファイル」-「開く」-「ファイル」で開きます。

2.アセンブリファイル名のノード(RCノード)を右クリックしてメニューを表示し、「リソースの追加」を選択します。

3.「リソースの追加」ダイアログの「インポート」ボタンをクリックし、埋め込みたいファイルを選択します。

4.保存して、完了です。

このような方法では、アセンブリをビルドするたびにリソースを埋め込み直す必要があります。それが面倒であれば、/win32res(VB.NETでは、/win32resource)コンパイラオプションを使用してcsc.exe(VB.NETでは、vbc.exe)でコンパイルする必要があります。次にこの方法を説明します。

/win32resオプションでは、Win32リソースファイルを指定する必要があるので、まずはこれを作成します。Win32リソースファイルを作るには、RCファイルを作成し、リソースコンパイラ(rc.exe、"C:\Program Files\Microsoft Visual Studio .NET 2003\Vc7\bin\rc.exe"あたりにあります)でコンパイルします。

"readme.html"を"test.html"というIDで埋め込む場合、RCファイルとして次のようなテキストファイルを作成します。ここでは、"win32.rc"として保存します。

‥‥▽ここから▽‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥ readme.html HTML "test.html" ‥‥△ここまで△‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥

コマンドラインで

rc.exe win32.rc

を実行すると、"win32.RES"というファイルができます。これが、Win32リソースファイルです。

あとはリソースを埋め込むアセンブリをコンパイルする時に、/win32resオプションでこのファイルを指定するだけです。

  1
vbc.exe /out:win32res.dll /target:library /win32resource:win32.RES win32res.vb
  1
csc.exe /out:win32res.dll /target:library /win32res:win32.RES win32res.cs

このようにして埋め込まれたファイルは、Internet Exploreから、

res://(アセンブリファイルのフルパス)/(リソースのID)

としてアクセスできます。つまり上の例では、

res://(アセンブリファイルのフルパス)/test.html

で埋め込まれたHTMLが表示されます。

最後に、Microsoft Web Browserコントロールで自分自身のアセンブリに埋め込んだHTML(IDが"test.html")を表示する例を示します。ラッパーDLLが作成されており、これを使用するものとします。

  1
  2
  3
  4
  5
  6
Dim url As String = "res://" + _
    System.Reflection.Assembly.GetExecutingAssembly().Location + _
    "/test.html"
 
'WebBrowserに表示する
AxWebBrowser1.Navigate(url)
  1
  2
  3
  4
  5
  6
  7
  8
  9
string url = "res://" +
    System.Reflection.Assembly.GetExecutingAssembly().Location +
    "/test.html";
 
//WebBrowserに表示する
object refmissing = System.Reflection.Missing.Value;
AxWebBrowser1.Navigate(url,
    ref refmissing, ref refmissing, 
    ref refmissing, ref refmissing);

参考:

○この記事の基になった掲示板のスレッド

タブのテキストを縦書きにするには?

注意

この記事の最新版は「TabControlのタブのテキストを縦書きにする」で公開しています。

【質問】

TabControlコントロールのAlignmentプロパティをRightにしてタブを右側に表示していますが、タブに表示される文字列を縦書きにできないでしょうか?

【回答】

まず、タブをオーナードローする方法があります。タブコントロールのタブをオーナードローする方法については、

をご覧ください。

以下にオーナードローによりタブのTextを縦書きにする例を示します。

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
'フォームのLoadイベントハンドラ
Private Sub Form1_Load(ByVal sender As Object, _
        ByVal e As System.EventArgs) Handles MyBase.Load
    TabControl1.Alignment = TabAlignment.Right
 
    'TabControlをオーナードローする
    TabControl1.DrawMode = TabDrawMode.OwnerDrawFixed
    'DrawItemイベントハンドラを追加
    AddHandler TabControl1.DrawItem, AddressOf TabControl1_DrawItem
End Sub
 
'TabControl1のDrawItemイベントハンドラ
Private Sub TabControl1_DrawItem(ByVal sender As Object, _
    ByVal e As DrawItemEventArgs)
    '対象のTabControlを取得
    Dim tab As TabControl = CType(sender, TabControl)
    Dim page As TabPage = tab.TabPages(e.Index)
    'タブページのテキストを取得
    Dim txt As String = page.Text
 
    'StringFormatを作成
    Dim sf As New StringFormat
    '縦書きにする
    sf.FormatFlags = StringFormatFlags.DirectionVertical
 
    Dim foreBrush, backBrush As Brush
    '背景の描画
    backBrush = New SolidBrush(page.BackColor)
    e.Graphics.FillRectangle(backBrush, e.Bounds)
    'Textの描画
    foreBrush = New SolidBrush(page.ForeColor)
    e.Graphics.DrawString(txt, page.Font, foreBrush, _
        RectangleF.op_Implicit(e.Bounds), sf)
 
    foreBrush.Dispose()
    backBrush.Dispose()
End Sub
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
//Form1のLoadイベントハンドラ
private void Form1_Load(object sender, System.EventArgs e)
{
    TabControl1.Alignment = TabAlignment.Right;
 
    //TabControlをオーナードローする
    TabControl1.DrawMode = TabDrawMode.OwnerDrawFixed;
    //DrawItemイベントハンドラを追加
    TabControl1.DrawItem += new DrawItemEventHandler(TabControl1_DrawItem);
}
 
//TabControl1のDrawItemイベントハンドラ
private void TabControl1_DrawItem(object sender, DrawItemEventArgs e)
{
    //対象のTabControlを取得
    TabControl tab = (TabControl) sender;
    TabPage page = tab.TabPages[e.Index];
    //タブページのテキストを取得
    string txt = page.Text;
 
    //StringFormatを作成
    StringFormat sf = new StringFormat();
    //縦書きにする
    sf.FormatFlags = StringFormatFlags.DirectionVertical;
 
    Brush foreBrush, backBrush;
    //背景の描画
    backBrush = new SolidBrush(page.BackColor);
    e.Graphics.FillRectangle(backBrush, e.Bounds);
    //Textの描画
    foreBrush = new SolidBrush(page.ForeColor);
    e.Graphics.DrawString(txt, page.Font, foreBrush, e.Bounds, sf);
    foreBrush.Dispose();
    backBrush.Dispose();
}

また、掲示板ではピラルクさんが縦書きフォントを使用する方法を紹介されています。オーナードローよりも簡単できれいに表示されます。ただし、タブが左にあるときは、文字が上下逆さになってしまい、この方法は使えません。

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
TabControl1.Alignment = TabAlignment.Right
 
'縦書きのフォントにする
TabControl1.Font = New Font( _
    TabControl1.Font.FontFamily, _
    TabControl1.Font.Size, _
    TabControl1.Font.Style, _
    TabControl1.Font.Unit, _
    TabControl1.Font.GdiCharSet, _
    True)
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
TabControl1.Alignment = TabAlignment.Right;
 
//縦書きのフォントにする
TabControl1.Font = new Font(
    TabControl1.Font.FontFamily,
    TabControl1.Font.Size,
    TabControl1.Font.Style,
    TabControl1.Font.Unit,
    TabControl1.Font.GdiCharSet,
    true);

○この記事の基になった掲示板のスレッド

テキストボックスの現在のカレット位置(行と列)を取得するには?

注意

この記事の最新版は「TextBox(またはRichTextBox)の現在のカレット位置(行と列)を取得する」で公開しています。

【質問】

テキストボックス(またはリッチテキストボックス)内で現在カレットのある行と列を取得するにはどのようにすればよいのでしょうか?

【回答】

行を理論的に数える場合(WordWrapがTrueで、右側で折り返されていても一行として数える)は、次のように計算できます。ここでは、テキストボックス"TextBox1"の現在のカレットの位置を計算しています。

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
'文字列
Dim str As String = TextBox1.Text
'カレットの位置を取得
Dim selectPos As Integer = TextBox1.SelectionStart
 
'カレットの位置までの行を数える
Dim row As Integer = 1
Dim startPos As Integer = 0
Dim endPos As Integer
While (True)
    endPos = str.IndexOf(vbLf, startPos)
    If (endPos < 0 Or endPos > selectPos) Then
        Exit While
    End If
    startPos = endPos + 1
    row += 1
End While
 
'列の計算
Dim col As Integer = selectPos - startPos + 1
 
'結果を表示
Console.WriteLine("行:{0} 列:{1}", row, col)
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
//文字列
string str = TextBox1.Text;
//カレットの位置を取得
int selectPos = TextBox1.SelectionStart;
 
//カレットの位置までの行を数える
int row = 1, startPos = 0;
for (int endPos = 0;
    (endPos = str.IndexOf('\n', startPos)) < selectPos && endPos > -1;
    row++)
{
    startPos = endPos + 1;
}
 
//列の計算
int col = selectPos - startPos + 1;
 
//結果を表示
Console.WriteLine("行:{0} 列:{1}", row, col);

行を物理的に数える場合(WordWrapがTrueで、右側で折り返されていれば2行として数える)は、SendMessage関数を使用して取得します。

次の例では、Button1をクリックすることにより、物理的な現在のカレットの位置を表示しています。

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
<System.Runtime.InteropServices.DllImport("User32.Dll")> _
Private Shared Function SendMessage( _
    ByVal hWnd As IntPtr, _
    ByVal Msg As Integer, _
    ByVal wParam As Integer, _
    ByVal lParam As Integer) As Integer
End Function
Private Const EM_LINEINDEX As Integer = &HBB
Private Const EM_LINEFROMCHAR As Integer = &HC9
 
'Button1のクリックイベントハンドラ
Private Sub Button1_Click(ByVal sender As Object, _
        ByVal e As System.EventArgs) Handles Button1.Click
    '現在の行を取得する
    Dim row As Integer = SendMessage( _
        TextBox1.Handle, EM_LINEFROMCHAR, -1, 0) + 1
 
    '現在の列を取得する
    Dim lineIndex As Integer = SendMessage( _
        TextBox1.Handle, EM_LINEINDEX, -1, 0)
    Dim col As Integer = TextBox1.SelectionStart - lineIndex + 1
 
    Console.WriteLine("行:{0} 列:{1}", row, col)
End Sub
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
[System.Runtime.InteropServices.DllImport("User32.Dll")]
private static extern int SendMessage(
    IntPtr hWnd,int Msg,int  wParam,int  lParam);
private const int EM_LINEINDEX = 0xBB;
private const int EM_LINEFROMCHAR=0xC9;
 
//Button1のクリックイベントハンドラ
private void Button1_Click(object sender, System.EventArgs e)
{
    //現在の行を取得する
    int row = SendMessage(
        TextBox1.Handle, EM_LINEFROMCHAR, -1, 0) + 1;
 
    //現在の列を取得する
    int lineIndex = SendMessage(
        TextBox1.Handle, EM_LINEINDEX, -1, 0);
    int col = TextBox1.SelectionStart - lineIndex + 1;
 
    Console.WriteLine("行:{0} 列:{1}", row, col);
}

なお、リッチテキストボックスの物理的な行は、GetLineFromCharIndexプロパティでも取得できます。

○この記事の基になった掲示板のスレッド

コメント



ページ情報
  • カテゴリ : .NET
  • 作成日 : 2005-02-09 (水) 06:00:00
  • 作成者 : DOBON!
  • 最終編集日 : 2010-03-22 (月) 01:55:32
  • 最終編集者 : DOBON!
[ トップ ]   [ 編集 | 凍結 | 差分 | バックアップ | 添付 | 複製 | 名前変更 | リロード ]   [ 新規 | 子ページ作成 | 一覧 | 単語検索 | 最終更新 | ヘルプ ]