views:

96

answers:

2

I have a Windows Form with a RichTextBox on it. The content of the RichTextBox is written to a database field that ist limited to 64k data. For my purpose that is way more than enough text to store.

I have set the MaxLength property to avoid insertng more data than allowed.

rtcControl.MaxLength = 65536

Howevery, that only restricts the amount of characters that so is allowed to put in the text. But with the formatting overhead from the Rtf I can type more text than I should be allowed to. It even get's worse if I insert a large image, which dosn't increase the TextLength at all but the Rtf Length grows quite a lot.

At the moment I check the Length of the richttextboxes' Rtf property in the FormClosing event and display a message to the user if it's to large. However that is just a workaround because I want to disallow putting more data than allowed into the control (like in a textbox if you exceed the MaxLength property nothing is inserted into the control and you hear the default beep().

Any ideas how to achive this?

I already tried:

  • using a custom control which extends the richtextbox and shadows th Rtf property to intercept the insertation. But it seems it isn't executed if I add text.

  • Even the TextChanged Event does not fire if I type smth. in the control.

+1  A: 

What about doing this:

Handle the TextChanged event and compare each time it changes. It fired for text entry and image drag and drops.

private void richTextBox1_TextChanged(object sender, EventArgs e)
{
   if (richTextBox1.Rtf.Length > richTextBox1.MaxLength)
   {
      // do something here - I displayed a label for
      // my example 
      label1.Text = "Text exceeds maximum size";
      label1.ForeColor = Color.Red;
   }
   else
   {
      label1.Text = richTextBox1.Rtf.Length.ToString();
      label1.ForeColor = Color.Red;
   }
}

This seemed to work, though I didn't spend a lot of time on this, admittedly. I suspect you could truncate the contents if it exceeds the max size.


Edit: I thought about this some more and I suspect you could utilize a StringBuilder to store off the contents of the richtextbox each time and if one attempted to exceed that length, restore the data to the previous state. I admit this is a bit hacky but it seems to work. Something like this:

StringBuild sb = new StringBuilder();

private void richTextBox1_TextChanged(object sender, EventArgs e)
{
   if (richTextBox1.Rtf.Length > richTextBox1.MaxLength)
   {
      richTextBox1.Rtf = sb.ToString();
   }
   else
   {
      sb.Insert(0,richTextBox1.Rtf);
   }
}

This seems to work pretty well. There might be a more elegant solution.

itsmatt
I haven't tried it yet, but why do you use a stringbuilder in this case? Do I get better performance compared to a string?
SchlaWiener
@SchlaWiener - Yes, probably the string would be better. Initially I was thinking about using the StringBuilder to append the new RTF stuff but abandoned that idea in favor of the simpler "copy everything" approach.
itsmatt
A: 

In Response to itsmatt:

That worked, except for the TextChanged Event which seems not to fire in my environment (don't know why). But since I have my own usercontrol which is interited from RichTextBox I was able to override the OnTextChanged() method which get's called. But the code get's a little bit more complicated, because if you change the text in the OnTextChanged() method it gets called again, which leads to a StackOverflowException. I also wanted a Beep() and the cursor position/selection should be remembered.

With this code it get's kinda slow with text > 30000 but that's good enough for me. The users shouldn't store so much data in it anyway.

    private string lastText;
    private string lastRtf;
    private int lastSelectionStart;
    private int lastSelectionLength;

    private bool skipLengthCheck;
    protected override void OnTextChanged(EventArgs e)
    {
        if (Rtf.Length > MaxLength && !skipLengthCheck)
        {
            skipLengthCheck = true;
            Console.WriteLine("MaxLength exceeded");
            System.Media.SystemSounds.Beep.Play();
            int start = lastSelectionStart;
            int length = lastSelectionLength;
            base.Text = lastText;
            base.Rtf = lastRtf;
            SelectionStart = start > 0 ? start - 1 : 0;
            SelectionLength = length;
            skipLengthCheck = false;               
        }
        else
        {
            lastText = Text;
            lastRtf = Rtf;

            base.OnTextChanged(e);
        }

    }

    protected override void OnSelectionChanged(EventArgs e)
    {
        lastSelectionStart = SelectionStart;
        lastSelectionLength = SelectionLength;

        base.OnSelectionChanged(e);
    }
SchlaWiener