Custom Caret for WinForms TextBox
Asked Answered
C

5

11

I'm developing a custom HyperTerminal like application in a WinForms .Net 2.0 application. I have a multiline TextBox in a Panel in which you can interact with a hardware device.

My customer wants to have a custom Caret, a filled rectangle the size of one character space instead of the vertical line that is by default.

I know .Net does not provide an option to do this by default, but there must some Windows function to do it.

Chest answered 4/3, 2009 at 9:59 Comment(0)
D
8

Assume a form with a textbox on it:

public partial class Form1 : Form
{
    [DllImport("user32.dll")]
    static extern bool CreateCaret(IntPtr hWnd, IntPtr hBitmap, int nWidth, int nHeight);
    [DllImport("user32.dll")]
    static extern bool ShowCaret(IntPtr hWnd);

    public Form1()
    {
        InitializeComponent();
    }

    private void Form1_Shown(object sender, EventArgs e)
    {
        CreateCaret(textBox1.Handle, IntPtr.Zero, 10, textBox1.Height);
        ShowCaret(textBox1.Handle);
    }
}
Diggins answered 4/3, 2009 at 10:42 Comment(0)
K
16

These are the list of Native Caret functions provided by Windows you can use them for you application.

    [DllImport("User32.dll")]
    static extern bool CreateCaret(IntPtr hWnd, int hBitmap, int nWidth, int nHeight);

    [DllImport("User32.dll")]
    static extern bool SetCaretPos(int x, int y);

    [DllImport("User32.dll")]
    static extern bool DestroyCaret();

    [DllImport("User32.dll")]
    static extern bool ShowCaret(IntPtr hWnd);

    [DllImport("User32.dll")]
    static extern bool HideCaret(IntPtr hWnd);

Refer SharpDevelop, Source Code @ src\Libraries\ICSharpCode.TextEditor\Project\Src\Gui\Caret.cs

Kaz answered 4/3, 2009 at 10:32 Comment(1)
CreateCaret's hBitmap parameter should be typed as IntPtr and not int because it's declared as HBITMAP which is a typedef of void*.Kermis
D
8

Assume a form with a textbox on it:

public partial class Form1 : Form
{
    [DllImport("user32.dll")]
    static extern bool CreateCaret(IntPtr hWnd, IntPtr hBitmap, int nWidth, int nHeight);
    [DllImport("user32.dll")]
    static extern bool ShowCaret(IntPtr hWnd);

    public Form1()
    {
        InitializeComponent();
    }

    private void Form1_Shown(object sender, EventArgs e)
    {
        CreateCaret(textBox1.Handle, IntPtr.Zero, 10, textBox1.Height);
        ShowCaret(textBox1.Handle);
    }
}
Diggins answered 4/3, 2009 at 10:42 Comment(0)
M
1

I would use System.Drawing to draw a custom cursor (bitmap), maybe with a timer to let it blink like another cursor.

Get the current position of the Cursor in pixels and draw a bitmap over that cursor. Can be tricky to find the correct position, but should be doable.

Have a look here for Owner drawn textbox in winforms.

Mohn answered 4/3, 2009 at 10:28 Comment(0)
C
0

Use:

richTextBoxConsole.GetPositionFromCharIndex(cursorPos)

Hide the normal caret and draw your own? Not tested, but should work I think.

Cuomo answered 30/1, 2017 at 9:17 Comment(0)
M
0

I'm not sure I understand question... But next will help others who like me not found answer. So you have one line text, you need find Caret position (click keybord LEFT or RIGHT). You have TextRenderer.MeasureString() and have TextRenderer.DrawText(). Idea simple, we need util function

private SizeF CalculateTextSize(String _text, int begin, int end) 

and it's calculate SizeF of given text. So if need find Caret position,

size = CalculateTextSize(text, 0, index)

and voalia

caretPos = new Point(x + size.Width, y)

Another, you need find position of index by passing (clickeX,clickedY).

for(int i=0; i < text.Lenght; i++)
{
   SizeF size = CalculateTextSize(text, 0, i);
   Rectangle rect = new Rectangle(x, y, size.Width, lineHeight);
    
   if(rect.Contains(clickedX,clickedY))
   {
      index = i;
      break;
   }
}

IDEA works fine with Multiline custom text edit

using MyDirectX;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;

