【C#】デリゲートの処理イメージ

下記のようにデリゲートを宣言したとします。

public delegate void MyProcess(string msg);

また、実際の処理を行う関数をMyClassのRealProcessというメソッドに実装したとします。

public class MyClass
{
    public void RealProcess(string msg)
    {
        MessageBox.Show(msg);
    }
}

それらをボタンクリックで次のようにして使用したとします。

private void button3_Click(object sender, EventArgs e)
{
    var myCls = new MyClass();
    var process = new MyProcess(myCls.RealProcess);
    process("あいうえお");
}

上記は、『process(“あいうえお”);』という場所でMyClassのRealProcessメソッドが処理されます。

【上記処理の内部動作イメージ】

デリゲートの宣言、『public delegate void MyProcess(string msg);』は、下記のクラスを作ることになるようです。

public class MyProcess : System.MulticastDelegate
{
    private Object _target;
    private IntPtr _methodPtr;

    public MyProcess(Object obj, IntPtr method)
    {
        this._target = obj;
        this._methodPtr = method;
    }

    public overide void Invoke(string msg)
    {
        _targetインスタンスの_methodPtrが示す関数を呼び出す処理
    }
}

ボタンクリック時のコードの中で『var process = new MyProcess(myCls.RealProcess);』と書いてMyClassのRealProcessメソッドと関連付けました。
これは、下記のコードを書いたイメージになるようです・

var process = new MyProcess(myClass,RealProcessメソッドへのポインタ);

また、『process(“あいうえお”);』というコードは下記のコードを書いたことになるようです。

process.Invoke("あいうえお");

内部的な処理の概要を知るまで、JavaScriptのように関数を変数に代入して使うやり方と混ざり合っていまいち理解できていませんでしたが、やっと頭の中が整理できました。


【C#】JANコード13桁タイプのチェックデジットを算出する関数

/// <summary>
/// JANコード13桁タイプのチェックディジットを算出する
/// </summary>
/// <param name="code">JANコード</param>
/// <returns></returns>
private int calcJAN13CheckDigit(string code)
{
    if (!Regex.IsMatch(code, @"^[0-9]{12,13}$"))
    {
        throw new Exception("対象のJANコードが数字12桁または数字13桁になっていません。");
    }

    //先頭12桁を取得
    var val = code.Substring(0, 12);

    //① 偶数桁合計を求める
    int gusuGokei = val.Where((c, index) =>
            index % 2 == 1).Select(c =>
                int.Parse(c.ToString())).Sum();
    //② 奇数桁合計を求める
    int kisuGokei = val.Where((c, index) =>
            index % 2 == 0).Select(c =>
                int.Parse(c.ToString())).Sum();
    //③ 『偶数桁合計×3+奇数桁合計』を算出する
    int gokei = gusuGokei * 3 + kisuGokei;
    //④ ③で算出した値の1の位を取得する
    int subVal = gokei % 10;
    //⑤ チェックデジットは『10から④で取得した値を引いた結果の1の位』
    int digit = (10 - subVal) % 10;

    return digit;
}

【C#】WinFormsの印刷プレビュー(GDI)

下記を参照設定に追加する必要があります

  • System.Drawing

private void button1_Click(object sender, EventArgs e)
{
    var printDoc = new PrintDocument();
    printDoc.DocumentName = "線の印刷サンプル";
    printDoc.PrintPage += PrintDoc_PrintPage;

    PrintPreviewDialog previewDialog = new PrintPreviewDialog();
    previewDialog.Height = 600;
    previewDialog.Width = 800;
    previewDialog.Document = printDoc;
    previewDialog.Show(this);
}

private void PrintDoc_PrintPage(object sender, PrintPageEventArgs e)
{
    e.Graphics.PageUnit = GraphicsUnit.Millimeter;
    var pen = new Pen(System.Drawing.Color.Black, 1);
    e.Graphics.DrawLine(pen, new Point(20, 20), new Point(180, 20));
}

【C#】WPFで印刷プレビュー(XPS)

前回『ミリメートル指定で線を印刷してみる』で作成した印刷ドキュメントを、プレビュー画面で表示するようにしてみました。

下記を参照設定に追加する必要があります

  • System.Printing

  • ReachFramework

