C#中的多行模板文本控件

时间:2022-09-20 15:54:20

I'd like a textbox that allows for certain text within to be "constant" and uneditable while the rest of the text is editable. For instance, I'd like to define a template like this:

我想要一个文本框,允许其中的某些文本是“常量”和不可编辑的,而文本的其余部分是可编辑的。例如,我想定义一个这样的模板:

<Name:>[]
<Address:>[] <City>:[]

So that the user could later enter:

以便用户以后可以输入:

<Name:>[Stefan]
<Address:>[Nowhere] <City>:[Alaska]

But not:

<I'm typing here lol:>[Stefan]
<Address:>[Nowhere] <State>:[Alaska]

Ideally, they wouldn't even be able to put their cursor in between the <>, similar to Microsoft Word templates.

理想情况下,他们甚至无法将光标放在<>之间,类似于Microsoft Word模板。

Any ideas? The masked textbox control seems to be along the right path, but isn't multiline and doesn't allow you to enter a variable number of characters in between the braces, for instance.

有任何想法吗?屏蔽的文本框控件似乎沿着正确的路径,但不是多行,并且不允许您在大括号之间输入可变数量的字符。

Thanks in advance.

提前致谢。

2 个解决方案

#1


3  

I don't know of any ready-made components. But you could try this simple method.

我不知道任何现成的组件。但你可以试试这个简单的方法。

  1. Create a normal multi-line text box control
  2. 创建一个普通的多行文本框控件

  3. Create a regex-based template that has (.*) or ([a-z]*) or whatever wherever you want the user to add text.
  4. 创建一个基于正则表达式的模板,该模板具有(。*)或([a-z] *)或任何您希望用户添加文本的位置。

  5. Whenever the text is changed, checked if it still matches the regex. If it does, accept the chagne. If it doesn't, reject the change.
  6. 每当更改文本时,检查它是否仍然与正则表达式匹配。如果确实如此,请接受chagne。如果没有,请拒绝更改。

You can then use the same regex to extract your data from the text box.

然后,您可以使用相同的正则表达式从文本框中提取数据。

You could even use a RichTextBox and use the regex to perform formatting.

您甚至可以使用RichTextBox并使用正则表达式来执行格式化。

Update Here's what I wrote. It seems to work:

更新这是我写的。它似乎工作:

[DefaultProperty("Regex")]
public partial class MaskedEdit : UserControl
{
    private Regex regex = new Regex("");
    private bool myChange = false;

    private string goodText;
    private Font dataFont;

    public MaskedEdit()
    {
        myChange = true;
        InitializeComponent();
        myChange = false;
        dataFont = new Font(Font, FontStyle.Bold);
        goodText = Text;
    }

    [Editor("System.ComponentModel.Design.MultilineStringEditor, System.Design", typeof(UITypeEditor)), Localizable(true)]
    [DefaultValue("")]
    public String Regex
    {
        get { return regex.ToString(); }
        set
        {
            if (value != null)
            {
                regex = new Regex(value);
            }
        }
    }

    [EditorBrowsable(EditorBrowsableState.Always)]
    [Browsable(true)] 
    [Editor("System.ComponentModel.Design.MultilineStringEditor, System.Design", typeof(UITypeEditor)), Localizable(true)]
    [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
    public override string Text
    {
        get { return rtf.Text; }
        set {
            int selSt = rtf.SelectionStart;
            int selLen = rtf.SelectionLength;

            rtf.Text = value;

            rtf.SelectionStart = selSt;
            rtf.SelectionLength = selLen;
        }
    }

    private void rtf_TextChanged(object sender, EventArgs e)
    {
        if (myChange) return;
        Match m = regex.Match(Text);
        if (m.Success)
        {
            goodText = Text;
            Colorize(m);
        }
        else
        {
            myChange = true;
            Text = goodText;
            myChange = false;
            m = regex.Match(Text);
            if (m.Success)
            {
                Colorize(m);
            }
        }
    }

    public IEnumerable<string> Data
    {
        get
        {
            Match m = regex.Match(Text);
            bool first = true;
            foreach (Group g in m.Groups)
            {
                if (first) { first = false; continue; }
                yield return Text.Substring(g.Index, g.Length);
            }
        }
    }

    private void Colorize(Match m)
    {
        int selSt = rtf.SelectionStart;
        int selLen = rtf.SelectionLength;

        rtf.SelectionStart = 0;
        rtf.SelectionLength = rtf.TextLength;
        rtf.SelectionFont = Font;
        bool first = true;
        foreach (Group g in m.Groups)
        {
            if (first) { first = false; continue; }
            rtf.SelectionStart = g.Index;
            rtf.SelectionLength = g.Length;
            rtf.SelectionFont = dataFont;
        }

        rtf.SelectionStart = selSt;
        rtf.SelectionLength = selLen;
    }

    private void MaskedEdit_FontChanged(object sender, EventArgs e)
    {
        dataFont = new Font(Font, FontStyle.Bold);
    }
}

#2


1  

I would suggest using multiple controls instead of just one. A single text box would require the user to cursor over to the next field, whereas multiple controls would allow the user to tab between fields, which is expected behavior. If you want to make it look like one control (perhaps to make it look like an address label, or something), you can use borderless textboxes and put the labels and textboxes all within a panel that has a border and a SystemColors.Window background. For an extra effect, it may be nice to put lines underneath each textbox, which is easily done with a 1-pixel high label with a BorderStyle.FixedSingle border.

我建议使用多个控件而不是一个。单个文本框将要求用户将光标移动到下一个字段,而多个控件将允许用户在字段之间进行选项卡,这是预期的行为。如果你想让它看起来像一个控件(也许是为了使它看起来像地址标签或其他东西),你可以使用无边框文本框并将标签和文本框全部放在具有边框和SystemColors.Window背景的面板中。为了获得额外的效果,可以在每个文本框下面放置线条,这可以通过带有BorderStyle.FixedSingle边框的1像素高标签轻松完成。

#1


3  

I don't know of any ready-made components. But you could try this simple method.

我不知道任何现成的组件。但你可以试试这个简单的方法。

