Back to Top

プログラムの覚書

Category: C#

.NET C#

C# テキストボックス(TextBox)入力制御

Visual Studio等でC#を使用してGUIアプリケーションを作成する場合に使用するTextBoxの説明です。

TextBoxで入力文字(数値・実数・カタカナ・ひらがな・全角文字・半角文字 など)制御をする際の例を記載します。

 

文字列制御関数

入力文字の制御を行いたい文字列の列挙体 InputTextType を作成します。以下の列挙体は例ですので、制御した文字列項目を追加作成して下さい。(列挙体を作成するのは汎用性をもたせるためです)

//入力文字列種別列挙体
public enum InputTextType : short
{
    // 文字(チェックなし)
    String,

    // 整数値
    Digit,

    // 全角カタカナ
    Katakana,
}

特定の文字列に含まれる文字(整数文字・全角カタカナ・TELなど)を判断する関数CheckChar()

特定の文字列を判断する関数CheckString()

を作成します。(文字チェック関数・文字列チェック関数を作成するのは、汎用性を持たせるためです)

//文字のチェックをします。
static public bool CheckChar(char c, InputTextType mode)
{
    bool bc = false;

    switch (mode)
    {
        case InputTextType.Digit:
            bc = IsHalfDigit(c);
            break;
        case InputTextType.Katakana:
            bc = IsDblKatakana(c);
            break;
        default:
            break;
    }

    return (bc);
}

//文字列のチェックをします。
static public bool CheckString(string s, InputTextType mode)
{
    bool bc = false;

    switch (mode)
    {
        case InputTextType.Digit:
            bc = IsHalfDigitStr(s);
            break;
        case InputTextType.Katakana:
            bc = IsDblKatakanaStr(s);
            break;
        default:
            break;
    }

    return (bc);
}

※文字と文字列を判断する関数等の例(IsHalfDigitIsHalfDigitStr関数等は特定文字の判断をご覧ください)

 

半角数値入力制御の例

TextBoxの入力制御は、Enter・Leave・KeyPress・KeyDownイベントで制御します。

EnterとLeaveではおもに、[ENTER]キーではなく、マウスでフォーカス移動された時のために処理しています。

KeyPressでは、特定文字列に含まれる文字以外は、入力出来ないようにしています。

KeyDownでは、[ENTER]キーを押された場合と、特殊キー(ESC・↑・↓)の処理をしています。

private void textBox1_Enter(object sender, EventArgs e)
{
    ((TextBox)sender).ClearUndo();
    ((TextBox)sender).SelectAll();
}

private void textBox1_Leave(object sender, EventArgs e)
{
    ((TextBox)sender).Undo();
}

private void textBox1_KeyPress(object sender, KeyPressEventArgs e)
{
    if (e.KeyChar == '\b')
    {
        return;
    }

    if (!CheckChar(e.KeyChar, InputTextType.Digit))
    {
        e.Handled = true;
    }
}

private void textBox1_KeyDown(object sender, KeyEventArgs e)
{
    switch (e.KeyCode)
    {
        case Keys.Up:
            this.ProcessTabKey(false);
            break;
        case Keys.Down:
            this.ProcessTabKey(true);
            break;
        case Keys.Tab:
            e.Handled = false;
            break;
        case Keys.Escape:
            ((TextBox)sender).Undo();
            ((TextBox)sender).ClearUndo();
            break;
        case Keys.Enter:
            bool bc = CheckString(((TextBox)sender).Text, InputTextType.Digit);
            if (bc)
            {
                ((TextBox)sender).ClearUndo();
                this.ProcessTabKey(true);
                e.Handled = false;
            }
            break;
        default:
            break;
    }
}

CheckChar()・とCheckString()のInputTextType列挙体の値を変えることにより入力文字列を制御できます。

以上TextBoxでの入力制御の方法の例です。

※ちなみに以上の制御では、クリップボードによるコピーは避けられません。

 

C# ランダムファイル

ランダムファイルをアクセスする方法を記載します

FileStreamによるアクセス

string FilePath = @"C:\work\myrfile.txt";
int RecodeLength = 15;
int RecNo = 1;

byte[] buffer = new byte[RecodeLength];

System.IO.FileStream fs = new System.IO.FileStream(FilePath, System.IO.FileMode.OpenOrCreate, System.IO.FileAccess.ReadWrite);

//レコード数取得(Rows)
long RecodeCount =  fs.Length / RecodeLength;

//レコード読み込み(Get)
fs.Position = RecodeLength * (RecNo - 1);       //Seek()でも出来る
fs.Read(buffer, 0, RecodeLength);

//レコード書き込み(Put)
fs.Position = RecodeLength * (RecNo - 1);
fs.Write(buffer, 0, RecodeLength);

fs.Close();

・FileStreamの場合byteで扱うので、その後の処理をするとき、byte[]をstructとうにメモリーコピーする必要が出てくる。

 

ランダムアクセス関数例

class RandomFile : IDisposable
{
    private FileStream _FStream = null;         //ファイルストリーム
    private int _RecodeLength = 1;              //レコード長(デフォルト)

    private Type _Type = null;                  //構造体タイプ(構造体IO用)
    private byte[] _buffer;                     //IO用中間バッファ(構造体IO用)


    /// <summary>
    /// コンストラクタ
    /// </summary>
    public RandomFile()
    {
    }