private void btnPreviewXPS_Click(object sender, RoutedEventArgs e)
{
    try
    {
        //1インチをミリメートルに換算した値
        const double MmPerInch = 25.40;
        //FixedPageの解像度
        const double DPI = 96;

        var canvas = new Canvas();

        //線を作成する
        var line = new Line();
        line.Stroke = Brushes.Black;

        //ピクセルで太さ、始点、終点を指定
        //※ Pixel = ミリメートル ÷ インチへの換算係数 × 解像度
        line.StrokeThickness = 1 / MmPerInch * DPI;
        line.X1 = 20 / MmPerInch * DPI;
        line.Y1 = 20 / MmPerInch * DPI;
        line.X2 = 180 / MmPerInch * DPI;
        line.Y2 = 20 / MmPerInch * DPI;

        //作成した線をキャンバスに追加
        canvas.Children.Add(line);

        //印刷ドキュメントにキャンバスを追加
        FixedPage page = new FixedPage();
        page.Children.Add(canvas);
        PageContent content = new PageContent();
        content.Child = page;
        FixedDocument doc = new FixedDocument();
        doc.Pages.Add(content);

        //ドキュメントビューアコントロールを作成
        var viewer = new DocumentViewer();
        viewer.HorizontalAlignment = HorizontalAlignment.Left;
        viewer.VerticalAlignment = VerticalAlignment.Top;
        viewer.Margin = new Thickness(0, 0, 0, 0);
        viewer.Document = doc;

        //プレビュー用のウインドウを作成
        var previewWindow = new Window();
        previewWindow.Owner = this;
        previewWindow.Height = 600;
        previewWindow.Width = 800;

        //プレビュー用ウインドウにドキュメントビューアをセット
        previewWindow.Content = viewer;

        //プレビュー画面を表示
        previewWindow.ShowDialog();
    }
    catch (Exception ex)
    {
        MessageBox.Show(ex.ToString());
    }
}

【C#】ミリメートル指定で線を印刷してみる(XPS)

下記を参照設定に追加する必要があります

  • System.Printing

  • ReachFramework

private void button_Copy_Click(object sender, RoutedEventArgs e)
{
    try
    {
        //1インチをミリメートルに換算した値
        const double MmPerInch = 25.40;
        //FixedPageの解像度
        const double DPI = 96;

        var canvas = new Canvas();

        //線を作成する
        var line = new Line();
        line.Stroke = Brushes.Black;

        //ピクセルで太さ、始点、終点を指定
        //※ Pixel = ミリメートル ÷ インチへの換算係数 × 解像度
        line.StrokeThickness = 1 / MmPerInch * DPI;
        line.X1 = 20 / MmPerInch * DPI;
        line.Y1 = 20 / MmPerInch * DPI;
        line.X2 = 180 / MmPerInch * DPI;
        line.Y2 = 20 / MmPerInch * DPI;

        //作成した線をキャンバスに追加
        canvas.Children.Add(line);

        //印刷ドキュメントにキャンバスを追加
        FixedPage page = new FixedPage();
        page.Children.Add(canvas);
        PageContent content = new PageContent();
        content.Child = page;
        FixedDocument doc = new FixedDocument();
        doc.Pages.Add(content);

        //デフォルトプリンタを取得する
        var defaultPrinter = LocalPrintServer.GetDefaultPrintQueue();

        //ジョブ名を設定する
        defaultPrinter.CurrentJobSettings.Description = "線の印刷サンプル(XPS)";

        //印刷用ライターを作成
        var docWriter = PrintQueue.CreateXpsDocumentWriter(defaultPrinter);

        //印刷
        docWriter.Write(doc);
    }
    catch (Exception ex)
    {
        MessageBox.Show(ex.ToString());
    }
}

【C#】ミリメートル指定で線を印刷してみる(GDI)

参照設定で System.Drawing を追加する必要があります。

private void button1_Click(object sender, RoutedEventArgs e)
{
    var printDoc = new System.Drawing.Printing.PrintDocument();
    printDoc.DocumentName = "線の印刷サンプル";
    printDoc.PrintPage += PrintDoc_PrintPage;
    printDoc.Print();
}

private void PrintDoc_PrintPage(object sender, System.Drawing.Printing.PrintPageEventArgs e)
{
    //単位をミリメートルにする
    e.Graphics.PageUnit = System.Drawing.GraphicsUnit.Millimeter;
    //1mmのペンを作成する
    var pen = new System.Drawing.Pen(System.Drawing.Color.Black, 1);
    //ラインを引く
    e.Graphics.DrawLine(pen, new System.Drawing.Point(20,20), new System.Drawing.Point(180,20));
}

