visual studio のtextboxに右余白を入れたいです

Category: visual studio cs_ja

Question

ももたろう on Fri, 12 Jul 2019 08:16:43


visual studio 2019を使用しています。

プロジェクトはWindowsフォームアプリケーションで、C#を選択しています。

コントロールのtextboxに右余白を入れたいのですが、方法が分かりません。

win32apiなどを使用してできないでしょうか?

プログラミングは始めたばかりなので、分かりやすく教えていただきたいです。

Replies

gekka on Fri, 12 Jul 2019 10:25:37


こんな?

using System;
using System.Windows.Forms;

namespace WindowsFormsApp1
{
    public partial class Form1 : Form
    {
        TextBox textBox1;

        public Form1()
        {
            InitializeComponent();

            textBox1 = new TextBox();
            textBox1.Dock = DockStyle.Fill;
            textBox1.AcceptsReturn = true;
            textBox1.Multiline = true;
            textBox1.ScrollBars = ScrollBars.Vertical;

            Random rnd = new Random();
            for (int i = 0; i < 100; i++)
            {
                textBox1.AppendText(string.Empty.PadRight(rnd.Next(200), '*') + "\r\n");
            }
  
            this.Controls.Add(textBox1);

            MenuStrip menu = new MenuStrip();
            this.Controls.Add(menu);

            var mitem = new ToolStripMenuItem("Test");
            mitem.Click += Mitem_Click;
            menu.Items.Add(mitem);
        }

        private void Mitem_Click(object sender, EventArgs e)
        {
            SetMargin(this.textBox1, 10, 50);
        }

        private void SetMargin(TextBox textBox, ushort left, ushort right)
        {
            uint margin = (uint)left + ((uint)right << 16);
            SendMessage(textBox.Handle, EM_SETMARGINS, EC_RIGHTMARGIN | EC_LEFTMARGIN, new UIntPtr(margin));

            //反映させる
            textBox1.Width--;
            textBox1.Width++;
        }

        [System.Runtime.InteropServices.DllImport("user32.dll")]
        static extern IntPtr SendMessage(IntPtr hWnd, int Msg, int wParam, UIntPtr lParam);

        const int EM_SETMARGINS = 0x00D3;
        const int EM_GETMARGINS = 0x00D4;
        const int EC_LEFTMARGIN = 1;
        const int EC_RIGHTMARGIN = 2;
        const int EC_USEFONTINFO = 0xFFFF;
    }
}

魔界の仮面弁士 on Fri, 12 Jul 2019 12:55:18


① 下記の "CustomTextBox.cs" を用意します。

using System;
using System.Drawing;
using System.Runtime.InteropServices;
using System.Windows.Forms;

public sealed class CustomTextBox : TextBox
{
    public CustomTextBox()
    {
        Multiline = true;
    }

    /// <summary>
    /// テキストボックスの内側の入力領域を取得・設定します。
    /// </summary>
    public Rectangle ClientArea
    {
        get
        {
            const int EM_GETRECT = 0x00B2;
            IntPtr ptr = Marshal.AllocCoTaskMem(Marshal.SizeOf<Rectangle>());
            var msg = Message.Create(Handle, EM_GETRECT, IntPtr.Zero, ptr);
            base.WndProc(ref msg);
            Rectangle rect = Marshal.PtrToStructure<Rectangle>(ptr);
            Marshal.FreeCoTaskMem(ptr);
            return rect;
        }
        set
        {
            // 複数行テキストボックスでなければ、領域サイズは変更できない
            if (!Multiline) { return; }

            const int EM_SETRECT = 0xB3;
            IntPtr ptr = Marshal.AllocCoTaskMem(Marshal.SizeOf<Rectangle>());
            Marshal.StructureToPtr(value, ptr, false);
            var msg = Message.Create(Handle, EM_SETRECT, IntPtr.Zero, ptr);
            base.WndProc(ref msg);
            Marshal.FreeCoTaskMem(ptr);
            Refresh();
            ClientAreaChanged?.Invoke(this, EventArgs.Empty);
        }
    }
    public EventHandler ClientAreaChanged;
}

② プロジェクトをビルドしてから、改めて Form1 のデザイン画面を開くと、ツールボックスに「CustomTextBox」コントロールが追加されているので、それを Form1 に貼ります。

③ Form1 の Load イベントに、下記のコードを追加します。

var rect = customTextBox1.ClientArea;
rect.Width -= 20;   // 右端を 20px 空ける
// rect.Inflate(-15, -15);   // 領域全体を 15px 小さくする
customTextBox1.ClientArea = rect;

ももたろう on Tue, 16 Jul 2019 06:06:53


gekka様、ありがとうございます。

実際にコードを入力して実行したら、右余白ができました。

何か、魔法のようで不思議に感じます。

uint margin = (uint)left + ((uint)right << 16);

の「<<」は、シフトでしょうか、どうしてシフトするのでしょうか。

IntPtr、UIntPtrは普段、あまりお目見えしないものです。

ネット検索しても、よく分かりませんでした。

ももたろう on Tue, 16 Jul 2019 06:10:49


魔界の仮面弁士様、ご返信ありがとうございます。

魔界の仮面弁士 on Tue, 16 Jul 2019 06:33:12


uint margin =(uint)left+ ((uint)right<< 16);

の「<<」は、シフトでしょうか、どうしてシフトするのでしょうか。

SendMessage の LPARAM パラメーターは、Win16/Win32 環境では32bit長、Win64環境では64bit長ですが、EM_SETMARGINS メッセージの LPARAM に対しては、どの環境でも 32bit 分だけが使われる仕様です。

そしてその 32bit 幅のうち、上位16bit部分が右マージン、下位16bit分が左マージンを意味します。

各マージンは 16bit 幅なので、今回は ushort left, ushort right と定義されています。
でもって、上位ワードにあたる right を 16bit 分だけ左シフトして足し合わせたのが今回の値。

ushort left = 0x1234;
ushort right = 0x5678;
uint margin = (uint)left + ((uint)right << 16);

というコードからは、uint margin = 0x56781234; という値が得られます。

ももたろう on Wed, 17 Jul 2019 00:08:47


魔界の仮面弁士様、ご説明していただきありがとうございます。

32ビット中、上位16ビットと下位16ビットで右マージンと左マージンに分かれているのですね。

驚きました。

ありがとうございます。