• 追加された行はこの色です。
  • 削除された行はこの色です。
#title(.NETプログラミング研究 第64号)

#navi(.NET プログラミング研究)
#navi(.NETプログラミング研究)

#contents

*.NETプログラミング研究 第64号 [#f6c3ea59]

**.NET質問箱 [#q45794f5]

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

-[[どぼん!のプログラミング掲示板>http://dobon.net/vb/bbs.html]]

***DataGridコントロールをスクロールさせた時に、別のDataGridを同時にスクロールさせる [#s2f83593]

#column(注意){{
この記事の最新版は「[[DataGridをスクロールさせた時に、別のDataGridを同時にスクロールさせる>http://dobon.net/vb/dotnet/datagrid/synchronousscroll.html]]」で公開しています。
}}

''【質問】''

System.Windows.Forms.DataGridコントロールを垂直方向にスクロールした時に、別のDataGridも同時にスクロールされるようにしたいのですが、どのようにすればよいのでしょうか?

''【回答】''

まずDataGridコントロールが垂直方向にスクロールされたことを知るには、Scrollイベントを捕捉したり、プロテクトメンバのGridVScrolledメソッドをオーバーライドしたり、同じくプロテクトメンバのVertScrollBarプロパティのScrollイベントを捕捉したりする方法があります。

GridVScrolledメソッドやVertScrollBarプロパティのScrollイベントを捕捉する場合は、注意が必要です。これらは、マウスのホイールを使ったスクロールや、カーソルキーによるスクロールには反応しません。よって、これらによるスクロールにも対応するためには、MouseWheelイベントなどの別の方法も併用する必要があります。

また、DataGridを指定した行までスクロールさせるには、「DataGrid内の指定された行までスクロールする」で紹介している方法が使えます。

-[[DOBON.NET .NET Tips - DataGrid内の指定された行までスクロールする>http://dobon.net/vb/dotnet/datagrid/gridvscrolled.html]]

以下に同時スクロールを可能にするDataGridの例を示します。ここではScrollイベントによりDataGridがスクロールされたことを感知しています。GridVScrolledメソッドを使った例に関しては、この記事の最後に示す掲示板のログをご覧ください。

#code(vbnet){{
'/ <summary>
'/ 同時スクロールを可能にするDataGrid
'/ </summary>
Public Class MyDataGrid
    Inherits DataGrid

    Public Sub New()
        AddHandler Me.Scroll, AddressOf MyDataGrid_Scroll
    End Sub

    Private _syncScrollGrid As MyDataGrid = Nothing
    '/ <summary>
    '/ 同時にスクロールさせるDataGrid
    '/ </summary>

    Public Property SyncScrollGrid() As MyDataGrid
        Get
            Return _syncScrollGrid
        End Get
        Set(ByVal Value As MyDataGrid)
            If Not _syncScrollGrid Is Me Then
                _syncScrollGrid = Value
            Else
                Throw New ApplicationException("自分自身に設定できません。")
            End If
        End Set
    End Property

    '/ <summary>
    '/ 指定した位置までスクロールさせる
    '/ </summary>
    '/ <param name="rowNum">この行までスクロールする</param>
    Public Sub SetTopRow(ByVal rowNum As Integer)
        Dim args As New ScrollEventArgs(ScrollEventType.LargeIncrement, rowNum)
        MyBase.GridVScrolled(Me, args)
    End Sub

    Private Sub MyDataGrid_Scroll(ByVal sender As Object, ByVal e As EventArgs)
        If Not (_syncScrollGrid Is Nothing) Then
            '指定位置までスクロール
            _syncScrollGrid.SetTopRow(VertScrollBar.Value)
        End If
        'フォーカスが別のコントロールに移動しないようにする
        Me.Focus()
    End Sub
End Class
}}

#code(csharp){{
/// <summary>
/// 同時スクロールを可能にするDataGrid
/// </summary>
public class MyDataGrid : DataGrid
{
    public MyDataGrid() : base()
    {
        this.Scroll += new EventHandler(MyDataGrid_Scroll);
    }

    private MyDataGrid _syncScrollGrid = null;
    /// <summary>
    /// 同時にスクロールさせるDataGrid
    /// </summary>
    public MyDataGrid SyncScrollGrid
    {
        get
        {
            return _syncScrollGrid;
        }
        set
        {
            if (_syncScrollGrid != this)
            {
                _syncScrollGrid = value;
            }
            else
            {
                throw new ApplicationException("自分自身に設定できません。");
            }
        }
    }

    /// <summary>
    /// 指定した位置までスクロールさせる
    /// </summary>
    /// <param name="rowNum">この行までスクロールする</param>
    public void SetTopRow(int rowNum)
    {
        ScrollEventArgs args = 
            new ScrollEventArgs(ScrollEventType.LargeIncrement, rowNum);
        base.GridVScrolled(this, args);
    }

    private void MyDataGrid_Scroll(object sender, EventArgs e)
    {
        if (_syncScrollGrid != null)
        {
            //指定位置までスクロール
            _syncScrollGrid.SetTopRow(VertScrollBar.Value);
        }
        //フォーカスが別のコントロールに移動しないようにする
        this.Focus();
    }
}
}}

このクラスを使用するには、DataGridコントロールをこのMyDataGridに置き換えます。ユーザーがスクロールするDataGridと、そのスクロールと同時にスクロールさせたいDataGridの両方にMyDataGridを使用します。そして、ユーザーがスクロールするMyDataGridのSyncScrollGridプロパティに、同時にスクロールさせたいMyDataGridを指定します。なお両者のMyDataGridの行数は必ず同じにしてください。

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

-[[DataGridの同期 | 投稿者(敬称略) おくら, NowhereMan>http://dobon.net/vb/bbs/log3-7/3992.html]]
-[[データグリッドでの画面分割 | 投稿者(敬称略) nepia>http://dobon.net/vb/bbs/log3-7/4041.html]]
-[[スクロールの同期、または抑止 | 投稿者(敬称略) nepia, NowhereMan>http://dobon.net/vb/bbs/log3-7/4150.html]]
-[[string "ff"を数値型の 255 に変換したい | 投稿者(敬称略) blessyou, 深山, nepia>http://dobon.net/vb/bbs/log3-8/4873.html]]

***DataGridを印刷する [#s3c983df]

#column(注意){{
この記事の最新版は「[[DataGridを印刷する>http://dobon.net/vb/dotnet/datagrid/printdatagrid.html]]」で公開しています。
}}

''【質問】''

System.Windows.Forms.DataGridコントロールに表示されているデータを印刷することはできますか?

''【回答】''

MSDNにそのものズバリの「コード : DataGrid の印刷」という例が紹介されています。しかしこれらは残念ながら表示されている範囲のみしか印刷できず、それ以外を印刷するには、適当な位置までスクロールして印刷するという処理を繰り返す必要があります。しかしそれも正確に行うのは難いため、この方法は実用的とは言えないでしょう。

-[[コード : DataGrid の印刷 (Visual C#)>http://www.microsoft.com/japan/msdn/library/ja/cscon/html/vclrfCodePrintingDataGridVisualC.asp]]
-[[コード : データ グリッドを印刷する (Visual Basic)>http://www.microsoft.com/japan/msdn/library/ja/dv_vbCode/html/vbtskCodeExamplePrintingDataGrid.asp]]

実際に良く使われている方法は、DataGridで表示しているデータを自分で描画して印刷する方法です。この方法はかなり手間がかかりますが、自由度が高く、融通が利きます。

ここではその方法を具体的には説明しません。しかし、このようにしてDataGridを印刷するためのクラスは数多く公開されています。以下にその幾つかを紹介させていただきますので、参考にしてください。

-[[How to Print a DataGrid in C# and .NET>http://www.c-sharpcorner.com/Graphics/DataGridPrinterMG.asp]]
-[[A class to print and print preview a DataGrid control - The Code Project - VB.NET>http://www.codeproject.com/vb/net/datagridprinter.asp]]
-[[Printing with a custom DataGrid - The Code Project - VB.NET>http://www.codeproject.com/vb/net/datagridex.asp]]
-[[DataGrid Printing Class v1.0b - The Code Project - VB.NET>http://www.codeproject.com/vb/net/DataGrid_Printing_Class.asp]]
-[[DataGrid Printing Class V1.0b - The Code Project - C# Controls>http://www.codeproject.com/cs/miscctrl/DataGridPrinter.asp]]
-[[Print a DataSet's Contents in VB.NET>http://www.thescarms.com/dotNet/PrintDataSet.asp]]
-[[Know Dot Net - Print Contents of Grid or ListView>http://www.knowdotnet.com/articles/printgriddemo.html]]

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

-[[DataGridの使い方 | 投稿者(敬称略) 満, NowhereMan>http://dobon.net/vb/bbs/log3-7/4044.html]]
-[[DataGridを印刷するのに | 投稿者(敬称略) ふむ, NowhereMan>http://dobon.net/vb/bbs/log3-7/4272.html]]

***単位を変更して描画する [#raa14e1e]

#column(注意){{
この記事の最新版は「[[単位を変更して描画する>http://dobon.net/vb/dotnet/graphics/pageunit.html]]」で公開しています。
}}

''【質問】''

メートルやインチ単位の長さを指定して描画や印刷を行いたいのですが、どのようにすればよいのでしょうか?

''【回答】''

一番簡単な方法は、描画先のGraphicsオブジェクトのPageUnitプロパティを変更してから描画するという方法でしょう。

PageUnitプロパティはページ座標で使用する長さの単位を指定するためのプロパティで、ページ変換(ページ座標からデバイス座標への変換)で使用されます。GDI+の座標系について詳しくは、MSDNの「座標系の種類」をご覧ください。

-[[座標系の種類>http://www.microsoft.com/japan/msdn/library/ja/cpguide/html/_gdiplus_Types_of_Coordinate_Systems_about.asp]]

PageUnitプロパティにはGraphicsUnit列挙体を指定しますが、GraphicsUnit列挙体には、以下のようなメンバーがあります(MSDNからの引用です)。つまり、これらの単位を指定できるわけです。

|Display|1/75インチを長さの単位に指定します。|
|Document|ドキュメント単位(1/300インチ)を長さの単位に指定します。|
|Inch|インチを長さの単位に指定します。|
|Millimeter|ミリメートルを長さの単位に指定します。|
|Pixel|デバイスピクセルを長さの単位に指定します。|
|Point|プリンタポイント(1/72インチ)を長さの単位に指定します。|
|World|ワールド単位を長さの単位に指定します。|

PageUnitプロパティを使った例を以下に示します。ここでは、フォームのOnPaintメソッドをオーバーライドすることにより、フォームに太さ0.1インチで4X2インチの長方形を描画しています。

#code(vbnet){{
Protected Overrides Sub OnPaint(ByVal e As PaintEventArgs)
    MyBase.OnPaint(e)

    'Penを用意する
    Dim redPen As New Pen(Color.Red, 0.1F)

    'インチ単位にする
    e.Graphics.PageUnit = GraphicsUnit.Inch
    '太さ0.1インチで4X2インチの長方形を描画
    e.Graphics.DrawRectangle(redPen, 0.2F, 0.4F, 4, 2)

    'Penを破棄
    redPen.Dispose()
End Sub
}}

#code(csharp){{
protected override void OnPaint(PaintEventArgs e)
{
    base.OnPaint (e);

    //Penを用意する
    Pen redPen = new Pen(Color.Red, 0.1f);
    
    //インチ単位にする
    e.Graphics.PageUnit = GraphicsUnit.Inch;
    //太さ0.1インチで4X2インチの長方形を描画
    e.Graphics.DrawRectangle(redPen, 0.2f, 0.4f, 4, 2);
    
    //Penを破棄
    redPen.Dispose();
}
}}

もし自分で描画する画面のdpi(dots per inch)を取得して、それを元にピクセル単位の長さを計算できるのであれば、そうすることもできます。dpiはその名の通り、1インチ内のピクセル数を表し、水平方向および垂直方向のdpiは、GraphicsクラスのDpiXとDpiYプロパティで取得することができます。

補足:「HOWTO: How to Make an Application Display Real Units of Measurement」では、GetDeviceCaps関数を使ってdpiを取得する方法が紹介されています。ちなみにDpiXプロパティはGdipGetDpiX関数を使っているようです。

-[[HOWTO: How to Make an Application Display Real Units of Measurement>http://support.microsoft.com/kb/127152/]]

このような方法により、先ほどと同じようにインチ単位で描画する例を示します。

#code(vbnet){{
Protected Overrides Sub OnPaint(ByVal e As PaintEventArgs)
    MyBase.OnPaint(e)

    'Penを用意する
    Dim redPen As New Pen(Color.Red, 0.1F * e.Graphics.DpiX)

    '太さ0.1インチで4X2インチの長方形を描画
    e.Graphics.DrawRectangle(redPen, _
        0.2F * e.Graphics.DpiX, 0.4F * e.Graphics.DpiX, _
        4 * e.Graphics.DpiX, 2 * e.Graphics.DpiX)

    'Penを破棄
    redPen.Dispose()
End Sub
}}

#code(csharp){{
protected override void OnPaint(PaintEventArgs e)
{
    base.OnPaint (e);
    
    //Penを用意する
    Pen redPen = new Pen(Color.Red, 0.1f * e.Graphics.DpiX);

    //太さ0.1インチで4X2インチの長方形を描画
    e.Graphics.DrawRectangle(redPen,
        0.2f * e.Graphics.DpiX, 0.4f * e.Graphics.DpiX,
        4 * e.Graphics.DpiX, 2 * e.Graphics.DpiX);

    //Penを破棄
    redPen.Dispose();
}
}}

このように長さをいちいち計算するのが面倒であれば、ScaleTransformメソッドにより、ワールド変換を使って解決することもできます。水平方向と垂直方向のdpiが同じであれば、PageScaleプロパティにより、ページ変換で行うこともできます。

ScaleTransformメソッドを使った例を以下に示します。

#code(vbnet){{
Protected Overrides Sub OnPaint(ByVal e As PaintEventArgs)
    MyBase.OnPaint(e)

    'Penを用意する
    Dim redPen As New Pen(Color.Red, 0.1F)

    'インチ単位にする
    e.Graphics.ScaleTransform(e.Graphics.DpiX, e.Graphics.DpiY)
    '太さ0.1インチで4X2インチの長方形を描画
    e.Graphics.DrawRectangle(redPen, 0.2F, 0.4F, 4, 2)

    'Penを破棄
    redPen.Dispose()
End Sub
}}

#code(csharp){{
protected override void OnPaint(PaintEventArgs e)
{
    base.OnPaint (e);
    
    //Penを用意する
    Pen redPen = new Pen(Color.Red, 0.1f);

    //インチ単位にする
    e.Graphics.ScaleTransform(e.Graphics.DpiX, e.Graphics.DpiY);
    //太さ0.1インチで4X2インチの長方形を描画
    e.Graphics.DrawRectangle(redPen, 0.2f, 0.4f, 4, 2);

    //Penを破棄
    redPen.Dispose();
}
}}

もちろんPageUnitプロパティが使えるならばこのような方法を使う必要はないと思いますが、PageUnitプロパティで指定できない単位を使いたい場合はこのような方法が必要になるでしょう。最後に蛇足ですが、寸単位で描画する例を示します。ここでは、DpiXとDpiYが同じものとし、PageScaleプロパティを使っています。

#code(vbnet){{
Protected Overrides Sub OnPaint(ByVal e As PaintEventArgs)
    MyBase.OnPaint(e)

    'Penを用意する
    Dim redPen As New Pen(Color.Red, 0.1F)

    '寸単位にする(1インチを0.8382寸とする)
    e.Graphics.PageScale = e.Graphics.DpiX / 0.8382F
    '太さ0.1寸で4X2寸の長方形を描画
    e.Graphics.DrawRectangle(redPen, 0.2F, 0.4F, 4, 2)

    'Penを破棄
    redPen.Dispose()
End Sub
}}

#code(csharp){{
protected override void OnPaint(PaintEventArgs e)
{
    base.OnPaint (e);
    
    //Penを用意する
    Pen redPen = new Pen(Color.Red, 0.1f);

    //寸単位にする(1インチを0.8382寸とする)
    e.Graphics.PageScale = e.Graphics.DpiX / 0.8382f;
    //太さ0.1寸で4X2寸の長方形を描画
    e.Graphics.DrawRectangle(redPen, 0.2f, 0.4f, 4, 2);

    //Penを破棄
    redPen.Dispose();
}
}}

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

-[[印刷位置をmm単位で指定するには | 投稿者(敬称略) カバチャン, ポン>http://dobon.net/vb/bbs/log3-6/3695.html]]
-[[Pictrueboxのイメージを縦・横1インチで印刷したい | 投稿者(敬称略) kon, ポン>http://dobon.net/vb/bbs/log3-7/3986.html]]
-[[印刷位置の設定について | 投稿者(敬称略) パオ, 管理人, こど。>http://dobon.net/vb/bbs/log3-4/1972.html]]
-[[マウスの座標(ミリメートル) | 投稿者(敬称略) れちりん, ant, 管理人>http://dobon.net/vb/bbs/log3-20/12260.html]]

***VB6のScaleHeight、ScaleWidthプロパティに代わるものは? [#ye6a84bf]

#column(注意){{
この記事の最新版は「[[VB6のScaleHeight、ScaleWidthプロパティに代わるものは?>http://dobon.net/vb/dotnet/vb6/scaleheight.html]]」で公開しています。
}}

''【質問】''

VB6のFormオブジェクトのScaleHeight、ScaleWidthプロパティに代わるものは、C#やVB.NETでは何ですか?

''【回答】''

MSDNの「Visual Basic .NET における Form オブジェクトの変更点」によると、これらに代わるものはありません。ただし、座標の単位がピクセルの場合は、FormクラスのClientRectangleプロパティやClientSizeプロパティのHeight、Widthプロパティがその代わりとして使えます。

-[[Visual Basic .NET における Form オブジェクトの変更点>http://www.microsoft.com/japan/msdn/library/ja/vbcon/html/vxconchangestoformobjectinvisualbasicnet.asp]]

ただし、コントロールの大きさをフォームの大きさに合わせて変える目的でScaleHeightとScaleWidthプロパティを使用するのであれば、DockやAnchorプロパティを使うのがよいでしょう。

MSDNの「ScaleMode がサポートされていない」によると、VB6のコードをVS.NETによりアップグレードすると、VB6.TwipsToPixelsXやTwipsToPixelsYを使って単位をtwipからピクセルに変換するようです。

-[[ScaleMode がサポートされていない>http://msdn.microsoft.com/library/ja/vbcon/html/vbup2038.asp]]

なおC#でTwipsToPixelsXやTwipsToPixelsYを使うには、「参照設定」に「Microsoft.VisualBasic.Compatibility」を追加し、

#code(csharp){{
Microsoft.VisualBasic.Compatibility.VB6.Support.TwipsToPixelsX(500)
Microsoft.VisualBasic.Compatibility.VB6.Support.TwipsToPixelsY(800)
}}

のようにして呼び出します。もしこれらのメソッドを使いたくないのであれば、次のようにしてTwipsPerPixelXとTwipsPerPixelYの値を計算することもできます。なおここではフォームクラス内に記述するものとし、フォームのある画面のTwipsPerPixelXとTwipsPerPixelYの値を計算しています。

#code(vbnet){{
Dim g As Graphics = Me.CreateGraphics()
Dim TwipsPerPixelX As Single = 1440.0F / g.DpiX
Dim TwipsPerPixelY As Single = 1440.0F / g.DpiY
g.Dispose()
}}

#code(csharp){{
Graphics g = this.CreateGraphics();
float TwipsPerPixelX = 1440f / g.DpiX;
float TwipsPerPixelY = 1440f / g.DpiY;
g.Dispose();
}}

しかしながら、TwipsToPixelsXやTwipsToPixelsYはできるだけ使わない方が良いでしょう。

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

-[[ScaleHeightの使い方 | 投稿者(敬称略) FUM, NowhereMan>http://dobon.net/vb/bbs/log3-7/4017.html]]
-[[twip→ピクセルに変換する方法 | 投稿者(敬称略) イニ, java.lang.Nullpo>http://dobon.net/vb/bbs/log3-10/5849.html]]

**コメント [#q990360e]
#comment

//これより下は編集しないでください
#pageinfo([[:Category/.NET]],2006-01-28 (土) 18:00:00,DOBON!,2010-03-22 (月) 03:06:23,DOBON!)


[ トップ ]   [ 新規 | 子ページ作成 | 一覧 | 単語検索 | 最終更新 | ヘルプ ]