tags:

views:

603

answers:

7

How would you prevent the user from adding or removing lines in a TextBox? By that I mean, if I set the text in a textbox to 7 lines with some text, how can I make sure that it will always be 7 lines of text there? The user must be able to edit those lines like usual, but not remove a line entirely, and not add any new ones.

Would have to take into account both keyboard input and also things like cut and paste, etc.

Any good ideas?


Reason: I want to make a file renamer kind of like Oscar's Renamer. You give it a folder, and it loads the filenames into a textbox where you can do changes pretty much like you do in a text editor. When you are happy with your changes, you write them back. Reason for constant n lines in textbox is then of course that line n is the name of file n. Adding a new line shouldn't be allowed since you only have those files in that folder. Removing a line should also not be allowed since you would then be missing a name for a file.

Why go through the trouble of making something like this when it already exists? Well, I am curious to see if I could do it, and thought it could be a nice excercise to learn a few things along the way. Since it has some interesting problems that needs to be solved. Like this one :) There are also some features I think are lacking in that Oscar's Renamer. So... to sum up: I'm doing it to learn and to try make an even better version of it. I know fully well that I may just as well fail completely though, or just never finish it :p But that is another story. I want to learn :D

+6  A: 

You have the wrong interface then for the data. In this case, you should have a fixed number of textboxes, one for each line of data. This would allow the user to modify the contents of each line, but not remove a line or add a line.

Trying to make a multi-line textbox do this will be maddening at best, since you will have to determine when a new line is added/removed and then kill the change.

casperOne
I think it'd better if you can also provide an answer to the question, not just an advice.
Sung Meister
@Sung Meister: It is an answer. Svish asked for an idea on what to do, and he was given one.
casperOne
A: 

With javascript.

But keep in mind that

  • You may be making some use cases (such as reordering the items) much more difficult
  • You'll still have to validate serverside if it's at all important to you (e.g. if violating this constraint could cause a DOS)
  • You'll be getting error reports because it doesn't work the way users expect.

You may want to consider using seven one line fields instead of one textbox with an odd hand-rolled constraint.

-- MarkusQ

MarkusQ
@MarcusQ: JavaScript in a WinForms C# app? I don't think so.
Ken White
Throw him a break - the only mention of WinForms is in the tag. Easy to miss.
Gavin Miller
Right. So if his answer had been "OK. You turn the screw on the backside of the lawnmower carburetor counter-clockwise to enrich the mixture, clockwise to lean it out.", that's appropriate too, just because he didn't notice that the tag didn't say "lawnmower tuning"? It's just as applicable.
Ken White
Oh, and there's also the prominent "C#' as the first two characters of the subject. So if you don't want to read the tags, and you don't want to read the subject, why are you answering the post?
Ken White
@KenWhite: If you're going to be hyper critical of people's attention to detail you ought to make sure to spell their names correctly. I'm just saying.
MarkusQ
+2  A: 

I'd probably let the user do what they want on the textbox, then add validation to show the user there is an error when they go above 7 lines (e.g. red-outline and tooltip or something). If they are under 7 lines, no problem, add them when you come to process that data.

What is the specific reason you want the always 7 lines? Like casperOne said, maybe the interface you're using isn't ideal to your needs.

Andrew Barrett
+1  A: 

One possible way of doing this is to sub-class the Textbox control and override the winProc method. This method handles all window messages pumped to the windowed control (Textbox in your case). You could monitor the use of the backspace and delete keys and carat position and discard the key strokes that attempt to remove carriage return line feed sequences. And provide the user with an interactive alert that tells them why they cannot remove entire lines.

Doing it this way gives you complete control and is the lowest level way to see all input that is coming into your Textbox control. You can intercept certain messages and discard them, the ones that you want to allow just pass them through into the base class method. Such as if the user highlights all lines and hits the delete key. There is other event handlers that you can use to intercept keyboard input but they have some limitations, the winProc will allow you to check any message directed to the control including delete, backspace copy and paste etc, mouse clicks etc.

Sample:

public class myCustomTextBox : TextBox
{
    protected override void WndProc(ref Message m)
    {
        if (m.Msg == 770) // window paste message id
        {
            string clipBoardData = Clipboard.GetDataObject().GetData(DataFormats.Text).ToString();
            handlePasteEvent(clipBoardData);
        }
        else
        {
            base.WndProc(ref m);
        }
    }
    private void handlePasteEvent(string pasteData)
    {
        // process pasted data
    }
}
James
+3  A: 

Why not use a Listbox instead?

Ishmael
+1  A: 

Since you already have stated that this is a learning project, a see if I can do it-project, I think you should throw in some WPF into it. A listbox with a itemtemplate would solve this very nicely.

If that's out of the way, I would consider using a datagrid instead of a textbox.

Vegar
but can you edit the text as in a textbox then? wouldn't that just be like editing in separate textboxes? which would be like editing them separately, like in explorer? which is what I would like to avoid?
Svish
There would be one textbox for each listitem, but it would be different then editing in explorer. Every item would be a textbox, not just the one you are editing.
Vegar
A: 

Load your data in a editable DataGrid instead of a TextBox, that should make things much more simple, and also you can pick which columns are editable and which are not.

Here's an example that uses a DataGridView and simulates your textbox:

  • The grid lines are hidden.
  • The headers of columns and rows are hidden.
  • The background color is the same color a TexBox has.

Add a DataGridView to a form and use the following code:

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

namespace Test
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            this.dataGridView1.AllowUserToResizeRows = false;
            this.dataGridView1.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.Fill;
            this.dataGridView1.BackgroundColor = SystemColors.Window;
            this.dataGridView1.BorderStyle = BorderStyle.None;
            this.dataGridView1.CellBorderStyle = DataGridViewCellBorderStyle.None;
            this.dataGridView1.ColumnHeadersHeightSizeMode = DataGridViewColumnHeadersHeightSizeMode.AutoSize;
            this.dataGridView1.ColumnHeadersVisible = false;
            this.dataGridView1.EditMode = DataGridViewEditMode.EditProgrammatically;
            this.dataGridView1.MultiSelect = false;
            this.dataGridView1.RowHeadersVisible = false;
            this.dataGridView1.SelectionChanged += new System.EventHandler(this.dataGridView1_SelectionChanged);

            dataGridView1.DataSource = Directory.GetFiles(Environment.CurrentDirectory).Select(f => new FileEdit { FileName = Path.GetFileName(f) }).ToList();
        }

        private void dataGridView1_SelectionChanged(object sender, EventArgs e)
        {
            dataGridView1.BeginEdit(true);
        }

    }

    class FileEdit
    {
        public string FileName { get; set; }    
    }
}
Pop Catalin