┏第34号━━━━━━━━━━━━━━━━━━━━━━━━━━┓
┃ .NETプログラミング研究 ┃
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
──<メニュー>───────────────────────
■お知らせ
■.NET質問箱
・文字列の計算式の計算結果を取得するには?
・TabControlのTabPageを非表示にするには?
・DataGridで複数行選択できないようにし、セルがアクティブになら
ならず、行全体が選択されるようにするには?
───────────────────────────────
───────────────────────────────
■お知らせ
───────────────────────────────
過去に発行した「.NETプログラミング研究」のタイトルの一覧を閲覧
できるようにしてほしいというご要望があったため、以下のURLから閲
覧できるようにしました。
[URL].NETプログラミング研究 バックナンバー
http://dobon.net/vb/melma/dotnet.cgi
バックナンバーのメニュー部分を切り出して列挙して表示しているだ
けのものですが、ご利用いただければ幸いです。
───────────────────────────────
■.NET質問箱
───────────────────────────────
「.NET質問箱」では、「どぼん!のプログラミング掲示板」に書き込
まれた.NETプログラミングに関する投稿を基に、さらに考察を加え、
Q&A形式にまとめて紹介します。
[URL]どぼん!のプログラミング掲示板
http://dobon.net/vb/bbs.html
●文字列の計算式の計算結果を取得するには?
【質問】
例えば、"(1+6)*5/(7-4)"のような計算式を表す文字列から、その計算
結果を取得するにはどのようにすればよいのでしょうか?
【回答】
これには、いろいろな方法が考えられます。
正攻法で行けば、計算式を解析し、計算するコードを自分で書くとい
うことになります。そのために参考になりそうなコードが、例えば、
「The Code Project」で紹介されている「A Math Expression
Evaluator」です。この「A Math Expression Evaluator」は簡単な計
算はもちろん、cos、sin、logなどにも対応しています。(ただし、コー
ドはVB.NETのみです。)
[URL]A Math Expression Evaluator
http://www.codeproject.com/vb/net/math_expression_evaluator.asp
さらに、掲示板でArAyさんが紹介されたMSDN Japanの「アルゴリズム
入門:第1章 Visual C# による文字列処理入門」も参考になります。
[URL]アルゴリズム入門:第1章 Visual C# による文字列処理入門
http://www.microsoft.com/japan/msdn/academic/Articles
/Algorithm/01/
このように自分でコードを書く以外の方法も様々あります。
まず、JScript.NETのEvalを使う方法があります。この方法は、
GotDotNet Message Boardsの「String to Numeric」のスレッドで
enderminhさんが紹介しています。
[URL]GotDotNet Message Boards - String to Numeric
http://www.gotdotnet.com/community/messageboard/Thread.aspx?id=212303
この方法によると、まず参照に「Microsoft.Jscript」と「Microsoft.
Vsa」を追加し、次のようなコードにより、計算を実行します。
‥‥▽VB.NET ここから▽‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥
'計算式
Dim exp As String = "(1+6)*5/(7-4)"
Dim ve As Microsoft.JScript.Vsa.VsaEngine = _
Microsoft.JScript.Vsa.VsaEngine.CreateEngine()
Dim result As Double = _
CDbl(Microsoft.JScript.Eval.JScriptEvaluate(exp, ve))
'結果を表示
Console.WriteLine(result)
‥‥△VB.NET ここまで△‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥
‥‥▽C# ここから▽‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥
//計算式
string exp = "(1+6)*5/(7-4)";
Microsoft.JScript.Vsa.VsaEngine ve =
Microsoft.JScript.Vsa.VsaEngine.CreateEngine();
double result =
(double) Microsoft.JScript.Eval.JScriptEvaluate(
exp, ve);
//結果を表示
Console.WriteLine(result);
‥‥△C# ここまで△‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥
また、JScriptCodeProviderにより、Evalメソッドを実行する方法が「
An Eval Function for C# using JScript.NET (JavaScript)」に紹介
されています。
[URL]An Eval Function for C# using JScript.NET (JavaScript)
http://www.odetocode.com/Code/80.aspx
この方法によると、次のようなコードがかけます。
‥‥▽VB.NET ここから▽‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥
'Imports System.Reflection
'Imports System.CodeDom.Compiler
'が宣言されているものとする
'計算式
Dim exp As String = "(1+6)*5/(7-4)"
'計算するためのコード
Dim [source] As String = _
"package Evaluator {" + vbCrLf + _
"class Evaluator {" + vbCrLf + _
"public function Eval(expr : String) : String {" + vbCrLf + _
"return eval(expr);}}}"
'コンパイルするための準備
Dim cp = New Microsoft.JScript.JScriptCodeProvider
Dim icc As ICodeCompiler = cp.CreateCompiler()
Dim cps As New CompilerParameters
Dim cres As CompilerResults
'メモリ内で出力を生成する
cps.GenerateInMemory = True
'コンパイルする
cres = icc.CompileAssemblyFromSource(cps, [source])
'コンパイルしたアセンブリを取得
Dim asm As [Assembly] = cres.CompiledAssembly
'クラスのTypeを取得
Dim t As Type = asm.GetType("Evaluator.Evaluator")
'インスタンスの作成
Dim eval As Object = Activator.CreateInstance(t)
'Evalメソッドを実行し、結果を取得
Dim result As String = CStr(t.InvokeMember("Eval", _
BindingFlags.InvokeMethod, Nothing, eval, New Object() {exp}))
'結果を表示
Console.WriteLine(result)
‥‥△VB.NET ここまで△‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥
‥‥▽C# ここから▽‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥
//using System.Reflection;
//using System.CodeDom.Compiler;
//が宣言されているものとする
//計算式
string exp = "(1+6)*5/(7-4)";
//計算するためのコード
string source =
@"package Evaluator
{
class Evaluator
{
public function Eval(expr : String) : String
{
return eval(expr);
}
}
}";
//コンパイルするための準備
CodeDomProvider cp = new Microsoft.JScript.JScriptCodeProvider();
ICodeCompiler icc = cp.CreateCompiler();
CompilerParameters cps = new CompilerParameters();
CompilerResults cres;
//メモリ内で出力を生成する
cps.GenerateInMemory = true;
//コンパイルする
cres = icc.CompileAssemblyFromSource(cps, source);
//コンパイルしたアセンブリを取得
Assembly asm = cres.CompiledAssembly;
//クラスのTypeを取得
Type t = asm.GetType("Evaluator.Evaluator");
//インスタンスの作成
object eval = Activator.CreateInstance(t);
//Evalメソッドを実行し、結果を取得
string result = (string) t.InvokeMember("Eval",
BindingFlags.InvokeMethod,
null,
eval,
new object[] {exp});
//結果を表示
Console.WriteLine(result);
‥‥△C# ここまで△‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥
さらに、掲示板でピラルクさんが提案されたように、
CSharpCodeProviderを使って、計算式が含まれるコードの文字列をコ
ンパイルし、実行する方法もあります。この方法は「The Code
Project」などでもいくつか紹介されています。
[URL]Runtime Compilation (A .NET eval statement)
http://www.codeproject.com/dotnet/evaluator.asp
[URL]Evaluating Mathematical Expressions by Compiling C# Code at Runtime
http://www.codeproject.com/csharp/matheval.asp
具体的なコードは、例えば、次のようなものです。
‥‥▽VB.NET ここから▽‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥
'Imports System.Reflection
'Imports System.CodeDom.Compiler
'が宣言されているものとする
'計算するためのコード
Dim [source] As String = _
"public class MainClass {" + vbCrLf + _
"public static double EVal() {" + vbCrLf + _
"return (1d+6d)*5d/(7d-4d);" + vbCrLf + _
"}}"
'コンパイルするための準備
Dim cp = New Microsoft.CSharp.CSharpCodeProvider
Dim icc As ICodeCompiler = cp.CreateCompiler()
Dim cps As New CompilerParameters
Dim cres As CompilerResults
'メモリ内で出力を生成する
cps.GenerateInMemory = True
'コンパイルする
cres = icc.CompileAssemblyFromSource(cps, [source])
'コンパイルしたアセンブリを取得
Dim asm As [Assembly] = cres.CompiledAssembly
'MainClassクラスのTypeを取得
Dim t As Type = asm.GetType("MainClass")
'EValメソッドを実行し、結果を取得
Dim d As Double = CDbl(t.InvokeMember("EVal", _
BindingFlags.InvokeMethod, Nothing, Nothing, Nothing))
'結果を表示
Console.WriteLine(d)
‥‥△VB.NET ここまで△‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥
‥‥▽C# ここから▽‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥
//using System.CodeDom.Compiler;
//using System.Reflection;
//が宣言されているものとする
//計算するためのコード
string source = @"
public class MainClass
{
public static double EVal()
{
return (1d+6d)*5d/(7d-4d);
}
}";
//コンパイルするための準備
CodeDomProvider cp = new Microsoft.CSharp.CSharpCodeProvider();
ICodeCompiler icc = cp.CreateCompiler();
CompilerParameters cps = new CompilerParameters();
CompilerResults cres;
//メモリ内で出力を生成する
cps.GenerateInMemory = true;
//コンパイルする
cres = icc.CompileAssemblyFromSource(cps, source);
//コンパイルしたアセンブリを取得
Assembly asm = cres.CompiledAssembly;
//MainClassクラスのTypeを取得
Type t = asm.GetType("MainClass");
//EValメソッドを実行し、結果を取得
double d = (double) t.InvokeMember("EVal",
BindingFlags.InvokeMethod,
null,
null,
null);
//結果を表示
Console.WriteLine(d);
‥‥△C# ここまで△‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥
前のJScriptCodeProviderを使った方法と比べると、この方法は計算式
をコードに含めなければならず、計算式が変わるたびにコンパイルが
必要になりますし、メモリのアセンブリをどのように解放するかとい
う問題もあります(*1)。そのため、実用としては困難かもしれません。
(*1)この問題に関しては、次のページに詳しいです。
[URL]Dynamically executing code in .Net
http://www.west-wind.com/presentations/dynamicCode/DynamicCode.
htm
Microsoft Script Controlが使用できるならば、VBScriptやJSCriptの
Eval関数を使う方法もあります。
‥‥▽VB.NET ここから▽‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥
'計算式
Dim exp As String = "(1+6)*5/(7-4)"
Dim t As Type = _
Type.GetTypeFromProgID("MSScriptControl.ScriptControl")
Dim obj As Object = Activator.CreateInstance(t)
t.InvokeMember("Language", _
System.Reflection.BindingFlags.SetProperty, _
Nothing, _
obj, _
New Object() {"vbscript"})
'Eval関数で計算を実行して結果を取得
Dim result As Double = CDbl( _
t.InvokeMember("Eval", _
System.Reflection.BindingFlags.InvokeMethod, _
Nothing, _
obj, _
New Object() {exp}))
'結果を表示
Console.WriteLine(result)
‥‥△VB.NET ここまで△‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥
‥‥▽C# ここから▽‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥
//計算式
string exp = "(1+6)*5/(7-4)";
Type t = Type.GetTypeFromProgID("MSScriptControl.ScriptControl");
object obj = Activator.CreateInstance(t);
t.InvokeMember("Language",
System.Reflection.BindingFlags.SetProperty,
null,
obj,
new object[] {"vbscript"});
//Eval関数で計算を実行して結果を取得
double result = (double) t.InvokeMember("Eval",
System.Reflection.BindingFlags.InvokeMethod,
null,
obj,
new object[] {exp});
//結果を表示
Console.WriteLine(result);
‥‥△C# ここまで△‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥
これ以外にも、DataTable.Computeメソッドを使う方法など、まだまだ
ありそうですが、きりが無いので、この辺で終わりにします。(面白
い方法がありましたら、ご連絡ください。)
○この記事の基になった掲示板のスレッド
[題名] C#で変数の中身を実行する
[投稿者(敬称略)] ArAy, ピラルク, 管理人
[URL] http://dobon.net/vb/bbs/log3-1/462.html
[題名] 文字列で表現した数式を計算
[投稿者(敬称略)] nissa.com, ArAy
[URL] http://dobon.net/vb/bbs/log3-5/2483.html
───────────────────────────────
●TabControlのTabPageを非表示にするには?
【質問】
TabControlのTabPageにはVisibleプロパティが見つかりません。
TabPageを非表示にするにはどのようにすればよいのでしょうか?
【回答】
TabPageを非表示にするには、TabControl.TabPagesプロパティの
RemoveまたはRemoveAtメソッドを使って削除するしかないようです。
よって一時的にTabPageを非表示にするには、非表示にするTabPageオ
ブジェクトを保持しておき、Removeメソッドで削除し、再び表示する
ときは、TabControl.TabPagesプロパティのAddメソッドで追加します。
TabControlのTabPageを隠し、再び表示させるためのコードを以下に示
します。まずは、次のようなTabPageManagerクラスを書きます。
‥‥▽VB.NET ここから▽‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥
Public Class TabPageManager
Private Class TabPageInfo
Public TabPage As TabPage
Public Visible As Boolean
Public Sub New(ByVal page As TabPage, ByVal v As Boolean)
TabPage = page
Visible = v
End Sub
End Class
Private _tabPageInfos As TabPageInfo() = Nothing
Private _tabControl As TabControl = Nothing
'''
''' TabPageManagerクラスのインスタンスを作成する
'''
''' 基になるTabControlオブジェクト
Public Sub New(ByVal crl As TabControl)
_tabControl = crl
_tabPageInfos = _
New TabPageInfo(_tabControl.TabPages.Count - 1) {}
Dim i As Integer
For i = 0 To _tabControl.TabPages.Count - 1
_tabPageInfos(i) = _
New TabPageInfo(_tabControl.TabPages(i), True)
Next i
End Sub
'''
''' TabPageの表示・非表示を変更する
'''
''' 変更するTabPageのIndex番号
''' 表示するときはTrue。
''' 非表示にするときはFalse。
Public Sub ChangeTabPageVisible( _
ByVal index As Integer, ByVal v As Boolean)
If _tabPageInfos(index).Visible = v Then
Return
End If
_tabPageInfos(index).Visible = v
_tabControl.SuspendLayout()
_tabControl.TabPages.Clear()
Dim i As Integer
For i = 0 To _tabPageInfos.Length - 1
If _tabPageInfos(i).Visible Then
_tabControl.TabPages.Add(_tabPageInfos(i).TabPage)
End If
Next i
_tabControl.ResumeLayout()
End Sub
End Class
‥‥△VB.NET ここまで△‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥
‥‥▽C# ここから▽‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥
public class TabPageManager
{
private class TabPageInfo
{
public TabPage TabPage;
public bool Visible;
public TabPageInfo(TabPage page, bool v)
{
TabPage = page;
Visible = v;
}
}
private TabPageInfo[] _tabPageInfos = null;
private TabControl _tabControl = null;
///
/// TabPageManagerクラスのインスタンスを作成する
///
/// 基になるTabControlオブジェクト
public TabPageManager(TabControl crl)
{
_tabControl = crl;
_tabPageInfos = new TabPageInfo[_tabControl.TabPages.Count];
for(int i = 0; i < _tabControl.TabPages.Count; i++)
_tabPageInfos[i] =
new TabPageInfo(_tabControl.TabPages[i], true);
}
///
/// TabPageの表示・非表示を変更する
///
/// 変更するTabPageのIndex番号
/// 表示するときはTrue。
/// 非表示にするときはFalse。
public void ChangeTabPageVisible(int index, bool v)
{
if (_tabPageInfos[index].Visible == v)
return;
_tabPageInfos[index].Visible = v;
_tabControl.SuspendLayout();
_tabControl.TabPages.Clear();
for(int i = 0; i < _tabPageInfos.Length; i++)
{
if (_tabPageInfos[i].Visible)
_tabControl.TabPages.Add(_tabPageInfos[i].TabPage);
}
_tabControl.ResumeLayout();
}
}
‥‥△C# ここまで△‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥
次に、このTabPageManagerクラスを使ってTabControlのTabPageを隠し、
再び表示させるコードを示します。フォーム(Form1)にTabControl
(TabControl1)が配置されており、TabControl1にいくつかのTabPageが
追加されている時、Button1のクリックによりTabControl1の一番先頭
のTabPageを隠し、さらに、Button2のクリックにより再びこの
TabPageを表示させるようにしています。
‥‥▽VB.NET ここから▽‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥
Private _tabPageManager As TabPageManager
'フォームのLoadイベントハンドラ
Private Sub Form1_Load(ByVal sender As Object, _
ByVal e As EventArgs) Handles MyBase.Load
'TabPageManagerオブジェクトの作成
_tabPageManager = New TabPageManager(TabControl1)
End Sub
'Button1のClickイベントハンドラ
Private Sub Button1_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles Button1.Click
'0番目のTabPageを非表示にする
_tabPageManager.ChangeTabPageVisible(0, False)
End Sub
'Button2のClickイベントハンドラ
Private Sub Button2_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles Button2.Click
'0番目のTabPageを表示する
_tabPageManager.ChangeTabPageVisible(0, True)
End Sub
‥‥△VB.NET ここまで△‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥
‥‥▽C# ここから▽‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥
TabPageManager _tabPageManager = null
//フォームのLoadイベントハンドラ
private void Form1_Load(object sender, System.EventArgs e)
{
//TabPageManagerオブジェクトの作成
_tabPageManager = new TabPageManager(TabControl1);
}
//Button1のClickイベントハンドラ
private void Button1_Click(object sender, System.EventArgs e)
{
//0番目のTabPageを非表示にする
_tabPageManager.ChangeTabPageVisible(0, false);
}
//Button2のClickイベントハンドラ
private void button2_Click(object sender, System.EventArgs e)
{
//0番目のTabPageを表示する
_tabPageManager.ChangeTabPageVisible(0, true);
}
‥‥△C# ここまで△‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥
○この記事の基になった掲示板のスレッド
[題名] TabControlのタブを非表示にしたい
[投稿者(敬称略)] どらごら, よねKEN
[URL] http://dobon.net/vb/bbs/log3-1/607.html
───────────────────────────────
●DataGridで複数行選択できないようにし、セルがアクティブになら
ならず、行全体が選択されるようにするには?
【質問】
Windowsアプリケーションにおいて、ListViewコントロールの
MultiSelectプロパティをFalseにした時のように、DataGridコントロー
ルで複数行選択することができなく、さらに、セルをクリックした時
にセルがアクティブになることなく、そのセルの行全体が選択される
ようにしたいのですが、どのようにすればよいでしょうか?
【回答】
まず、DataGridで一つの行だけを選択できるようにする方法を考えて
みましょう。これに関しては、「Windows Forms FAQ」の「How can I
make my DataGrid support a single select mode, and not the
default multiselect mode?」や「TheScarms .NET Code Library」の
「Make the DataGrid support single select vs multiselect mode.」
でその方法が紹介されています。
[URL]Windows Forms FAQ
5.37 How can I make my DataGrid support a single select mode,
and not the default multiselect mode?
http://www.syncfusion.com/faq/winforms/search/839.asp
[URL]TheScarms .NET Code Library
Make the DataGrid support single select vs multiselect mode.
http://www.thescarms.com/dotNet/SingleSelect.asp
これらで紹介されている方法は、DataGridクラスのOnMouseMoveをオー
バーライドして、マウスドラッグによる選択が無効になるようにし、
さらにOnMouseDownをオーバーライドして、前に選択した行の選択を取
り消すという方法です。
しかしこのやり方では、キーボードによる複数行の選択を全く考慮し
ておらず、不完全です。(マウスの部分も不完全な点が多々あります
が。)
そこで以下に示すコードでは、OnMouseMoveとOnMouseDownをオーバー
ライドする以外に、ProcessCmdKeyをオーバーライドして、Shiftキー
と上矢印または下矢印キーが押された時、ShiftキーとCtrlキーと
HomeまたはEndキーが押された時、CtrlキーとAキーが押された時のそ
れぞれの場合を無効にしています。
‥‥▽VB.NET ここから▽‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥
Imports System
Imports System.Drawing
Imports System.Windows.Forms
Namespace Dobon.Samples.Forms
Public Class MyDataGrid
Inherits DataGrid
Private lastRowSelected As Integer = -1
Private rowSelecting As Boolean = False
Protected Overrides Sub OnMouseMove(ByVal e As MouseEventArgs)
If rowSelecting = False Or _
(e.Button And MouseButtons.Left) <> MouseButtons.Left Then
MyBase.OnMouseMove(e)
End If
End Sub
Protected Overrides Sub OnMouseDown(ByVal e As MouseEventArgs)
rowSelecting = False
Dim info As HitTestInfo = Me.HitTest(e.X, e.Y)
If info.Type = HitTestType.Cell Or _
info.Type = HitTestType.RowHeader Then
'選択されている行をリセットする
If lastRowSelected <> -1 Then
Dim cm As CurrencyManager = _
CType(BindingContext(DataSource), _
CurrencyManager)
If lastRowSelected < cm.Count Then
Me.UnSelect(lastRowSelected)
Else
Me.ResetSelection()
End If
End If
If info.Type = HitTestType.Cell Then
'セル上の時
lastRowSelected = -1
MyBase.OnMouseDown(e)
Else If info.Type = HitTestType.RowHeader Then
'行ヘッダ上の時
If (Control.ModifierKeys And Keys.Shift) = 0 Then
MyBase.OnMouseDown(e)
Else
CurrentCell = _
New DataGridCell(info.Row, info.Column)
End If
Me.Select(info.Row)
lastRowSelected = info.Row
rowSelecting = True
End If
Else
MyBase.OnMouseDown(e)
End If
End Sub
Protected Overrides Function ProcessCmdKey( _
ByRef msg As Message, ByVal keyData As Keys) As Boolean
Const WM_KEYDOWN As Integer = &H100
Const WM_KEYUP As Integer = &H101
If msg.Msg = WM_KEYDOWN Or msg.Msg = WM_KEYUP Then
Dim keyCode As Keys = _
CType(CInt(keyData), Keys) And Keys.KeyCode
'Shift+Up,Downキーでの複数行選択を防止
If (keyData And Keys.Shift) = Keys.Shift And _
(keyCode = Keys.Up Or keyCode = Keys.Down) Then
Return True
End If 'Shift+Ctrl+Home,Endキーでの複数行選択を防止
If (keyData And Keys.Shift) = Keys.Shift And _
(keyData And Keys.Control) = Keys.Control And _
(keyCode = Keys.Home Or keyCode = Keys.End) Then
Return True
End If
'Ctrl+Aキーでの複数行選択を防止
If (keyData And Keys.Control) = Keys.Control And _
keyCode = Keys.A Then
Return True
End If
End If
Return MyBase.ProcessCmdKey(msg, keyData)
End Function
End Class
End Namespace
‥‥△VB.NET ここまで△‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥
‥‥▽C# ここから▽‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥
using System;
using System.Drawing;
using System.Windows.Forms;
namespace Dobon.Samples.Forms
{
public class MyDataGrid : DataGrid
{
private int lastRowSelected = -1;
private bool rowSelecting = false;
protected override void OnMouseMove(MouseEventArgs e)
{
if (rowSelecting == false || _
(e.Button & MouseButtons.Left) != MouseButtons.Left)
base.OnMouseMove(e);
}
protected override void OnMouseDown(MouseEventArgs e)
{
rowSelecting = false;
HitTestInfo info = this.HitTest(e.X, e.Y);
if (info.Type == HitTestType.Cell ||
info.Type == HitTestType.RowHeader)
{
//選択されている行をリセットする
if (lastRowSelected != -1)
{
CurrencyManager cm =
(CurrencyManager) BindingContext[DataSource];
if (lastRowSelected < cm.Count)
this.UnSelect(lastRowSelected);
else
this.ResetSelection();
}
if (info.Type == HitTestType.Cell)
{
//セル上の時
lastRowSelected = -1;
base.OnMouseDown(e);
}
else if (info.Type == HitTestType.RowHeader)
{
//行ヘッダ上の時
if ((Control.ModifierKeys & Keys.Shift) == 0)
base.OnMouseDown(e);
else
CurrentCell =
new DataGridCell(info.Row, info.Column);
this.Select(info.Row);
lastRowSelected = info.Row;
rowSelecting = true;
}
}
else
{
base.OnMouseDown(e);
}
}
protected override bool ProcessCmdKey(
ref Message msg, Keys keyData)
{
const int WM_KEYDOWN = 0x100;
const int WM_KEYUP = 0x101;
if (msg.Msg == WM_KEYDOWN || msg.Msg == WM_KEYUP)
{
Keys keyCode = (Keys)(int)keyData & Keys.KeyCode;
//Shift+Up,Downキーでの複数行選択を防止
if ((keyData & Keys.Shift) == Keys.Shift &&
(keyCode == Keys.Up || keyCode == Keys.Down))
return true;
//Shift+Ctrl+Home,Endキーでの複数行選択を防止
if ((keyData & Keys.Shift) == Keys.Shift &&
(keyData & Keys.Control) == Keys.Control &&
(keyCode == Keys.Home || keyCode == Keys.End))
return true;
//Ctrl+Aキーでの複数行選択を防止
if ((keyData & Keys.Control) == Keys.Control &&
keyCode == Keys.A)
return true;
}
return base.ProcessCmdKey(ref msg, keyData);
}
}
}
‥‥△C# ここまで△‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥
次にセルがアクティブにならないようにする方法を考えます。この方
法も、「Windows Forms FAQ」で紹介されています。
[URL]Windows Forms FAQ
5.41 How can I make my grid never have an active edit cell and
always select whole rows (as in a browser-type grid)?
http://www.syncfusion.com/faq/winforms/search/856.asp
[URL]Windows Forms FAQ
5.83 How can I prevent all the cells in my DataGrid from being
edited without deriving GridColumnStyle?
http://www.syncfusion.com/faq/winforms/search/1008.asp
「5.41」は、GridColumnStyleを使い、GridColumnStyleのEditメソッ
ドをオーバーライドし、編集できないようにする方法です。「5.83」
は、GridColumnStyleを使わずに、DataGrid.Controlsからスクロール
バー以外のコントロールを削除するという方法です。
ここでは、「5.83」の方法を採用し、DataGridのOnControlAddedメソ
ッドをオーバーライドして、スクロールバー以外のコントロールが追
加された時は、これを削除するようにします。(ただしこの方法では
DataGridBoolColumnによるチェックボックスはアクティブになってし
まうようです。)
‥‥▽VB.NET ここから▽‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥
Protected Overrides Sub OnControlAdded( _
ByVal e As ControlEventArgs)
MyBase.OnControlAdded(e)
If Not TypeOf e.Control Is VScrollBar And _
Not TypeOf e.Control Is HScrollBar Then
Me.Controls.Remove(e.Control)
End If
End Sub
‥‥△VB.NET ここまで△‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥
‥‥▽C# ここから▽‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥
protected override void OnControlAdded(ControlEventArgs e)
{
base.OnControlAdded(e);
if (!(e.Control is VScrollBar) && !(e.Control is HScrollBar))
this.Controls.Remove(e.Control);
}
‥‥△C# ここまで△‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥
セルをクリックした時に行全体が選択されるようにするには、これま
た「Windows Forms FAQ」にあるように、DataGridのMouseUpイベント
で行を選択するようにすればよいでしょう。
[URL]Windows Forms FAQ
5.11 How can I select the entire row when the user clicks on a
cell in the row
http://www.syncfusion.com/faq/winforms/search/689.asp
クリックでも行が選択されるようにするには、CurrentCellChangedイ
ベントなど、適当なイベントを使ってください。ここでは、
OnMouseDownメソッドで行を選択するようにします。
以上の考察により書かれたDataGridクラスの派生クラス(MyDataGridク
ラス)のコードを以下に示します。
‥‥▽VB.NET ここから▽‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥
Imports System
Imports System.Drawing
Imports System.Windows.Forms
Namespace Dobon.Samples.Forms
Public Class MyDataGrid
Inherits DataGrid
Private lastRowSelected As Integer = -1
Private rowSelecting As Boolean = False
Protected Overrides Sub OnMouseMove(ByVal e As MouseEventArgs)
If rowSelecting = False Or _
(e.Button And MouseButtons.Left) <> MouseButtons.Left Then
MyBase.OnMouseMove(e)
End If
End Sub
Protected Overrides Sub OnMouseDown(ByVal e As MouseEventArgs)
rowSelecting = False
Dim info As HitTestInfo = Me.HitTest(e.X, e.Y)
If info.Type = HitTestType.Cell Or _
info.Type = HitTestType.RowHeader Then
'選択されている行をリセットする
If lastRowSelected <> -1 Then
Dim cm As CurrencyManager = _
CType(BindingContext(DataSource), _
CurrencyManager)
If lastRowSelected < cm.Count Then
Me.UnSelect(lastRowSelected)
Else
Me.ResetSelection()
End If
End If
If info.Type = HitTestType.Cell Then
'セル上の時
lastRowSelected = -1
MyBase.OnMouseDown(e)
Me.Select(info.Row)
Else If info.Type = HitTestType.RowHeader Then
'行ヘッダ上の時
If (Control.ModifierKeys And Keys.Shift) = 0 Then
MyBase.OnMouseDown(e)
Else
CurrentCell = _
New DataGridCell(info.Row, info.Column)
End If
Me.Select(info.Row)
lastRowSelected = info.Row
rowSelecting = True
End If
Else
MyBase.OnMouseDown(e)
End If
End Sub
Protected Overrides Function ProcessCmdKey( _
ByRef msg As Message, ByVal keyData As Keys) As Boolean
Const WM_KEYDOWN As Integer = &H100
Const WM_KEYUP As Integer = &H101
If msg.Msg = WM_KEYDOWN Or msg.Msg = WM_KEYUP Then
Dim keyCode As Keys = _
CType(CInt(keyData), Keys) And Keys.KeyCode
'Shift+Up,Downキーでの複数行選択を防止
If (keyData And Keys.Shift) = Keys.Shift And _
(keyCode = Keys.Up Or keyCode = Keys.Down) Then
Return True
End If
'Shift+Ctrl+Home,Endキーでの複数行選択を防止
If (keyData And Keys.Shift) = Keys.Shift And _
(keyData And Keys.Control) = Keys.Control And _
(keyCode = Keys.Home Or keyCode = Keys.End) Then
Return True
End If
'Ctrl+Aキーでの複数行選択を防止
If (keyData And Keys.Control) = Keys.Control And _
keyCode = Keys.A Then
Return True
End If
End If
Return MyBase.ProcessCmdKey(msg, keyData)
End Function
Protected Overrides Sub OnControlAdded( _
ByVal e As ControlEventArgs)
MyBase.OnControlAdded(e)
If Not TypeOf e.Control Is VScrollBar And _
Not TypeOf e.Control Is HScrollBar Then
Me.Controls.Remove(e.Control)
End If
End Sub
End Class
End Namespace
‥‥△VB.NET ここまで△‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥
‥‥▽C# ここから▽‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥
using System;
using System.Drawing;
using System.Windows.Forms;
namespace Dobon.Samples.Forms
{
public class MyDataGrid : DataGrid
{
private int lastRowSelected = -1;
private bool rowSelecting = false;
protected override void OnMouseMove(MouseEventArgs e)
{
if (rowSelecting == false || _
(e.Button & MouseButtons.Left) != MouseButtons.Left)
base.OnMouseMove(e);
}
protected override void OnMouseDown(MouseEventArgs e)
{
rowSelecting = false;
HitTestInfo info = this.HitTest(e.X, e.Y);
if (info.Type == HitTestType.Cell ||
info.Type == HitTestType.RowHeader)
{
//選択されている行をリセットする
if (lastRowSelected != -1)
{
CurrencyManager cm =
(CurrencyManager) BindingContext[DataSource];
if (lastRowSelected < cm.Count)
this.UnSelect(lastRowSelected);
else
this.ResetSelection();
}
if (info.Type == HitTestType.Cell)
{
//セル上の時
lastRowSelected = -1;
base.OnMouseDown(e);
this.Select(info.Row);
}
else if (info.Type == HitTestType.RowHeader)
{
//行ヘッダ上の時
if ((Control.ModifierKeys & Keys.Shift) == 0)
base.OnMouseDown(e);
else
CurrentCell =
new DataGridCell(info.Row, info.Column);
this.Select(info.Row);
lastRowSelected = info.Row;
rowSelecting = true;
}
}
else
{
base.OnMouseDown(e);
}
}
protected override bool ProcessCmdKey(
ref Message msg, Keys keyData)
{
const int WM_KEYDOWN = 0x100;
const int WM_KEYUP = 0x101;
if (msg.Msg == WM_KEYDOWN || msg.Msg == WM_KEYUP)
{
Keys keyCode = (Keys)(int)keyData & Keys.KeyCode;
//Shift+Up,Downキーでの複数行選択を防止
if ((keyData & Keys.Shift) == Keys.Shift &&
(keyCode == Keys.Up || keyCode == Keys.Down))
return true;
//Shift+Ctrl+Home,Endキーでの複数行選択を防止
if ((keyData & Keys.Shift) == Keys.Shift &&
(keyData & Keys.Control) == Keys.Control &&
(keyCode == Keys.Home || keyCode == Keys.End))
return true;
//Ctrl+Aキーでの複数行選択を防止
if ((keyData & Keys.Control) == Keys.Control &&
keyCode == Keys.A)
return true;
}
return base.ProcessCmdKey(ref msg, keyData);
}
protected override void OnControlAdded(ControlEventArgs e)
{
base.OnControlAdded(e);
if (!(e.Control is VScrollBar) &&
!(e.Control is HScrollBar))
this.Controls.Remove(e.Control);
}
}
}
‥‥△C# ここまで△‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥
○この記事の基になった掲示板のスレッド
[題名] NO TITLE
[投稿者(敬称略)] しびっく, ピラルク, 管理人
[URL] http://dobon.net/vb/bbs/log3-2/618.html
===============================
■このマガジンの購読、購読中止、バックナンバー、説明に関しては
次のページをご覧ください。
http://www.mag2.com/m/0000104516.htm
■発行人・編集人:どぼん!
(Microsoft MVP for Visual Basic, Oct 2003-Oct 2004)
http://dobon.net
dobon_info@yahoo.co.jp
■ご質問等はメールではなく、掲示板へお願いいたします。
http://dobon.net/vb/bbs.html
■上記メールアドレスへのメールは確実に読まれる保障はありません
(スパム、ウィルス対策です)。メールは下記URLのフォームメール
から送信してください。
http://dobon.net/mail.html
Copyright (c) 2003 - 2004 DOBON! All rights reserved.
===============================