  1. Create a normal multi-line text box control
  2. 创建一个普通的多行文本框控件

  3. Create a regex-based template that has (.*) or ([a-z]*) or whatever wherever you want the user to add text.
  4. 创建一个基于正则表达式的模板,该模板具有(。*)或([a-z] *)或任何您希望用户添加文本的位置。

  5. Whenever the text is changed, checked if it still matches the regex. If it does, accept the chagne. If it doesn't, reject the change.
  6. 每当更改文本时,检查它是否仍然与正则表达式匹配。如果确实如此,请接受chagne。如果没有,请拒绝更改。

You can then use the same regex to extract your data from the text box.

然后,您可以使用相同的正则表达式从文本框中提取数据。

You could even use a RichTextBox and use the regex to perform formatting.

您甚至可以使用RichTextBox并使用正则表达式来执行格式化。

Update Here's what I wrote. It seems to work:

更新这是我写的。它似乎工作:

[DefaultProperty("Regex")]
public partial class MaskedEdit : UserControl
{
    private Regex regex = new Regex("");
    private bool myChange = false;

    private string goodText;
    private Font dataFont;

    public MaskedEdit()
    {
        myChange = true;
        InitializeComponent();
        myChange = false;
        dataFont = new Font(Font, FontStyle.Bold);
        goodText = Text;
    }

    [Editor("System.ComponentModel.Design.MultilineStringEditor, System.Design", typeof(UITypeEditor)), Localizable(true)]
    [DefaultValue("")]
    public String Regex
    {
        get { return regex.ToString(); }
        set
        {
            if (value != null)
            {
                regex = new Regex(value);
            }
        }
    }

    [EditorBrowsable(EditorBrowsableState.Always)]
    [Browsable(true)] 
    [Editor("System.ComponentModel.Design.MultilineStringEditor, System.Design", typeof(UITypeEditor)), Localizable(true)]
    [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
    public override string Text
    {
        get { return rtf.Text; }
        set {
            int selSt = rtf.SelectionStart;
            int selLen = rtf.SelectionLength;

            rtf.Text = value;

            rtf.SelectionStart = selSt;
            rtf.SelectionLength = selLen;
        }
    }

    private void rtf_TextChanged(object sender, EventArgs e)
    {
        if (myChange) return;
        Match m = regex.Match(Text);
        if (m.Success)
        {
            goodText = Text;
            Colorize(m);
        }
        else
        {
            myChange = true;
            Text = goodText;
            myChange = false;
            m = regex.Match(Text);
            if (m.Success)
            {
                Colorize(m);
            }
        }
    }

    public IEnumerable<string> Data
    {
        get
        {
            Match m = regex.Match(Text);
            bool first = true;
            foreach (Group g in m.Groups)
            {
                if (first) { first = false; continue; }
                yield return Text.Substring(g.Index, g.Length);
            }
        }
    }

    private void Colorize(Match m)
    {
        int selSt = rtf.SelectionStart;
        int selLen = rtf.SelectionLength;

        rtf.SelectionStart = 0;
        rtf.SelectionLength = rtf.TextLength;
        rtf.SelectionFont = Font;
        bool first = true;
        foreach (Group g in m.Groups)
        {
            if (first) { first = false; continue; }
            rtf.SelectionStart = g.Index;
            rtf.SelectionLength = g.Length;
            rtf.SelectionFont = dataFont;
        }

        rtf.SelectionStart = selSt;
        rtf.SelectionLength = selLen;
    }

    private void MaskedEdit_FontChanged(object sender, EventArgs e)
    {
        dataFont = new Font(Font, FontStyle.Bold);
    }
}

#2


1  

I would suggest using multiple controls instead of just one. A single text box would require the user to cursor over to the next field, whereas multiple controls would allow the user to tab between fields, which is expected behavior. If you want to make it look like one control (perhaps to make it look like an address label, or something), you can use borderless textboxes and put the labels and textboxes all within a panel that has a border and a SystemColors.Window background. For an extra effect, it may be nice to put lines underneath each textbox, which is easily done with a 1-pixel high label with a BorderStyle.FixedSingle border.

我建议使用多个控件而不是一个。单个文本框将要求用户将光标移动到下一个字段,而多个控件将允许用户在字段之间进行选项卡,这是预期的行为。如果你想让它看起来像一个控件(也许是为了使它看起来像地址标签或其他东西),你可以使用无边框文本框并将标签和文本框全部放在具有边框和SystemColors.Window背景的面板中。为了获得额外的效果,可以在每个文本框下面放置线条,这可以通过带有BorderStyle.FixedSingle边框的1像素高标签轻松完成。