【C#】Enterキー、上下矢印キーの押下でフォーム上にあるコントロールのフォーカスを移動する(WinForms)

各コントロールのKwyDownイベントで行うと、
コントロールを追加するたびにイベントへの記述を追加しなければならなくなります。

そこで、FormのKeyDownイベントに実装します。

フォーム上のコントロールで起こったKeyDownイベントを
フォーム側でも受け取れるようにするため、
下記のプロパティを設定しておきます。

Form1.KeyPreview = true;

FormのKeyDownイベントを実装します。

private void Form1_KeyDown(object sender, KeyEventArgs e)
{
    try
    {
        switch (e.KeyCode)
        {
            case Keys.Return:
                //次のコントロールにフォーカス移動
                this.SelectNextControl(this.ActiveControl
                    , true, true, true, true);
                break;

            case Keys.Down:
            case Keys.Up:
                //リストボックスの上下キーは選択リストの移動なのでフォーカス移動させない
                if (this.ActiveControl is ListBox)
                {
                    break;
                }
                //リストボックスの上下キーは選択リストの移動なのでフォーカス移動させない
                if (this.ActiveControl is ComboBox)
                {
                    break;
                }
                //次のコントロールにフォーカス移動
                this.SelectNextControl(this.ActiveControl
                    ,e.KeyCode == Keys.Down, true, true, true);
                break;
        }
    }
    catch (Exception ex)
    {
        Console.Error.WriteLine(ex);
    }
}

C#でバイト長による入力制限を持ったTextBoxを作る(WinForms)

クリップボードからペーストされた時のチェックは未対応です。
Validatingイベント、Validatedイベント等でのチェック処理や入力値を用いる処理直前での値チェックは必要不可欠です。

TextChangedイベントで入力可能桁数を制限します。

//この文字コードで扱う場合のバイト数で入力文字数を制限する
private Encoding ENCODE_BYTECHK = Encoding.GetEncoding("Shift_JIS");

private void textBox3_TextChanged(object sender, EventArgs e)
{
    var txtBox = sender as TextBox;

    try
    {
        txtBox.TextChanged -= textBox3_TextChanged;

        if (!string.IsNullOrWhiteSpace(txtBox.Text))
        {
            var value = txtBox.Text.Trim();
            if (ENCODE_BYTECHK.GetByteCount(value) > txtBox.MaxLength)
            {
                // 入力可能桁数を超えているので今入力されたものを無効にしてしまう
                int currentPoint = txtBox.SelectionStart;
                var leftStr = value.Substring(0, currentPoint > txtBox.MaxLength ? txtBox.MaxLength : currentPoint - 1);
                var rightStr = leftStr.Length >= txtBox.MaxLength ? "" : value.Substring(currentPoint);
                txtBox.Text = leftStr + rightStr;
                txtBox.SelectionStart = currentPoint - 1;
            }
        }
    }
    finally
    {
        txtBox.TextChanged += textBox3_TextChanged;
    }

}

C#で小数がある数値入力専用のTextBoxを作る(WinForms)

下記仕様のTextBoxを作成します。

  • 数字と小数点のみを入力可能とする

  • 入力可能桁数の制限を行う。

  • 入力可能桁数は整数部と小数部を分けて指定する

  • フォーカスが当たっていないときは3桁毎のカンマ区切りで表示する

  • 小数がない場合、整数のみを表示する

  • 小数は存在する桁だけを表示する。

クリップボードからペーストされた時のチェックは未対応です。
Validatingイベント、Validatedイベント等でのチェック処理や入力値を用いる処理直前での値チェックは必要不可欠です。

数字、小数点のみを入力可能にするため、
KeyPressイベントを下記のように実装します。

private void textBox2_KeyPress(object sender, KeyPressEventArgs e)
{
    try
    {
        //バックスペースと小数点使用可能とする
        if (e.KeyChar == 0x08 || e.KeyChar == '.')
        {
            return;
        }

        //数字キー以外の入力をキャンセルする
        if (e.KeyChar < 0x30 || e.KeyChar > 0x39)
        {
            e.Handled = true;
        }
    }
    catch (Exception ex)
    {
        Console.Error.WriteLine(ex);
    }
}

