.NETプログラミング研究 第35号 †
.NET質問箱 †
「.NET質問箱」では、「どぼん!のプログラミング掲示板」に書き込まれた.NETプログラミングに関する投稿を基に、さらに考察を加え、Q&A形式にまとめて紹介します。
ComboBoxで上下矢印キーで項目を変更できないようにするには? †
【質問】
WindowsアプリケーションのComboBoxコントロールで、マウスだけで項目を選択でき、上下矢印キーによる選択ができないようにしたいのですが、どのような方法がありますか?
【回答】
簡単な方法としては、ComboBoxのKeyDownイベントハンドラで上下矢印キーが押された時にKeyEventArgsオブジェクトのHandledプロパティをTrueにして、キー入力を無効にする方法があります。
1
2
3
4
5
6
7
8
| | Private Sub ComboBox1_KeyDown(ByVal sender As Object, _
ByVal e As System.Windows.Forms.KeyEventArgs) _
Handles ComboBox1.KeyDown
If e.KeyCode = Keys.Down Or e.KeyCode = Keys.Up Then
e.Handled = True
End If
End Sub
|
1
2
3
4
5
6
7
| | private void ComboBox1_KeyDown(
object sender, System.Windows.Forms.KeyEventArgs e)
{
if (e.KeyCode == Keys.Down || e.KeyCode == Keys.Up)
e.Handled = true;
}
|
しかし残念ながらこの方法は、.NET Framework 1.0では正常に働きません。(1.1ではこのバグが修正されています。).NET Framework 1.0では、ComboBoxの派生クラスを作成し、WndProcやPreProcessMessageメソッドをオーバーライドすることにより、上下矢印キーを無視するなどの方法を使用する必要があります。
次にPreProcessMessageメソッドをオーバーライドすることにより上下矢印キーでの操作を無効にしたComboBoxの派生クラス(MyComboBoxクラス)のサンプルを示します。このクラスを実際に使用するには、"System.Windows.Forms.ComboBox"の代わりに"MyComboBox"を使うようにします。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
| | Public Class MyComboBox
Inherits System.Windows.Forms.ComboBox
Private WM_KEYDOWN As Integer = &H100
Public Overrides Function PreProcessMessage( _
ByRef msg As Message) As Boolean
If msg.Msg = WM_KEYDOWN Then
Dim keyCode As Keys = _
CType(msg.WParam.ToInt32(), Keys) And Keys.KeyCode
If keyCode = Keys.Up Or keyCode = Keys.Down Then
Return True
End If
End If
Return MyBase.PreProcessMessage(msg)
End Function
End Class
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
| | public class MyComboBox : System.Windows.Forms.ComboBox
{
private const int WM_KEYDOWN = 0x100;
public override bool PreProcessMessage(ref Message msg)
{
if (msg.Msg == WM_KEYDOWN)
{
Keys keyCode = (Keys)(int)msg.WParam & Keys.KeyCode;
if (keyCode == Keys.Up || keyCode == Keys.Down)
return true;
}
return base.PreProcessMessage(ref msg);
}
}
|
○この記事の基になった掲示板のスレッド
PageSetupDialogのMarginsが正常に機能しない †
【質問】
PageSetupDialogを使ってページ設定ダイアログを表示したとき、マージン指定が正常に機能しません。例えば次のようなコードでマージンの上下左右に1インチを指定してもページ設定ダイアログでは10センチと表示され、さらに「OK」で確定後、PageSetupDialogオブジェクトのPageSettings.Marginsの値が上下左右39になってしまいます。なぜでしょうか?
1
2
3
4
5
6
7
8
9
10
| | Dim PageSetupDialog1 As New PageSetupDialog
PageSetupDialog1.Document = New System.Drawing.Printing.PrintDocument
PageSetupDialog1.PageSettings.Margins = _
New System.Drawing.Printing.Margins(100, 100, 100, 100)
If PageSetupDialog1.ShowDialog() = DialogResult.OK Then
Console.WriteLine(PageSetupDialog1.PageSettings.Margins)
End If
|
1
2
3
4
5
6
7
8
9
10
11
| | PageSetupDialog PageSetupDialog1 = new PageSetupDialog();
PageSetupDialog1.Document =
new System.Drawing.Printing.PrintDocument();
PageSetupDialog1.PageSettings.Margins =
new System.Drawing.Printing.Margins(100, 100, 100, 100);
if (PageSetupDialog1.ShowDialog() == DialogResult.OK)
Console.WriteLine(PageSetupDialog1.PageSettings.Margins);
|
【回答】
これは.NET Frameworkのバグです。マイクロソフトサポート技術情報814355で紹介されています。
マイクロソフトサポート技術情報では回避法はコントロールパネルの「地域と言語のオプション」の設定を変更するということですが、それができれば苦労はありません。
今後もこのバグが修正されないという前提のもとでは、回避法として、システムでメートル法が選択されているか確認し、そうであればページ設定ダイアログを表示する前にマージンの単位を変更するという方法が考えられます。この方法はニュースグループで紹介されています。
この方法を使って問題を回避したコードの例を示します。ただし、将来このバグが修正された時には逆にバグとなってしまうことに注意してください。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
| | Dim PageSetupDialog1 As New PageSetupDialog
PageSetupDialog1.Document = New System.Drawing.Printing.PrintDocument
PageSetupDialog1.PageSettings.Margins = _
New System.Drawing.Printing.Margins(100, 100, 100, 100)
If System.Globalization.RegionInfo.CurrentRegion.IsMetric Then
PageSetupDialog1.PageSettings.Margins.Top *= 2.54
PageSetupDialog1.PageSettings.Margins.Bottom *= 2.54
PageSetupDialog1.PageSettings.Margins.Left *= 2.54
PageSetupDialog1.PageSettings.Margins.Right *= 2.54
End If
If PageSetupDialog1.ShowDialog() = DialogResult.OK Then
Console.WriteLine(PageSetupDialog1.PageSettings.Margins)
ElseIf System.Globalization.RegionInfo.CurrentRegion.IsMetric Then
PageSetupDialog1.PageSettings.Margins.Top /= 2.54
PageSetupDialog1.PageSettings.Margins.Bottom /= 2.54
PageSetupDialog1.PageSettings.Margins.Left /= 2.54
PageSetupDialog1.PageSettings.Margins.Right /= 2.54
End If
|
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
| | PageSetupDialog PageSetupDialog1 = new PageSetupDialog();
PageSetupDialog1.Document =
new System.Drawing.Printing.PrintDocument();
PageSetupDialog1.PageSettings.Margins =
new System.Drawing.Printing.Margins(100, 100, 100, 100);
if (System.Globalization.RegionInfo.CurrentRegion.IsMetric)
{
PageSetupDialog1.PageSettings.Margins.Top =
(int)Math.Round(
PageSetupDialog1.PageSettings.Margins.Top * 2.54);
PageSetupDialog1.PageSettings.Margins.Bottom =
(int)Math.Round(
PageSetupDialog1.PageSettings.Margins.Bottom * 2.54);
PageSetupDialog1.PageSettings.Margins.Left =
(int)Math.Round(
PageSetupDialog1.PageSettings.Margins.Left * 2.54);
PageSetupDialog1.PageSettings.Margins.Right =
(int)Math.Round(
PageSetupDialog1.PageSettings.Margins.Right * 2.54);
}
if (PageSetupDialog1.ShowDialog() == DialogResult.OK)
Console.WriteLine(PageSetupDialog1.PageSettings.Margins);
else if (System.Globalization.RegionInfo.CurrentRegion.IsMetric)
{
PageSetupDialog1.PageSettings.Margins.Top =
(int)Math.Round(
PageSetupDialog1.PageSettings.Margins.Top / 2.54);
PageSetupDialog1.PageSettings.Margins.Bottom =
(int)Math.Round(
PageSetupDialog1.PageSettings.Margins.Bottom / 2.54);
PageSetupDialog1.PageSettings.Margins.Left =
(int)Math.Round(
PageSetupDialog1.PageSettings.Margins.Left / 2.54);
PageSetupDialog1.PageSettings.Margins.Right =
(int)Math.Round(
PageSetupDialog1.PageSettings.Margins.Right / 2.54);
}
|
TreeViewのNodeの右に色の違う文字列を描画するには? †
【質問】
Outlook ExpressのTreeViewでは、未読のメール数がノードの右側に青色で表示されますが、同じようなことを.NETでできませんか?
【回答】
これを実現させる方法がC# Helpで紹介されています。
この記事ではC#のコードしか紹介されていませんが、これをVB.NETのコードにしたものを掲示板でよねKENさんから投稿していただきました。
これらのコードを以下に紹介させていただきます(一部変更していますが、ほぼ同じです)。フォームにツリービューコントロールTreeView1が配置されているものとし、TreeView1のあるノードの末尾にそのノードの子ノードの数(孫ノード以降は含めない)を青色で表示しています。TreeView1のあるフォームクラスに記述してください。
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
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
| |
Public Structure NMHDR
Public hwndFrom As IntPtr
Public idFrom As Integer
Public code As UInt32
End Structure
Public Structure NMCUSTOMDRAW
Public hdr As NMHDR
Public dwDrawStage As Integer
Public hdc As Integer
Public x1 As Integer
Public y1 As Integer
Public x2 As Integer
Public y2 As Integer
Public dwItemSpec As Integer
Public uItemState As Integer
Public lItemlParam As Integer
End Structure
Private Const WM_NOTIFY As Integer = &H4E
Private ReadOnly NM_CUSTOMDRAW As UInt32 = _
Convert.ToUInt32(4294967284)
Private Const CDDS_ITEMPREPAINT As Integer = 65537
Private Const CDRF_NOTIFYSUBITEMDRAW As Integer = 32
Protected Overrides Sub WndProc(ByRef m As Message)
Dim lp2 As NMCUSTOMDRAW
Dim lp As NMHDR
If (m.Msg = WM_NOTIFY) Then
lp = Marshal.PtrToStructure(m.LParam, lp.GetType())
If (lp.code.CompareTo(NM_CUSTOMDRAW) = 0) Then
lp2 = Marshal.PtrToStructure(m.LParam, lp2.GetType())
If (lp2.dwDrawStage = CDDS_ITEMPREPAINT) Then
MyBase.WndProc(m)
TreeViewPaint(m)
m.Result = IntPtr.Zero
Return
Else
m.Result = New IntPtr(CDRF_NOTIFYSUBITEMDRAW)
End If
End If
End If
MyBase.WndProc(m)
End Sub
Public Sub TreeViewPaint(ByRef m As Message)
Dim lp2 As NMCUSTOMDRAW
Dim g As Graphics
Dim node As TreeNode
Dim strText As String
Dim x1, y1 As Long
lp2 = Marshal.PtrToStructure(m.LParam, lp2.GetType())
g = System.Drawing.Graphics.FromHwnd(Me.TreeView1.Handle)
node = TreeView1.GetNodeAt(lp2.x1 + 1, lp2.y1 + 1)
x1 = lp2.x1 + node.Bounds.X + node.Bounds.Width + 5
y1 = lp2.y1
strText = "( " + node.Nodes.Count.ToString() + " )"
g.DrawString(strText, TreeView1.Font, _
System.Drawing.Brushes.Blue, x1, y1, _
System.Drawing.StringFormat.GenericTypographic)
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
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
| | private unsafe struct NMHDR
{
public uint hwndFrom;
public uint idFrom;
public uint code;
}
private unsafe struct NMCUSTOMDRAW
{
public NMHDR hdr;
public uint dwDrawStage;
public uint hdc;
public uint x1;
public uint y1;
public uint x2;
public uint y2;
public uint dwItemSpec;
public uint uItemState;
public uint lItemlParam;
}
private const uint WM_NOTIFY = 0x4E;
private const uint NM_CUSTOMDRAW = 4294967284;
private const uint CDDS_ITEMPREPAINT = 65537;
private const uint CDRF_NOTIFYSUBITEMDRAW = 32;
protected unsafe override void WndProc(ref Message m)
{
NMCUSTOMDRAW *lp2;
NMHDR *lp;
if (m.Msg == WM_NOTIFY)
{
lp = (NMHDR*) m.LParam.ToPointer();
if (lp->code == NM_CUSTOMDRAW)
{
lp2 = (NMCUSTOMDRAW*)m.LParam.ToPointer();
if (lp2->dwDrawStage == CDDS_ITEMPREPAINT)
{
base.WndProc(ref m);
TreeViewPaint(ref m);
m.Result =(IntPtr)0;
return;
}
else
{
m.Result =(IntPtr)CDRF_NOTIFYSUBITEMDRAW;
}
}
}
base.WndProc(ref m);
}
private unsafe void TreeViewPaint(ref Message m)
{
NMCUSTOMDRAW *lp2;
System.Drawing.Graphics g;
System.Windows.Forms.TreeNode node;
string strText;
long x1, y1;
lp2 = (NMCUSTOMDRAW*)m.LParam.ToPointer();
g = System.Drawing.Graphics.FromHwnd(TreeView1.Handle);
node = TreeView1.GetNodeAt((int)lp2->x1 + 1, (int)lp2->y1 + 1);
x1 = lp2->x1 + node.Bounds.X + node.Bounds.Width + 5;
y1 = lp2->y1;
strText = "( " + node.Nodes.Count.ToString() + " )";
g.DrawString(strText, TreeView1.Font,
System.Drawing.Brushes.Blue,
x1, y1,
System.Drawing.StringFormat.GenericTypographic);
}
|
ただしこの方法では、ノードの末尾に文字列を描画しているだけなので、水平スクロールバーが表示される時(あるいは表示されるべき時)にこの文字列の長さは考慮されないため、スクロールさせても表示されなくなる「可能性があります。
○この記事の基になった掲示板のスレッド
コンピュータ雑学 †
最近巷では「トリビアの泉」や「うんちく王」の影響でか、雑学ブームであるらしいです。生ぬるい雑学を得意げに披露されると非常に腹立たしいものですが、ここではこのような人に話すと嫌われるであろうコンピュータに関する雑学を紹介していこうかなと考えています(今回限りという可能性も大いにありますが)。.NETとは関係ありませんが、息抜きにどうぞ。
「C言語」の「C」の意味は? †
C言語は、1973年頃AT&Tベル研究所でデニス・リッチー氏らにより、UNIXのために開発されたプログラミング言語です。同AT&Tベル研究所のケン・トンプソンらにより開発されたB言語の後続という意味で、「C言語」と名づけられましたので、「C」は何かの略ということではありません。
それではB言語の前にA言語があったのかというと、そうではありませんが、B言語の元をたどると「ALGOL 60」となるため(そのまえにBCPL、CPLがありますが)、うまい具合に「ABC」と並びます。
そうなると次は「D」ですが、「D言語」は現在開発中で、そこそこ知られています。
さらに調べてみたところ、E、F、G(National Instruments Labviewのgraphical programming language)、Hと見つかりました。Iに関しては見つかりませんでしたが、ちゃんと探せばあるかもしれません。
コメント †