namespace MyRenderText2
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
            LoadFont();
            text = "Олександр Буханенко";
            location = new Point(100, 33);
        }

        String text;
        int index = 0;

        Font font;
        private void LoadFont()
        {
            font = new Font("Calibri", 12f);
        }

        Point location;

        private void Form1_Paint(object sender, PaintEventArgs e)
        {
            e.Graphics.Clear(Color.AntiqueWhite);
            RenderRect();
            RenderText();
        }

        private void RenderText()
        {
            Graphics g = this.CreateGraphics();

            TextRenderer.DrawText(g, text, font, location, Color.Red, TextFormatFlags.NoPadding);

            g.Dispose();
        }

        private void RenderRect()
        {
            SizeF textSize = new SizeF(0, 0);
            textSize = CalculateTextSize(text, 0, text.Length);

            Graphics g = this.CreateGraphics();
            g.DrawRectangle(Pens.Blue, new Rectangle(location.X, location.Y, (int)textSize.Width, (int)textSize.Height));
            g.Dispose();
        }



        private SizeF CalculateTextSize(String _text, int begin, int end)
        {
            String text = _text.Substring(begin, end - begin);
            SizeF textSize = new SizeF(0, 0);

            Graphics g = this.CreateGraphics();
            textSize = TextRenderer.MeasureText(g, text, font, new Size(0, 0), TextFormatFlags.NoPadding);
            g.Dispose();

            return textSize;
        }



        protected override void OnGotFocus(EventArgs e)
        {
            base.OnGotFocus(e);

            Size size = new Size(0, 0);
            size = GetCaretSize();
            Point pos = new Point(0, 0);
            pos = GetCaretPos();

            Win32.CreateCaret(this.Handle, IntPtr.Zero, 1, size.Height);
            Win32.SetCaretPos(pos.X, pos.Y);
            Win32.ShowCaret(this.Handle);

        }

        protected override void OnLostFocus(EventArgs e)
        {
            base.OnLostFocus(e);

            Win32.DestroyCaret();
        }



        private Size GetCaretSize()
        {
            Size caretSize = new Size(0, 0);

            String s = "1";
            SizeF sSize = new SizeF(0, 0);

            sSize = this.CalculateTextSize(s, 0, 1);

            caretSize.Width = 1;
            caretSize.Height = (int)sSize.Height;

            return caretSize;
        }

        private Point GetCaretPos()
        {
            Point pos = new Point(0, 0);

            String text1 = text.Substring(0, index);
            String text2 = text.Substring(index);
            String[] lines = text1.Split('\r');
            String lastLineText1 = lines[lines.Length - 1];

            //calculate x
            SizeF lastLineText1Size = new SizeF(0, 0);
            lastLineText1Size = CalculateTextSize(lastLineText1, 0, lastLineText1.Length);
            pos.X = location.X + (int)lastLineText1Size.Width;

            //calculate y
            pos.Y = location.Y;
            SizeF text1Size = new SizeF(0, 0);
            text1Size = CalculateTextSize(text1, 0, text1.Length);
            pos.Y += (int)text1Size.Height;

            String s = "1";
            SizeF sSize = new SizeF(0, 0);
            sSize = CalculateTextSize(s, 0, 1);
            pos.Y -= (text.Substring(0, index).Length != 0 ? 1 : 0) * (int)sSize.Height;

            return pos;
        }



        private void Form1_KeyDown(object sender, KeyEventArgs e)
        {
            Point pos = new Point(0, 0);
            String s1 = "", s2 = "";


            switch (e.KeyCode)
            {
                case Keys.Left:
                    index--;
                    index = Math.Max(index, 0);

                    //this.Invalidate();

                    pos = GetCaretPos();
                    Win32.HideCaret(this.Handle);
                    Win32.SetCaretPos(pos.X, pos.Y);
                    Win32.ShowCaret(this.Handle);
                    break;

                case Keys.Right:
                    index++;
                    index = Math.Min(index, text.Length);

                    //this.Invalidate();

                    pos = GetCaretPos();
                    Win32.HideCaret(this.Handle);
                    Win32.SetCaretPos(pos.X, pos.Y);
                    Win32.ShowCaret(this.Handle);
                    break;

                case Keys.Back:
                    s1 = text.Substring(0, Math.Max(index - 1, 0));
                    s2 = text.Substring(index);

                    text = s1 + s2;
                    index--;
                    index = Math.Max(0, index);

                    pos = GetCaretPos();
                    Win32.SetCaretPos(pos.X, pos.Y);

                    this.Invalidate();
                    break;

                case Keys.Delete:
                    s1 = text.Substring(0, index);
                    s2 = text.Substring(Math.Min(index + 1, text.Length));

                    text = s1 + s2;

                    this.Invalidate();
                    break;
            }
        }

        private void Form1_KeyPress(object sender, KeyPressEventArgs e)
        {
            if (e.KeyChar == (int)Keys.Back)
                return;

            String s1 = "", s2 = "";
            s1 = text.Substring(0, index);
            s2 = text.Substring(index);

            text = s1 + e.KeyChar + s2;
            index++;

            Point pos = new Point(0, 0);
            pos = GetCaretPos();
            Win32.SetCaretPos(pos.X, pos.Y);

            this.Invalidate();
        }



        private void Form1_MouseClick(object sender, MouseEventArgs e)
        {
            

        }
    }
}

Change

Win32.CreateCaret(...), Win32.ShowCaret(...), Win32.SetCaretPos(...), Win32.DestroyCaret(...) 

to

CreateCaret(...), ShowCaret(...)...
[DllImport("user32.dll")]
static extern bool CreateCaret(IntPtr hWnd, IntPtr hBitmap, int nWidth, int nHeight);
[DllImport("user32.dll")]
static extern bool ShowCaret(IntPtr hWnd);

full project

Monstrance answered 5/4 at 9:11 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.