これから入力可能桁数の制限を実装していきますが、小数がない場合と比べて考慮が必要な点があります。

既に小数点付きの数字が入力されている場合に小数点を削除されると、
それまで小数部だった桁が整数部へ移動することになるため、
このときに整数の溢れ分をまとめて削除する必要があります。

この場合、カーソル位置にある数字を削除してしまうと違和感が発生するため、
上桁から削除していくこととします。

桁数の制限はTextChangedイベントで行う予定ですが、
TextChangedイベントでは文字が削除されたのかどうかを判断することができません。

前の文字列を保存しておいて比較する手もあるかと思いますが、
今回はDeleteキーやBackSpaceが押されたかどうかを判断することにします。

KeyPressイベントではDeleteキーを検知できませんので、
KeyDownイベントまたはKeyUpイベントを使用します。

ただし、イベントが呼び出される順番が KeyDown→TextChanged→KeyUp なので
KeyDownイベントを使用します。

bool isDelete = false;

private void textBox2_KeyDown(object sender, KeyEventArgs e)
{
    isDelete = (e.KeyCode == Keys.Delete || e.KeyCode == Keys.Back);
}

TextChangedイベントで入力可能桁数を制限します。

//整数部桁数
private readonly int LENGTH_INT_PART = 7;
//小数部桁数
private readonly int LENGTH_DECIMAL_PLACES = 2;

