.NETプログラミング研究 第29号 †
.NET質問箱 †
「.NET質問箱」では、「どぼん!のプログラミング掲示板」に書き込まれた.NETプログラミングに関する投稿をQ&A形式にまとめ、紹介します。
DataGridの列ヘッダテキストの配置方法を変更する †
【質問】
Windowsアプリケーションで、DataGridコントロールの列ヘッダのテキストだけ中央に配置されるようにしたいのですが、どのようにすればよいのでしょうか?「DataGridの列のテキストの配置方法を変更する」で紹介されているように、DataGridColumnStyleのAlignmentプロパティを変更する方法では、ヘッダとデータの両方のテキストの配置が変更されてしまいます。
【回答】
この問題で困っていらっしゃる方が多いようで(あるいは、簡単な解決法があって当たり前のはずなのに、なかなか見つからないということかもしれませんが)、同様の質問を何度かいただいております。
結論から言いますと、.NET Frameworkにはこの問題を解決する方法が用意されていません。ですので、普通の方法では、今のところ、どうしようもありません。
何か方法がないものかと考えたところ、発想を逆転させ、ヘッダの配置を変えるのではなく、逆にデータの配置を変えるという方法を思いつきました。セルの表示方法は、DataGridColumnStyleのPaintメソッドをオーバーライドすることにより変更することができますので、Alignmentプロパティには列ヘッダの配置を指定し、Paintメソッドでセル内の文字列をAlignmentプロパティとは別の配置方法で描画するということができます。
この考え方にしたがって、セル内の文字列の配置方法を変更できるDataGridColumnStyleの派生クラスを作成すると、例えば次のようになります。
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
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
| | using System;
using System.Drawing;
using System.Windows.Forms;
using System.Data;
namespace Dobon.Samples.Forms
{
public class DataGridCellAlignColumn : DataGridTextBoxColumn
{
Point _margin = new Point(0, 2);
HorizontalAlignment _cellAlign = HorizontalAlignment.Left;
public HorizontalAlignment CellAlign
{
get
{
return _cellAlign;
}
set
{
_cellAlign = value;
}
}
protected override void Edit(
CurrencyManager source,
int rowNum,
Rectangle bounds,
bool readOnly,
string instantText,
bool cellIsVisible)
{
base.Edit(source, rowNum, bounds, readOnly,
instantText, cellIsVisible);
this.TextBox.TextAlign = _cellAlign;
}
protected override void Paint(
Graphics g,
Rectangle bounds,
CurrencyManager source,
int rowNum,
Brush backBrush,
Brush foreBrush,
bool alignToRight)
{
string text =
GetColumnValueAtRow(source, rowNum).ToString();
StringFormat sf = new StringFormat();
switch (_cellAlign)
{
case HorizontalAlignment.Left:
sf.Alignment = StringAlignment.Near;
break;
case HorizontalAlignment.Center:
sf.Alignment = StringAlignment.Center;
break;
case HorizontalAlignment.Right:
sf.Alignment = StringAlignment.Far;
break;
}
sf.FormatFlags = StringFormatFlags.NoWrap;
if (alignToRight)
sf.FormatFlags |=
StringFormatFlags.DirectionRightToLeft;
g.FillRectangle(backBrush, bounds);
RectangleF rectf = new RectangleF(
bounds.X, bounds.Y, bounds.Width, bounds.Height);
rectf.Inflate(-_margin.X, -_margin.Y);
g.DrawString(text, this.DataGridTableStyle.DataGrid.Font,
foreBrush, rectf, sf);
sf.Dispose();
}
}
}
|
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
78
79
80
81
82
83
84
85
86
87
| | Imports System
Imports System.Drawing
Imports System.Windows.Forms
Imports System.Data
Namespace Dobon.Samples.Forms
Public Class DataGridCellAlignColumn
Inherits DataGridTextBoxColumn
Private _margin As New Point(0, 2)
Private _cellAlign As HorizontalAlignment = _
HorizontalAlignment.Left
Public Property CellAlign() As HorizontalAlignment
Get
Return _cellAlign
End Get
Set(ByVal Value As HorizontalAlignment)
_cellAlign = Value
End Set
End Property
Protected Overloads Overrides Sub Edit( _
ByVal [source] As CurrencyManager, _
ByVal rowNum As Integer, _
ByVal bounds As Rectangle, _
ByVal [readOnly] As Boolean, _
ByVal instantText As String, _
ByVal cellIsVisible As Boolean)
MyBase.Edit([source], rowNum, bounds, [readOnly], _
instantText, cellIsVisible)
Me.TextBox.TextAlign = _cellAlign
End Sub
Protected Overloads Overrides Sub Paint( _
ByVal g As Graphics, _
ByVal bounds As Rectangle, _
ByVal [source] As CurrencyManager, _
ByVal rowNum As Integer, _
ByVal backBrush As Brush, _
ByVal foreBrush As Brush, _
ByVal alignToRight As Boolean)
Dim [text] As String = _
GetColumnValueAtRow([source], rowNum).ToString()
Dim sf As New StringFormat
Select Case _cellAlign
Case HorizontalAlignment.Left
sf.Alignment = StringAlignment.Near
Case HorizontalAlignment.Center
sf.Alignment = StringAlignment.Center
Case HorizontalAlignment.Right
sf.Alignment = StringAlignment.Far
End Select
sf.FormatFlags = StringFormatFlags.NoWrap
If alignToRight Then
sf.FormatFlags = sf.FormatFlags Or _
StringFormatFlags.DirectionRightToLeft
End If
g.FillRectangle(backBrush, bounds)
Dim rectf As New RectangleF( _
bounds.X, bounds.Y, bounds.Width, bounds.Height)
rectf.Inflate(-_margin.X, -_margin.Y)
g.DrawString([text], _
Me.DataGridTableStyle.DataGrid.Font, _
foreBrush, rectf, sf)
sf.Dispose()
End Sub
End Class
End Namespace
|
このDataGridCellAlignColumnクラスを使用するには、使用したい列の列スタイルにDataGridCellAlignColumnオブジェクトを設定し、DataGridCellAlignColumnのAlignmentプロパティに列ヘッダテキストの配置を指定し、CellAlignプロパティにセル内の文字列の配置を指定します。
列スタイルを設定する詳しい方法は、次のページをご覧ください。
○この記事の基になった掲示板のスレッド
.NET Tips †
型のすべてのメンバを取得する †
ここでは次のようなクラスが定義されているものとし、このクラスのメンバを取得します。
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
| | Namespace MyNamespace
Public Class [MyClass]
Public Enum PublicEnum
One
Two
End Enum
Private PrivateField As Integer
Public ReadOnly Property PublicProperty() As Integer
Get
Return PrivateField
End Get
End Property
Public Sub New(ByVal val As Integer)
PrivateField = val
End Sub
Public Overloads Function PublicMethod( _
ByVal str As String) As String
str = "PublicMethodが呼び出されました。" + str
Return str
End Function
Public Overloads Function PublicMethod() As String
Return PublicMethod("")
End Function
End Class
End Namespace
|
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
| | namespace MyNamespace
{
public class MyClass
{
public enum PublicEnum
{
One,
Two,
}
private int PrivateField;
public int PublicProperty
{
get
{
return PrivateField;
}
}
public MyClass(int val)
{
PrivateField = val;
}
public string PublicMethod(string str)
{
str = "PublicMethodが呼び出されました。" + str;
return str;
}
public string PublicMethod()
{
return PublicMethod("");
}
}
}
|
ある型(クラス、値型、配列、インターフェイス、ポインタ、列挙体)のすべてのメンバ(プロパティ、メソッド、フィールド、イベントなど)を取得するには、TypeクラスのGetMembersメソッドを呼び出します。
上記MyClassクラスのすべてのメンバ(継承されたメンバを除く)を列挙するには、次のようにします。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
| |
Dim t As Type = GetType(MyNamespace.MyClass)
Dim members As MemberInfo() = t.GetMembers( _
BindingFlags.Public Or BindingFlags.NonPublic Or _
BindingFlags.Instance Or BindingFlags.Static Or _
BindingFlags.DeclaredOnly)
Dim m As MemberInfo
For Each m In members
Console.WriteLine("{0} - {1}", m.MemberType, m.Name)
Next
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
| |
Type t = typeof(MyNamespace.MyClass);
MemberInfo[] members = t.GetMembers(
BindingFlags.Public | BindingFlags.NonPublic |
BindingFlags.Instance | BindingFlags.Static |
BindingFlags.DeclaredOnly);
foreach (MemberInfo m in members)
{
Console.WriteLine("{0} - {1}", m.MemberType, m.Name);
}
|
この結果は、次のように表示されます。
Field - PrivateField
Method - get_PublicProperty
Method - PublicMethod
Method - PublicMethod
Constructor - .ctor
Property - PublicProperty
NestedType - PublicEnum
上記のように検索するメンバの種類を指定するために、BindingFlagsを指定してGetMembersメソッドを呼び出します。GetMembersメソッドでよく使用されるBindingFlagsには次のようなものがあります。
メンバ名 | 説明 | 値 |
Public | パブリックメンバを検索の対象に加える。 | 16 |
NonPublic | パブリックでないメンバを検索の対象に加える。 | 32 |
Instance | 非静的メンバ(インスタンスメンバ)を検索の対象に加える。 | 4 |
Static | 静的メンバを検索の対象に加える。 | 8 |
DeclaredOnly | 継承されたメンバを検索の対象にしない。 | 2 |
FlattenHierarchy | 階層構造の上位の静的メンバを検索の対象に加える。 | 64 |
GetMembersメソッドをパラメータなしで呼び出したときは、
BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static
(つまり、28)を指定したのと同じになります。
先に示したコードで、パラメータを指定しないでGetMembersメソッドを呼び出したとき、その結果は次のようになります。
Method - GetHashCode
Method - Equals
Method - ToString
Method - get_PublicProperty
Method - PublicMethod
Method - PublicMethod
Method - GetType
Constructor - .ctor
Property - PublicProperty
NestedType - PublicEnum
ある型のすべてのプロパティ、メソッド、フィールド、コンストラクタ、インターフェイス、イベント、入れ子になっている型を取得するには、それぞれ、GetProperties、GetMethods、GetFields、GetInterfaces、GetEvents、GetNestedTypesメソッドを使用します。使い方は、戻り値が違う以外は、GetMembersメソッドと同じです。
次にGetMethodsメソッドを使った例を示します。ここではMyClassクラスのすべてのメソッドを、そのアクセシビリティ、戻り値、パラメータ情報などとともに表示しています。
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
| |
Dim t As Type = GetType(MyNamespace.MyClass)
Dim methods As MethodInfo() = t.GetMethods( _
BindingFlags.Public Or BindingFlags.NonPublic Or _
BindingFlags.Instance Or BindingFlags.Static)
Dim m As MethodInfo
For Each m In methods
If m.IsSpecialName Then
GoTo ContinueForEach1
End If
If m.IsPublic Then
Console.Write("public ")
End If
If m.IsPrivate Then
Console.Write("private ")
End If
If m.IsAssembly Then
Console.Write("internal ")
End If
If m.IsFamily Then
Console.Write("protected ")
End If
If m.IsFamilyOrAssembly Then
Console.Write("internal protected ")
End If
If m.IsStatic Then
Console.Write("static ")
End If
If m.IsAbstract Then
Console.Write("abstract ")
Else If m.IsVirtual Then
Console.Write("virtual ")
End If
If m.ReturnType.Equals(Type.GetType("System.Void")) Then
Console.Write("void ")
Else
Console.Write((m.ReturnType.ToString() + " "))
End If
Console.Write(m.Name)
Dim prms As ParameterInfo() = m.GetParameters()
Console.Write("(")
Dim i As Integer
For i = 0 To prms.Length - 1
Dim p As ParameterInfo = prms(i)
Console.Write((p.ParameterType.ToString() + " " + p.Name))
If prms.Length - 1 > i Then
Console.Write(", ")
End If
Next
Console.Write(")")
Console.WriteLine()
ContinueForEach1:
Next
|
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
| |
Type t = typeof(MyNamespace.MyClass);
MethodInfo[] methods = t.GetMethods(
BindingFlags.Public | BindingFlags.NonPublic |
BindingFlags.Instance | BindingFlags.Static);
foreach (MethodInfo m in methods)
{
if (m.IsSpecialName)
continue;
if (m.IsPublic)
Console.Write("public ");
if (m.IsPrivate)
Console.Write("private ");
if (m.IsAssembly)
Console.Write("internal ");
if (m.IsFamily)
Console.Write("protected ");
if (m.IsFamilyOrAssembly)
Console.Write("internal protected ");
if (m.IsStatic)
Console.Write("static ");
if (m.IsAbstract)
Console.Write("abstract ");
else if (m.IsVirtual)
Console.Write("virtual ");
if (m.ReturnType == typeof(void))
Console.Write("void ");
else
Console.Write(m.ReturnType.ToString() + " ");
Console.Write(m.Name);
ParameterInfo[] prms = m.GetParameters();
Console.Write("(");
for (int i = 0; i < prms.Length; i++)
{
ParameterInfo p = prms[i];
Console.Write(p.ParameterType.ToString() + " " + p.Name);
if (prms.Length - 1 > i)
Console.Write(", ");
}
Console.Write(")");
Console.WriteLine();
}
|
結果は次のようになります。
protected virtual void Finalize()
public virtual System.Int32 GetHashCode()
public virtual System.Boolean Equals(System.Object obj)
public virtual System.String ToString()
public System.String PublicMethod(System.String str)
public System.String PublicMethod()
public System.Type GetType()
protected System.Object MemberwiseClone()
コメント †