.NETプログラミング研究 第51号 †
.NET質問箱 †
「.NET質問箱」では、「どぼん!のプログラミング掲示板」に書き込まれた.NETプログラミングに関する投稿を基に、さらに考察を加え、Q&A形式にまとめて紹介します。
コンボボックスの高さを変えるのは? †
【質問】
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
| | Private Sub Form1_Load(ByVal sender As Object, _
ByVal e As System.EventArgs) Handles MyBase.Load
ComboBox1.DrawMode = DrawMode.OwnerDrawFixed
ComboBox1.ItemHeight = 20
AddHandler ComboBox1.DrawItem, AddressOf ComboBox1_DrawItem
ComboBox1.Items.Add("おはよう。")
ComboBox1.Items.Add("こんにちは。")
ComboBox1.Items.Add("こんばんは。")
End Sub
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
| | private void Form1_Load(object sender, System.EventArgs e)
{
ComboBox1.DrawMode = DrawMode.OwnerDrawFixed;
ComboBox1.ItemHeight = 20;
ComboBox1.DrawItem +=
new DrawItemEventHandler(ComboBox1_DrawItem);
ComboBox1.Items.Add("おはよう。");
ComboBox1.Items.Add("こんにちは。");
ComboBox1.Items.Add("こんばんは。");
}
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ファイルを一時ファイルに書き込み、それを表示するようにすれば済む話ですが、ここでは直接表示する方法を紹介します。
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"
AxWebBrowser1.Navigate(url)
|
1
2
3
4
5
6
7
8
9
| | string url = "res://" +
System.Reflection.Assembly.GetExecutingAssembly().Location +
"/test.html";
object refmissing = System.Reflection.Missing.Value;
AxWebBrowser1.Navigate(url,
ref refmissing, ref refmissing,
ref refmissing, ref refmissing);
|
参考:
○この記事の基になった掲示板のスレッド
タブのテキストを縦書きにするには? †
【質問】
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
| | Private Sub Form1_Load(ByVal sender As Object, _
ByVal e As System.EventArgs) Handles MyBase.Load
TabControl1.Alignment = TabAlignment.Right
TabControl1.DrawMode = TabDrawMode.OwnerDrawFixed
AddHandler TabControl1.DrawItem, AddressOf TabControl1_DrawItem
End Sub
Private Sub TabControl1_DrawItem(ByVal sender As Object, _
ByVal e As DrawItemEventArgs)
Dim tab As TabControl = CType(sender, TabControl)
Dim page As TabPage = tab.TabPages(e.Index)
Dim txt As String = page.Text
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)
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
| | private void Form1_Load(object sender, System.EventArgs e)
{
TabControl1.Alignment = TabAlignment.Right;
TabControl1.DrawMode = TabDrawMode.OwnerDrawFixed;
TabControl1.DrawItem += new DrawItemEventHandler(TabControl1_DrawItem);
}
private void TabControl1_DrawItem(object sender, DrawItemEventArgs e)
{
TabControl tab = (TabControl) sender;
TabPage page = tab.TabPages[e.Index];
string txt = page.Text;
StringFormat sf = new StringFormat();
sf.FormatFlags = StringFormatFlags.DirectionVertical;
Brush foreBrush, backBrush;
backBrush = new SolidBrush(page.BackColor);
e.Graphics.FillRectangle(backBrush, e.Bounds);
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);
|
○この記事の基になった掲示板のスレッド
テキストボックスの現在のカレット位置(行と列)を取得するには? †
【質問】
テキストボックス(またはリッチテキストボックス)内で現在カレットのある行と列を取得するにはどのようにすればよいのでしょうか?
【回答】
行を理論的に数える場合(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
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;
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プロパティでも取得できます。
○この記事の基になった掲示板のスレッド
コメント †