ファイルをバイナリ形式で表示する (C#/VB Winow Form編)
ファイルデータ・ビューでは以下のイベントを処理します。
描画イベント。
垂直スクロールバーの操作イベント。
水平スクロールバーの操作イベント。
マウスホイールの操作イベント。
マウスの左ボタンを押下したときの操作イベント。
マウスホイールの操作イベント。
ウインドウサイズの変更イベント。
ファイルデータの描画は以下の手順で行います。
ファイルデータ表示ビューの背景を塗りつぶしてクリアします。
-
以下の固定幅フォントを作成します。
項目 値 フォント名 "MS ゴシック" フォントサイズ 12 作成したフォントの幅と高さを取得します。
-
横方向の描画開始座標を水平スクロールバーの現在の値から算出します。
(横方向の描画開始座標) = (水平スクロールバーのつまみ位置) × (フォントの幅) × -1
水平スクロールバーのつまみが右に移動するとビューの画像は左に移動することになるので、オフセットに-1をかけます。
1画面に表示可能な文字数を計算します。
-
1画面に描画可能な行数を画面の高さとフォントの高さから算出します。
(描画可能な行数) = (画面の高さ + (フォントの高さ - 1)) ÷ (フォントの高さ)
画面の高さがフォントの高さで割り切れない場合に(画面の高さ)÷(フォントの高さ)では端数の領域が空白になってしまうので、(フォントの高さ-1)だけ画面の高さを割り増しして端数の領域も1行分として増やして文字を描画するようにして、空白にならないようにします。
最初にスケールを描画します。
-
次にファイルデータを描画します。
スケールの1行を除いた1画面に描画可能な行数分のファイルデータを1行づつバイナリ形式で描画します。
最後に作成したフォントを破棄します。
C# ソースコード [FileDataView.cs]
// -----------------------------------------------------------------------
/// <summary>
/// ファイル・データのバイナリ表示画面を描画する.
/// </summary>
/// <param name="sender">
/// イベントを発生させたオブジェクト.
/// </param>
/// <param name="e">
/// イベント・パラメータ.
/// </param>
private void FileData_Paint( object sender, PaintEventArgs e )
{
var rc = e.ClipRectangle;
var g = e.Graphics;
//! 画面をクリアする.
g.FillRectangle( Brushes.White, 0, 0, rc.Width, rc.Height );
//! ファイルデータが無い場合は描画を行わない.
if( FileBuff == null ){
return;
}
//! フォントを作成する.
Font fnt = new Font( "MS ゴシック", 12 );
float x = 0;
float y = 0;
int colomuns = 1;
using( StringFormat sf = new StringFormat( StringFormat.GenericTypographic ) ){
//! 1文字の幅を取得する.
var fs = g.MeasureString( "0", fnt, rc.Width, sf );
//! 水平方向の書き出し座標を計算する.
x = -1 * (FileDataHScroll.Value * fs.Width);
//! 1画面に表示可能な文字数を計算する.
if( fs.Width > 0 ){
colomuns = (int)(rc.Width / fs.Width);
}
}
//! 1画面に表示可能な行数を計算する.
int lines = (fnt.Height > 0) ? ((rc.Height) + (fnt.Height - 1)) / fnt.Height : 1;
//! スケールを描画する.
y = DrawScale( g, rc, fnt, x, y );
//! ファイルデータを16進数で描画する.
int adr = FileDataVScroll.Value * 16;
for( int i = 1; i <= lines; i++ ){
DrawBinaryData( g, rc, fnt, x, y, adr );
adr += 16;
y += fnt.Height;
}
//! フォントを破棄する.
fnt.Dispose();
}
VB ソースコード [FileDataView.vb]
'' -----------------------------------------------------------------------
''' <summary>
''' ファイル・データのバイナリ表示画面を描画する.
''' </summary>
''' <param name="sender">
''' イベント対象のオブジェクト.
''' </param>
''' <param name="e">
''' イベントパラメータ.
''' </param>
Private Sub FileData_Paint( sender As Object, e As PaintEventArgs ) Handles FileData.Paint
Dim rc As Rectangle = e.ClipRectangle
Dim g As Graphics = e.Graphics
' 画面をクリアする.
g.FillRectangle( Brushes.White, 0, 0, rc.Width, rc.Height )
' ファイルデータが無い場合は描画を行わない.
If FileBuff Is Nothing Then
Return
End If
' フォントを作成する.
Dim fnt As New Font( "MS ゴシック", 12 )
Dim x As Single = 0
Dim y As Single = 0
Dim colomuns As Integer = 1
Using sf As New StringFormat( StringFormat.GenericTypographic )
' 1文字の幅を取得する.
Dim fs = g.MeasureString( "0", fnt, rc.Width, sf )
' 水平方向の書き出し座標を計算する.
x = -1 * (FileDataHScroll.Value * fs.Width)
' 1画面に表示可能な文字数を計算する.
If fs.Width > 0 Then
colomuns = (rc.Width / fs.Width)
End If
End Using
' 1画面に表示可能な行数を計算する.
Dim lines As Integer = 1
If fnt.Height > 0 Then
lines = ((rc.Height) + (fnt.Height - 1)) / fnt.Height
End If
' スケールを描画する.
y = DrawScale( g, rc, fnt, x, y )
' ファイルデータを16進数で描画する.
Dim adr As Integer = FileDataVScroll.Value * 16
For i As Integer = 1 To lines
DrawBinaryData( g, rc, fnt, x, y, adr )
adr += 16
y += fnt.Height
Next
' フォントを破棄する.
fnt.Dispose()
End Sub
バイナリデータが見易くなるようにスケールを描画します。
垂直方向にスクロールしてもスケールは常に表示するようにしますが、水平方向のスクロールした場合はスケールも移動するようにします。
オフセットアドレスを描画する領域の背景を塗りつぶしておきます。
C# ソースコード [FileDataView.cs]
// -----------------------------------------------------------------------
/// <summary>
/// スケールを描画する.
/// </summary>
/// <param name="g">
/// 描画に使用するグラフィックス.
/// </param>
/// <param name="rc">
/// クライアント領域のサイズ.
/// </param>
/// <param name="fnt">
/// フォント.
/// </param>
/// <param name="x">
/// 描画開始するX座標.
/// </param>
/// <param name="y">
/// 描画開始するY座標.
/// </param>
/// <returns>
/// 次に描画を開始するY座標.
/// </returns>
private float DrawScale( Graphics g, Rectangle rc, Font fnt, float x, float y )
{
using( StringFormat sf = new StringFormat( StringFormat.GenericTypographic ) ){
//! アドレスを描画する領域を塗りつぶす.
var adrArea = g.MeasureString( "000000000", fnt, rc.Width, sf );
g.FillRectangle( Brushes.Gray, x, 0, adrArea.Width, rc.Height );
//! スケールを描画する.
g.FillRectangle( Brushes.Gray, 0, 0, rc.Width, fnt.Height );
g.DrawString( "ADDRESS: +0 +1 +2 +3 +4 +5 +6 +7 +8 +9 +A +B +C +D +E +F ASCII", fnt, Brushes.White, x, y, sf );
y += fnt.Height;
}
return y;
}
VB ソースコード [FileDataView.vb]
'' -----------------------------------------------------------------------
''' <summary>
''' スケールを描画する.
''' </summary>
''' <param name="g">
''' 描画に使用するグラフィックス.
''' </param>
''' <param name="rc">
''' クライアント領域のサイズ.
''' </param>
''' <param name="fnt">
''' フォント.
''' </param>
''' <param name="x">
''' 描画開始するX座標.
''' </param>
''' <param name="y">
''' 描画開始するY座標.
''' </param>
''' <returns>
''' 次に描画を開始するY座標.
''' </returns>
Private Function DrawScale(g As Graphics, rc As Rectangle, fnt As Font, x As Single, y As Single) As Single
Using sf As New StringFormat(StringFormat.GenericTypographic)
' アドレスを描画する領域を塗りつぶす.
Dim adrArea As SizeF = g.MeasureString("000000000", fnt, rc.Width, sf)
g.FillRectangle( Brushes.Gray, x, 0, adrArea.Width, rc.Height )
' スケールを描画する.
g.FillRectangle( Brushes.Gray, 0, 0, rc.Width, fnt.Height )
g.DrawString("ADDRESS: +0 +1 +2 +3 +4 +5 +6 +7 +8 +9 +A +B +C +D +E +F ASCII", fnt, Brushes.White, x, y, sf)
y += fnt.Height
End Using
Return y
End Function
1行分のファイルデータの表示を行います。
最初に描画する行のファイル上のオフセット・アドレスを描画します。
オフセット・アドレスは16進数で8桁固定で描画するので、オフセット・アドレスを16進数8桁の文字列に変換して描画します。
次にファイルデータを16バイト(1行分)描画します。
-
forループで以下の処理を16回実行します。
16バイト描画する途中でファイルデータが終わってしまった場合は、そこでファイルデータの描画は終了にします。
ファイルデータは16進数で2桁固定で描画するので、ファイルデータを16進数2桁の文字列に変換して描画します。
ファイルデータをASCII文字に変換して、描画用のASCII文字列も併せて作ります。
(ファイルデータが0x20以上0x7F以下の場合は該当するASCII文字にし、それ以外は「.」に変換します。) 最後にforループ内で作成しておいたASCII文字列を描画します。
C# ソースコード [FileDataView.cs]
// -----------------------------------------------------------------------
/// <summary>
/// 1行分のバイナリ・データを描画する.
/// </summary>
/// <param name="g">
/// 描画に使用するグラフィックス.
/// </param>
/// <param name="rc">
/// クライアント領域のサイズ.
/// </param>
/// <param name="fnt">
/// フォント.
/// </param>
/// <param name="x">
/// 描画開始するX座標.
/// </param>
/// <param name="y">
/// 描画開始するY座標.
/// </param>
/// <param name="adr">
/// 描画開始するバイナリデータのアドレス.
/// </param>
private void DrawBinaryData( Graphics g, Rectangle rc, Font fnt, float x, float y, int adr )
{
if( adr >= FileBuff.Length ){
return;
}
using( StringFormat sf = new StringFormat( StringFormat.GenericTypographic ) ){
var fs = g.MeasureString( "0", fnt, rc.Width, sf );
//! アドレスを描画する.
string adrText = adr.ToString( "X8" );
g.DrawString( adrText, fnt, Brushes.White, x, y, sf );
var x1 = x + (9 * fs.Width);
string ascii = string.Empty;
for( int i = 0; i < 16; i++ ){
string byteText = string.Empty;
if( (adr + i) < FileBuff.Length ){
uint c = FileBuff[adr + i];
byteText = " " + c.ToString( "X2" );
ascii += ((0x20 <= c && c < 0x80) ? (char)c : '.');
}else{
byteText = " ";
}
g.DrawString( byteText, fnt, Brushes.Black, x1, y, sf );
x1 += (3 * fs.Width);
}
string asciiText = " " + ascii;
g.DrawString( asciiText, fnt, Brushes.Black, x1, y, sf );
}
}
VB ソースコード [FileDataView.vb]
'' -----------------------------------------------------------------------
''' <summary>
''' 1行分のバイナリ・データを描画する.
''' </summary>
''' <param name="g">
''' 描画に使用するグラフィックス.
''' </param>
''' <param name="rc">
''' クライアント領域のサイズ.
''' </param>
''' <param name="fnt">
''' フォント.
''' </param>
''' <param name="x">
''' 描画開始するX座標.
''' </param>
''' <param name="y">
''' 描画開始するY座標.
''' </param>
''' <param name="adr">
''' 描画開始するバイナリデータのアドレス.
''' </param>
Private Sub DrawBinaryData(g As Graphics, rc As Rectangle, fnt As Font, x As Single, y As Single, adr As Integer)
If adr >= FileBuff.Length Then
Return
End If
Using sf As New StringFormat( StringFormat.GenericTypographic )
Dim fs As SizeF = g.MeasureString( "0", fnt, rc.Width, sf )
' アドレスを描画する.
Dim adrText As String = adr.ToString( "X8" )
g.DrawString( adrText, fnt, Brushes.White, x, y, sf )
Dim x1 As Single = x + (9 * fs.Width)
Dim ascii As String = String.Empty
For i As Integer = 0 To 15
Dim byteText As String = String.Empty
If (adr + i) < FileBuff.Length Then
Dim c As UInteger = FileBuff(adr + i)
byteText = " " + c.ToString("X2")
If &H20 <= c And c < &H80 Then
ascii += Convert.ToChar( c )
Else
ascii += "."
End If
Else
byteText = " "
End If
g.DrawString( byteText, fnt, Brushes.Black, x1, y, sf )
x1 += (3 * fs.Width)
Next
Dim asciiText As String = " " + ascii
g.DrawString( asciiText, fnt, Brushes.Black, x1, y, sf )
End Using
End Sub
ウインドウサイズ等が変更されると、スクロールバーのページサイズの変更が必要になります。
C# ソースコード [FileDataView.cs]
// -----------------------------------------------------------------------
/// <summary>
/// スクロールバーを設定する.
/// </summary>
private void SetScrollBar()
{
//! クライアント領域のサイズを取得する.
System.Drawing.Size rc = FileData.ClientSize;
//! グラフィックス・オブジェクトを取得する.
Graphics g = FileData.CreateGraphics();
//! フォントを作成する.
Font fnt = new Font( "MS ゴシック", 12 );
//! 1画面に表示可能な行数を計算する.
int lines = (fnt.Height > 0) ? ((rc.Height) + (fnt.Height - 1)) / fnt.Height : 1;
//! 1画面に表示可能な文字数を計算する.
int colomuns = 1;
using( StringFormat sf = new StringFormat( StringFormat.GenericTypographic ) ){
//! 1文字の幅を取得する.
var fs = g.MeasureString( "0", fnt, rc.Width, sf );
//! 1画面に表示可能な文字数を計算する.
if( fs.Width > 0 ){
colomuns = (int)(rc.Width / fs.Width);
}
}
//! スクロールバーのつまみ幅を画面に合わせて再設定する.
FileDataVScroll.LargeChange = lines / 2;
FileDataHScroll.LargeChange = colomuns / 2;
//! フォントを破棄する.
fnt.Dispose();
//! グラフィックス・オブジェクトを解放する.
g.Dispose();
}
VB ソースコード [FileDataView.vb]
'' -----------------------------------------------------------------------
''' <summary>
''' スクロールバーを設定する.
''' </summary>
Private Sub SetScrollBar()
' クライアント領域のサイズを取得する.
Dim rc As System.Drawing.Size = FileData.ClientSize
' グラフィックス・オブジェクトを取得する.
Dim g As Graphics = FileData.CreateGraphics()
' フォントを作成する.
Dim fnt As New Font( "MS ゴシック", 12 )
' 1画面に表示可能な行数を計算する.
Dim lines As Integer = 1
If fnt.Height > 0 Then
lines = ((rc.Height) + (fnt.Height - 1)) / fnt.Height
End If
Dim colomuns As Integer = 1
Using sf As New StringFormat( StringFormat.GenericTypographic )
' 1文字の幅を取得する.
Dim fs = g.MeasureString( "0", fnt, rc.Width, sf )
' 1画面に表示可能な文字数を計算する.
If fs.Width > 0 Then
colomuns = (rc.Width / fs.Width)
End If
End Using
' スクロールバーのつまみ幅を画面に合わせて再設定する.
FileDataVScroll.LargeChange = lines / 2
FileDataHScroll.LargeChange = colomuns / 2
' フォントを破棄する.
fnt.Dispose()
' グラフィックス・オブジェクトを解放する.
g.Dispose()
End Sub
最初にファイルデータ・ビューの表示領域のサイズを取得します。
描画に使用するフォントを作成します。
次に表示領域のサイズとフォントの高さから1ページに表示可能な行数を計算します。
次に表示領域のサイズとフォントの幅から1ページに表示可能な文字数を計算します。
垂直スクロールバーの1ページの移動量(FileDataVScroll.LargeChange)を1画面あたりの描画行数の1/2にします。
水平スクロールバーの1ページの移動量(FileDataHScroll.LargeChange)を1画面あたりの描画文字数の1/2にします。
垂直スクロールバーの操作イベント処理を行うために、垂直スクロールバー(FileDataVScroll)のScroll()メソッドをオーバーライドします。
「FileDataVScroll」の「Scroll」プロパティに「FileDataVScroll_Scroll」を追加します。
垂直スクロールバーが操作されたときは、バイナリデータの表示ビューを再描画します
C# ソースコード [FileDataView.cs]
// -----------------------------------------------------------------------
/// <summary>
/// バイナリ表示画面の垂直スクロールバーが操作されたときのイベント処理.
/// </summary>
/// <param name="sender">
/// イベントを発生させたオブジェクト.
/// </param>
/// <param name="e">
/// イベント・パラメータ.
/// </param>
private void FileDataVScroll_Scroll( object sender, ScrollEventArgs e )
{
//! バイナリ表示画面を再描画する.
splitContainer2.Panel2.Refresh();
}
VB ソースコード [FileDataView.vb]
'' -----------------------------------------------------------------------
''' <summary>
''' バイナリ表示画面の垂直スクロールバーが操作されたときのイベント処理.
''' </summary>
''' <param name="sender">
''' イベント対象のオブジェクト.
''' </param>
''' <param name="e">
''' イベントパラメータ.
''' </param>
Private Sub FileDataVScroll_Scroll(sender As Object, e As ScrollEventArgs) Handles FileDataVScroll.Scroll
' バイナリ表示画面を再描画する.
SplitContainer2.Panel2.Refresh()
End Sub
水平スクロールバーの操作イベント処理を行うために、水平スクロールバー(FileDataHScroll)のScroll()メソッドをオーバーライドします。
「FileDataHScroll」の「Scroll」プロパティに「FileDataHScroll_Scroll」を追加します。
水平スクロールバーが操作されたときは、バイナリデータの表示ビューを再描画します
C# ソースコード [FileDataView.cs]
// -----------------------------------------------------------------------
/// <summary>
/// バイナリ表示画面の水平スクロールバーが操作されたときのイベント処理.
/// </summary>
/// <param name="sender">
/// イベントを発生させたオブジェクト.
/// </param>
/// <param name="e">
/// イベント・パラメータ.
/// </param>
private void FileDataHScroll_Scroll( object sender, ScrollEventArgs e )
{
//! バイナリ表示画面を再描画する.
splitContainer2.Panel2.Refresh();
}
VB ソースコード [FileDataView.vb]
'' -----------------------------------------------------------------------
''' <summary>
''' バイナリ表示画面の水平スクロールバーが操作されたときのイベント処理.
''' </summary>
''' <param name="sender">
''' イベント対象のオブジェクト.
''' </param>
''' <param name="e">
''' イベントパラメータ.
''' </param>
Private Sub FileDataHScroll_Scroll(sender As Object, e As ScrollEventArgs) Handles FileDataHScroll.Scroll
' バイナリ表示画面を再描画する.
SplitContainer2.Panel2.Refresh()
End Sub
ホイールマウスの操作イベントはIDE(Visual Studio)からは設定できなので、ビューの初期化処理内で、イベントの登録を行います。
C# ソースコード [FileDataView.cs]
//! ホイールイベントの追加.
FileData.MouseWheel += new System.Windows.Forms.MouseEventHandler( FileData_MouseWheel );
VB ソースコード [FileDataView.vb]
' ホイールイベントの追加.
AddHandler FileData.MouseWheel, New System.Windows.Forms.MouseEventHandler( AddressOf FileData_MouseWheel )
マウスホイールの操作イベントでは以下の処理を行います。
MouseEventArgs.Deltaからマウスホイールの移動方向を取得します。
-
マウスの移動量は以下の式で求めます。
(マウスの移動量) = (マウスホイールの移動方向) × MouseWheelScrollLines ÷ 120
マウスホイールの移動量が正数の場合、スクロールバーの上矢印ボタンを押したときと同じ動作、
マウスホイールの移動量が負数の場合、スクロールバーの下矢印ボタンを押したときと同じ動作になるようにします。 -
マウスの現在のポジションにマウスホイールの移動量を引き算して移動後のマウスポジションを計算します。
このとき、スクロールバーの最小値と最大値を超えないように補正します。 計算したマウスポジションを垂直スクロールバーにセットします。
最後にバイナリ表示ビューを再描画します.
ソースコードを以下に示します。
C# ソースコード [FileDataView.cs]
// -----------------------------------------------------------------------
/// <summary>
/// マウス・ホイールのイベント処理.
/// </summary>
/// <param name="sender">
/// イベントを発生させたオブジェクト.
/// </param>
/// <param name="e">
/// イベント・パラメータ.
/// </param>
private void FileData_MouseWheel( object sender, MouseEventArgs e )
{
//! スクロール量(方向)の表示.
var val = FileDataVScroll.Value - (e.Delta * SystemInformation.MouseWheelScrollLines / 120);
if( val < FileDataVScroll.Minimum ){
val = FileDataVScroll.Minimum;
}
int maximun = FileDataVScroll.Maximum - (FileDataVScroll.LargeChange - 1);
if( val > maximun ){
val = maximun;
}
FileDataVScroll.Value = val;
//! バイナリ表示画面を再描画する.
splitContainer2.Panel2.Refresh();
}
VB ソースコード [FileDataView.vb]
'' -----------------------------------------------------------------------
''' <summary>
''' マウス・ホイールのイベント処理.
''' </summary>
''' <param name="sender">
''' イベント対象のオブジェクト.
''' </param>
''' <param name="e">
''' イベントパラメータ.
''' </param>
Private Sub FileData_MouseWheel( sender As Object, e As MouseEventArgs )
' スクロール量(方向)の表示.
Dim val As Single = FileDataVScroll.Value - (e.Delta * SystemInformation.MouseWheelScrollLines / 120)
If val < FileDataVScroll.Minimum Then
val = FileDataVScroll.Minimum
End If
Dim maximun As Integer = FileDataVScroll.Maximum - (FileDataVScroll.LargeChange - 1)
If val > maximun Then
val = maximun
End If
FileDataVScroll.Value = val
' バイナリ表示画面を再描画する.
splitContainer2.Panel2.Refresh()
End Sub
マウスホイールの操作イベントがPictureBoxにフォーカスが無いと受け取れない為、PictureBoxをクリックしたときにフォーカスを移すようにします。
その為、PictureBoxでマウスを左クリックしたときのイベント処理を追加し、イベントが発生したときにPictureBoxにフォーカスを移動します。
マウスの左ボタンを押下したときのイベント処理を行うために、Click()メソッドをオーバーライドします。
「FileData」の「Click」プロパティに「FileData_Click」を追加します。
C# ソースコード [FileDataView.cs]
// -----------------------------------------------------------------------
/// <summary>
/// ファイルデータ表示ビューがクリックされたときのイベント処理.
/// </summary>
/// <param name="sender">
/// イベントを発生させたオブジェクト.
/// </param>
/// <param name="e">
/// イベント・パラメータ.
/// </param>
private void FileData_Click( object sender, EventArgs e )
{
//! ピクチャボックスにフォーカスを移す.
FileData.Focus();
}
VB ソースコード [FileDataView.vb]
'' -----------------------------------------------------------------------
''' <summary>
''' ファイルデータ表示ビューがクリックされたときのイベント処理.
''' </summary>
''' <param name="sender">
''' イベントを発生させたオブジェクト.
''' </param>
''' <param name="e">
''' イベント・パラメータ.
''' </param>
Private Sub FileData_Click( sender As Object, e As EventArgs ) Handles FileData.Click
' ピクチャボックスにフォーカスを移す.
FileData.Focus()
End Sub
ウインドウサイズが変更されると、ビューの描画できる領域が変わるので、スクロールバーのつまみの長さをビューの描画領域に合わせて変更します。
その為、PictureBoxのウインドウサイズが変更されたときのイベント処理を追加し、イベントが発生したときにスクロールバーの再設定を行います。
ウインドウサイズが変更されたときのイベント処理を行うために、SizeChanged()メソッドをオーバーライドします。
「FileData」の「SizeChanged」プロパティに「FileData_SizeChanged」を追加します。
C# ソースコード [FileDataView.cs]
// -----------------------------------------------------------------------
/// <summary>
/// ウインドウサイズが変更されたときのイベント処理.
/// </summary>
/// <param name="sender">
/// イベントを発生させたオブジェクト.
/// </param>
/// <param name="e">
/// イベント・パラメータ.
/// </param>
private void FileData_SizeChanged( object sender, EventArgs e )
{
//! スクロールバーを設定する.
SetScrollBar();
}
VB ソースコード [FileDataView.vb]
'' -----------------------------------------------------------------------
''' <summary>
''' ウインドウサイズが変更されたときのイベント処理.
''' </summary>
''' <param name="sender">
''' イベントを発生させたオブジェクト.
''' </param>
''' <param name="e">
''' イベント・パラメータ.
''' </param>
Private Sub FileData_SizeChanged( sender As Object, e As EventArgs ) Handles FileData.SizeChanged
' スクロールバーを設定する.
SetScrollBar()
End Sub