private void textBox2_TextChanged(object sender, EventArgs e)
{
    var txtBox = sender as TextBox;

    txtBox.TextChanged -= textBox2_TextChanged;

    try
    {
        if (!string.IsNullOrWhiteSpace(txtBox.Text))
        {
            var value = txtBox.Text;
            //整数と小数を分割する
            string[] values = value.Split('.');

            int currentPoint = 0;
            switch (values.Length)
            {
                case 1:
                    //------------------------------
                    //整数部のみで構成されている場合
                    //------------------------------
                    //整数部の最大桁数を超えた場合、今回の入力値を無効にする
                    if (value.Length > LENGTH_INT_PART)
                    {
                        //DELETEやBackSpaceによる削除で桁あふれが発生しているなら
                        //小数点の削除によるものなので、先頭桁から削除する
                        if (isDelete)
                        {
                            txtBox.Text = value.Substring(value.Length - LENGTH_INT_PART);

                            //カーソル位置を移動
                            if (currentPoint - (values.Length - LENGTH_INT_PART) >= 0)
                            {
                                txtBox.SelectionStart = currentPoint -
                                	(values.Length - LENGTH_INT_PART);
                            }
                            else
                            {
                                txtBox.SelectionStart = 0;
                            }
                        }
                        else
                        {
                            //入力後の現カーソル位置を取得
                            currentPoint = txtBox.SelectionStart;

                            //カーソル位置の前の1文字が今回入力された文字、
                            //よって、それを省いた文字列に編集する
                            var left = value.Substring(0
                                , currentPoint > LENGTH_INT_PART
                                    ? LENGTH_INT_PART : currentPoint - 1);
                            var right = left.Length >= LENGTH_INT_PART
                                ? "" : value.Substring(currentPoint);
                            txtBox.Text = left + right;

                            //カーソル位置を入力前の位置に戻す
                            txtBox.SelectionStart = currentPoint - 1;
                        }
                    }
                    break;

                case 2:
                    //----------------------------------
                    //整数部+小数部で構成されている場合
                    //----------------------------------
                    //入力後の現カーソル位置を取得
                    currentPoint = txtBox.SelectionStart;

                    //今回の入力値が"."の場合、小数点を基準点として桁あふれ分を除外する
                    if (value.Substring(currentPoint - 1, 1) == ".")
                    {
                        //=== 整数部の処理 ===
                        var intPart = values[0];
                        if (values[0].Length > LENGTH_INT_PART)
                        {
                            intPart = values[0].Substring(LENGTH_INT_PART - values[0].Length);
                        }

                        //=== 小数部の処理 ===
                        var decimalPart = values[1];
                        if (values[1].Length > LENGTH_DECIMAL_PLACES)
                        {
                            decimalPart = values[1].Substring(0, LENGTH_DECIMAL_PLACES);
                        }

                        //整数と小数を結合
                        if (values[0].Length > LENGTH_INT_PART || values[1].Length > LENGTH_DECIMAL_PLACES)
                        {
                            txtBox.Text = string.Format("{0}.{1}", intPart, decimalPart);

                            //小数点入力時なら小数点の後ろにカーソルをセット
                            txtBox.SelectionStart = value.IndexOf(".") + 1;
                        }
                    }
                    else
                    {
                        //=== 整数部の処理 ===
                        var intPart = values[0];
                        if (values[0].Length > LENGTH_INT_PART)
                        {
                            //カーソル位置の前の1文字が今回入力された文字、
                            //よって、それを省いた文字列に編集する
                            var left = values[0].Substring(0
                                , currentPoint > LENGTH_INT_PART
                                    ? LENGTH_INT_PART : currentPoint - 1);
                            var right = left.Length >= LENGTH_INT_PART
                                ? "" : values[0].Substring(currentPoint);

                            //桁数調整後の整数部文字列
                            intPart = left + right;
                        }

                        //=== 小数部の処理 ===
                        var decimalPart = values[1];
                        if (values[1].Length > LENGTH_DECIMAL_PLACES)
                        {
                            //整数部と小数点を除いたときのカーソル位置を算出
                            var tempPoint = currentPoint - values[0].Length - 1;
                            //カーソル位置の前の1文字を除外する
                            var left = values[1].Substring(0
                                , tempPoint > LENGTH_DECIMAL_PLACES
                                    ? LENGTH_DECIMAL_PLACES : tempPoint - 1);
                            var right = left.Length >= LENGTH_DECIMAL_PLACES
                                    ? "" : values[1].Substring(tempPoint);

                            //桁数調整後の小数部文字列
                            decimalPart = left + right;
                        }

                        //整数と小数を結合
                        if (values[0].Length > LENGTH_INT_PART || values[1].Length > LENGTH_DECIMAL_PLACES)
                        {
                            txtBox.Text = string.Format("{0}.{1}", intPart, decimalPart);

                            //カーソル位置を入力前の位置に戻す
                            txtBox.SelectionStart = currentPoint - 1;
                        }
                    }

                    break;

                default:
                    //"."で文字列を分割したときに3以上になるのなら、
                    //既に"."が存在しているのに今回"."が入力されたことを示す
                    //よって、今回の入力を無効にしてしまう。
                    {
                        currentPoint = txtBox.SelectionStart;
                        var left = value.Substring(0, currentPoint - 1);
                        var right = value.Substring(currentPoint);
                        txtBox.Text = left + right;
                        txtBox.SelectionStart = currentPoint - 1;
                    }
                    break;
            }

            //先頭が小数点なら先頭に 0 を入れる
            if (txtBox.Text.StartsWith("."))
            {
                currentPoint = txtBox.SelectionStart;
                txtBox.Text = "0" + txtBox.Text;
                txtBox.SelectionStart = currentPoint + 1;
            }
        }
    }
    catch (Exception ex)
    {
        Console.Error.WriteLine(ex);
    }
    finally
    {
        txtBox.TextChanged += textBox2_TextChanged;
    }
}

フォーカスが外れたときに3桁毎のカンマ区切りで表示させるために
Validated イベントを下記のように実装します。

private void textBox2_Validated(object sender, EventArgs e)
{
    var txtBox = sender as TextBox;
    txtBox.TextChanged -= textBox2_TextChanged;
    try
    {
        if (!string.IsNullOrWhiteSpace(txtBox.Text))
        {
            string fmt = "#,0." + "#".PadRight(LENGTH_DECIMAL_PLACES, '#');
            txtBox.Text = decimal.Parse(txtBox.Text.Replace(",", "")).ToString(fmt);
        }
    }
    catch (Exception ex)
    {
        Console.Error.WriteLine(ex.Message);
    }
    finally
    {
        txtBox.TextChanged += textBox2_TextChanged;
    }
}

フォーカスが当たったときに3桁毎のカンマ区切りを解除します。
Enter イベントを下記のように実装します。

private void textBox2_Enter(object sender, EventArgs e)
{
    var txtBox = sender as TextBox;
    txtBox.TextChanged -= textBox2_TextChanged;
    try
    {
        txtBox.Text = txtBox.Text.Replace(",", "");
    }
    catch (Exception ex)
    {
        Console.Error.WriteLine(ex.Message);
    }
    finally
    {
        txtBox.TextChanged += textBox2_TextChanged;
    }
}