    private bool disposed = false;      //重複する呼び出しの制御

    private void Dispose(bool disposing)
    {
        if (disposed)                   //既に解放済みかチェック
            return;

        if (disposing)
        {
        }

        Close();

        disposed = true;                //解放済みフラグを立てる
    }

    public virtual void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }

    /// <summary>
    /// デストラクタ
    /// </summary>
    ~RandomFile()
    {
        Dispose(false);
    }

    /// <summary>
    /// ファイルオープン
    /// </summary>
    /// <param name="fpath">ファイル名</param>
    /// <param name="RecodeLength">レコード長</param>
    /// <returns></returns>
    public void Open(string fpath, int RecodeLength)
    {
        _FStream = new System.IO.FileStream(fpath, System.IO.FileMode.OpenOrCreate, System.IO.FileAccess.ReadWrite);

        if (0 < RecodeLength)                   //レコード長
        {
            _RecodeLength = RecodeLength;
        }
    }

    /// <summary>
    /// ファイルオープン構造体IO用
    /// </summary>
    /// <param name="fpath"></param>
    /// <param name="obj"></param>
    public void Open(string fpath, object obj)
    {
        _FStream = new System.IO.FileStream(fpath, System.IO.FileMode.OpenOrCreate, System.IO.FileAccess.ReadWrite);

        _RecodeLength = Marshal.SizeOf(obj);

        _buffer = new byte[_RecodeLength];
        _Type = obj.GetType();
    }

    /// <summary>
    /// ファイルクローズ
    /// </summary>
    /// <returns></returns>
    public void Close()
    {
        if (_FStream != null)
        {
            _FStream.Close();
            _FStream = null;
        }

        _buffer = null;
        _Type = null;
    }

    /// <summary>
    /// 
    /// </summary>
    /// <returns></returns>
    public long Rows()
    {
        long count = -1;

        if (_FStream != null)
        {
            count = _FStream.Length / (long)_RecodeLength;
        }

        return (count);
    }

    /// <summary>
    /// 1レコード読み込む
    /// </summary>
    /// <param name="buffer">読み込むバッファ</param>
    /// <param name="RecordNumber">レコード番号</param>
    /// <returns></returns>
    public void Get(byte[] buffer, long RecordNumber)
    {
        if (0 < RecordNumber)
        {
            _FStream.Position = _RecodeLength * (RecordNumber - 1);
        }
        else
        {
            _FStream.Position = 0;                                      //先頭行にする
        }

        _FStream.Read(buffer, 0, _RecodeLength);
    }

    /// <summary>
    /// 1レコード書き込む
    /// </summary>
    /// <param name="buffer">書き込むバッファ</param>
    /// <param name="RecordNumber">レコード番号</param>
    /// <returns></returns>
    public void Put(byte[] buffer, long RecordNumber = 0)
    {
        if (0 < RecordNumber)
        {
            _FStream.Position = _RecodeLength * (RecordNumber - 1);
        }
        else
        {
            _FStream.Position = _RecodeLength * this.Rows();        //最終行にする
        }

        _FStream.Write(buffer, 0, _RecodeLength);
    }

    /// <summary>
    /// 構造体を1レコード読み込む
    /// </summary>
    /// <param name="objType">構造体タイプ</param>
    /// <param name="RecordNumber">レコード番号</param>
    /// <returns></returns>
    public object Get(long RecordNumber)
    {
        this.Get(_buffer, RecordNumber);

        GCHandle gch = GCHandle.Alloc(_buffer, GCHandleType.Pinned);
        object obj = Marshal.PtrToStructure(gch.AddrOfPinnedObject(), _Type);
        gch.Free();
    }

    /// <summary>
    /// 構造体を1レコード書き込む
    /// </summary>
    /// <param name="obj">構造体</param>
    /// <param name="RecordNumber">レコード番号</param>
    /// <returns></returns>
    public void Put(object obj, long RecordNumber = 0)
    {
        GCHandle gch = GCHandle.Alloc(_buffer, GCHandleType.Pinned);
        Marshal.StructureToPtr(obj, gch.AddrOfPinnedObject(), false);
        gch.Free();

        this.Put(_buffer, RecordNumber);
    }
}

呼び出し側

[StructLayout(LayoutKind.Sequential, Pack = 1)]
unsafe struct Person 
{
    public fixed byte name[10];
    public fixed byte Age[5];
}

または

[StructLayout(LayoutKind.Sequential, Pack = 1)]                 //メンバーを定義順に格納する
public struct Person
{
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 10)]       //byte[10]で格納
    public byte[] name;
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 5)]        //byte[5]で格納
    public byte[] Age;
}


string FilePath = @"C:\work\myrfile.txt";

using (RandomFile rf = new RandomFile())
{
    rf.Open(FilePath, new Person());
    Person pos = (Person)rf.Get(2);

    string s = System.Text.Encoding.GetEncoding(932).GetString(pos.name);
   
    rf.Put(pos);
    rf.Close();
}

・長さを固定するために構造体の書き方に工夫がいる、メモリ中では、速度を上げるために、バイト単位で確保するより、レジスタ単位で確保したほうが速度が速くなるため、コンパイラ等が最適な並びで確保するので、並び等を指定する必要が出てくる。のでMarshalAs、StructLayoutAttributeUnmanagedTypeで指定する。

・構造体から値を取り出す場合にはByte[]から文字列型に変換する必要が出たりする。