最後にテキストボックスで右クリックメニューを表示させないようにするため、
フォームのコンストラクタで下記のようにコードを追加します。

public Form1()
{
    InitializeComponent();

	//これを追加
    textBox2.ContextMenu = new ContextMenu();
}

C#で数値入力専用のTextBoxを作る(WinForms)

下記仕様のTextBoxを作成します。

  • 数字のみ入力可能とする

  • 入力可能桁数をTextBoxのMaxLengthプロパティに設定された桁数までとする

  • フォーカスが当たっていないときは3桁毎のカンマ区切りで表示する

クリップボードからペーストされた時のチェックは未対応です。
Validatingイベント、Validatedイベント等でのチェック処理や入力値を用いる処理直前での値チェックは必要不可欠です。

まず、数値のみ入力できるようにするために KeyPress イベントを実装します。

private void textBox1_KeyPress(object sender, KeyPressEventArgs e)
{
    try
    {
        //バックスペースは使用可能とする
        if (e.KeyChar == 0x08)
        {
            return;
        }

        //数字キー以外の入力をキャンセルする
        if (e.KeyChar < 0x30 || e.KeyChar > 0x39)
        {
            e.Handled = true;
        }
    }
    catch (Exception ex)
    {
        Console.Error.WriteLine(ex);
    }
}

次に、入力可能桁数を制限します。
TextChanged イベントを下記のように実装します。

private void textBox1_TextChanged(object sender, EventArgs e)
{
    var txtBox = sender as TextBox;

    try
    {
        txtBox.TextChanged -= textBox1_TextChanged;

        if (!string.IsNullOrWhiteSpace(txtBox.Text))
        {
            string value = txtBox.Text.Replace(",", "");

            //既に入力可能桁数に達しているなら、今回入力された値を除外する
            if (value.Length > txtBox.MaxLength)
            {
                var currentPoint = txtBox.SelectionStart;
                var left = value.Substring(0
                    , currentPoint > txtBox.MaxLength
                         ? txtBox.MaxLength : currentPoint);
                var right = left.Length >= txtBox.MaxLength
                     ? "" : value.Substring(currentPoint + 1);
                txtBox.Text = left + right;
                txtBox.SelectionStart = currentPoint;
            }
        }
    }
    catch (Exception ex)
    {
        Console.Error.WriteLine(ex);
    }
    finally
    {
        //2桁以上で先頭が0なら先頭0を除外する
        if (txtBox.Text.StartsWith("0") && txtBox.Text != "0")
        {
            var currentPoint = txtBox.SelectionStart;
            txtBox.Text = txtBox.Text.Substring(1);
            if (currentPoint > txtBox.Text.Length)
            {
                txtBox.SelectionStart = txtBox.Text.Length;
            }
        }

        txtBox.TextChanged += textBox1_TextChanged;
    }
}

フォーカスが外れたときに3桁毎のカンマ区切りで表示させるために
Validated イベントを下記のように実装します。

private void textBox1_Validated(object sender, EventArgs e)
{
    try
    {
        var txtBox = sender as TextBox;
        if (!string.IsNullOrWhiteSpace(txtBox.Text))
        {
            txtBox.Text = int.Parse(txtBox.Text.Replace(",", "")).ToString("#,##0");
        }
    }
    catch (Exception ex)
    {
        Console.Error.WriteLine(ex.Message);
    }
}

フォーカスが当たったときに3桁毎のカンマ区切りを解除します。
Enter イベントを下記のように実装します。

private void textBox1_Enter(object sender, EventArgs e)
{
    var txtBox = sender as TextBox;

    try
    {
        txtBox.TextChanged -= textBox1_TextChanged;

        txtBox.Text = txtBox.Text.Replace(",", "");
        txtBox.SelectAll();
    }
    catch (Exception ex)
    {
        Console.Error.WriteLine(ex.Message);
    }
    finally
    {
        txtBox.TextChanged += textBox1_TextChanged;
    }
}

最後にテキストボックスで右クリックメニューを表示させないようにするため、
フォームのコンストラクタで下記のようにコードを追加します。

public Form1()
{
    InitializeComponent();

	//これを追加
    textBox1.ContextMenu = new ContextMenu();
}