This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
How to prevent richTextBox to paste images within it?
If you're using Richtextbox, there are several advantages in Richtextbox for example:
we can use color font on it
Setting custom font in a region
Attach files on it.. etc
take a look at the picture:
Here is my problem:
Can i just make it text only?
In my project, attach file or the like is unnecessary at all. I even didn't want attach or paste an images on it, i just want "text only" on Richtextbox
How can i do that?
Since RichTextBox doesn't have a Images or Objects collection you have to go for the RTF formatting codes. All data of RichTextBox is stored as plain text with special formatting codes, this is exposed by the control through its RTF property. Learning this code language is essential if you want to read or change it, learning resources are easily available throughout the web, see for example this overview. RichTextBox uses more simplified rtf codes than several full-feature editors like MS Word etc, so it is usually beneficial to load data into a RTB before manipulating it, this will remove much redundant data.
Making a long story short, I found that it is necessary to search for rtf groups that start with either "pict" or "object" command. Knowing that groups may be nested you can't just find the first end-group char from there, you have to parse the string char by char while keeping count of grouping to find the end of those groups. Now you have enough information to remove that part of the string. Rtf may contain multiple picture/object groups so you have to do this until all are removed. Here is a sample function that return rtf string after removing those groups:
private string removeRtfObjects(string rtf)
{
//removing {\pict or {\object groups
string pattern = "\\{\\\\pict|\\{\\\\object";
Match m = Regex.Match(rtf, pattern);
while (m.Success) {
int count = 1;
for (int i = m.Index + 2; i <= rtf.Length; i++) {
//start group
if (rtf(i) == '{') {
count += 1;
//end group
} else if (rtf(i) == '}') {
count -= 1;
}
//found end of pict/object group
if (count == 0) {
rtf = rtf.Remove(m.Index, i - m.Index + 1);
break; // TODO: might not be correct. Was : Exit For
}
}
m = Regex.Match(rtf, pattern);
//go again
}
return rtf;
}
When should this be done? You have already mention Paste, there is also Insert, these can be trapped with the KeyDown event where you get the clipboard info and handle it accordingly. Setting e.Handled=True when you have handled the operation yourself signals that the control should not do any default processing for this key combination. This is also how you block pasting images without destroying the users clipboard. Example:
private void RichTextBox1_KeyDown(object sender, System.Windows.Forms.KeyEventArgs e)
{
//aware of Paste or Insert
if (e.Control && e.KeyCode == Keys.V || e.Shift && e.KeyCode == Keys.I) {
if (Clipboard.ContainsImage || Clipboard.ContainsFileDropList) {
//some images are transferred as filedrops
e.Handled = true;
//stops here
} else if (Clipboard.ContainsData(DataFormats.Rtf)) {
RichTextBox rtbox = new RichTextBox();
//use a temp box to validate/simplify
rtbox.Rtf = Clipboard.GetData(DataFormats.Rtf);
this.RichTextBox1.SelectedRtf = this.removeRtfObjects(rtbox.Rtf);
rtbox.Dispose();
e.Handled = true;
}
}
}
Yes, it is possible.
Handle Ctrl+V in RichTextBox1_KeyDown, then check the data format in the Clipboard: if data is plain text, paste it; if data is RTF, convert it to plain text (in a buffer without changing the Clipboard content) and paste it; don't paste any other type of data.
This is a partial example just to show you how to proceed:
private void richTextBox1_KeyDown(object sender, KeyEventArgs e)
{
if (e.Control && e.KeyCode == Keys.V)
{
// suspend layout to avoid blinking
richTextBox2.SuspendLayout();
// get insertion point
int insPt = richTextBox2.SelectionStart;
// preserve text from after insertion pont to end of RTF content
string postRTFContent = richTextBox2.Text.Substring(insPt);
// remove the content after the insertion point
richTextBox2.Text = richTextBox2.Text.Substring(0, insPt);
// add the clipboard content and then the preserved postRTF content
richTextBox2.Text += (string)Clipboard.GetData("Text") + postRTFContent;
// adjust the insertion point to just after the inserted text
richTextBox2.SelectionStart = richTextBox2.TextLength - postRTFContent.Length;
// restore layout
richTextBox2.ResumeLayout();
// cancel the paste
e.Handled = true;
}
}
Related
I'm curious as to add a feature to my program so when it detects the clipboard text to be a certain length and start with certain characters, when they paste the text from the clipboard it pastes someting different.
Example:
User copies 'abcdefghijklmnopqrstuvwxyz'
--Program intercepts recognizes the certain string length and starting characters and switches text--
User pastes 'zyxwvutsrqponmlkjihgfedcba'
Help would be appreciated :)
If you just need directions:
To copy to clipboard you can use:
Clipboard.SetText("abcdefghijklmnopqrstuvwxyz");
Use this to get text from ClipBoard:
string s = Clipboard.GetText();
After getting the text you can apply your rules like checking length and Starting characters and do your conversion and finally add the converted text to your textbox.
if (s.Length > 16 && s.StartsWith("abc")
{
// do your stuff here
string convertedText = ConvertText(s);
//finally
txtBoxToPasteTo.Text = convertedText;
}
To intercept a paste, you need to create a custom textbox
class InterceptedTextBox : System.Windows.Forms.TextBox
{
protected override void WndProc(ref Message m)
{
// WM_PASTE:
if (m.Msg == 0x302 && Clipboard.ContainsText()) {
this.Text = ConvertText(Clipboard.GetText());
return;
}
base.WndProc(ref m);
}
}
References
I'm pulling the content from a text file into a RichTextBox. I've got the RichTextBox set up to where it only shows 6 lines at a time. I've got a search method that finds the text I need within the RichTextBox, but what I am needing it to do is display 6 specific lines. Each "item" in my text file consists of 6 lines. No matter which of the six lines the search method finds the text on, I need the RichTextBox to only display the 6 lines of each "item" with the currently selected "found" text remaining highlighted.
I've gotten it working reasonably well thanks to a few code examples I've pull from this site. But every now and then, it doesn't work entirely well, and am looking for some advice from a fresh set of eyes looking at my code and perhaps even be told an easier/more efficient way to go about it. But here is my code so far. Thanks in advance!
try
{
string s = txtFindPlaylistEntry.Text;
rtxEditPlaylistEntry.Focus();
findPosEntry = rtxEditPlaylistEntry.Find(s, findPosEntry, RichTextBoxFinds.None);
// Jump to the line we need.
int count = rtxEditPlaylistEntry.GetLineFromCharIndex(findPosEntry);
count = (count - (count % 6)) + 1; // Must be divisible by 6 then plus 1
rtxEditPlaylistEntry.SelectionStart = rtxEditPlaylistEntry.Find(rtxEditPlaylistEntry.Lines[count]);
rtxEditPlaylistEntry.ScrollToCaret();
rtxEditPlaylistEntry.Select(findPosEntry, s.Length);
findPosEntry += txtFindPlaylistEntry.Text.Length;
}
catch
{
MessageBox.Show("No occurences found");
findPosEntry = 0;
}
As of right now, I'm attempting to use a line count with modulus plus 1 to get the line I need. Like I said, it works, just not 100% of the time and I can't figure out why.
EDIT to try to accommodate Minimal, Complete, Verifiable.
I've already posted my "find" function. Here is other related code that might be useful. First, here is my code for creating the various controls I am using.
rtxEditPlaylistEntry = new RichTextBox();
rtxEditPlaylistEntry.Location = new System.Drawing.Point(15, 90);
rtxEditPlaylistEntry.Size = new System.Drawing.Size(375, 85);
rtxEditPlaylistEntry.Multiline = true;
rtxEditPlaylistEntry.ScrollBars = RichTextBoxScrollBars.None;
Here is my button function to pull text from a file and place it into the RichTextBox.
private void btnBrowseForPlaylistToEditEntry_Click(object sender, EventArgs e)
{
OpenFileDialog openFileDialog = new OpenFileDialog();
openFileDialog.Filter = "LPL Files|*.lpl";
if (openFileDialog.ShowDialog() == DialogResult.OK)
{
savedFileNameEntry = openFileDialog.SafeFileName;
txtPathToPlaylistToEditEntry.Text = openFileDialog.FileName;
}
// After finding the file, load it into the richtextbox control
using (StreamReader sr = File.OpenText(openFileDialog.FileName))
{
// Initially show the first 6 lines (IE first entry). This should be accomplished by
// the richtextbox control settings
rtxEditPlaylistEntry.Text = sr.ReadToEnd();
}
previousNextCount = 0;
}
I hope this is sufficient. If not please let me know!
I'm using the RichEditBox to build a simple editor.
I found a piece of code which toggles bold text on a selection within the document window
private void RichEditBox_KeyDown(object sender, KeyRoutedEventArgs e)
{
var state = Window.Current.CoreWindow.GetKeyState(Windows.System.VirtualKey.Control);
if ((state & CoreVirtualKeyStates.Down) == CoreVirtualKeyStates.Down)
{
if (e.Key == Windows.System.VirtualKey.B)
{
if (Editor.Document.Selection.Text.Length > 0)
{
ITextCharacterFormat format = Editor.Document.Selection.CharacterFormat;
format.Bold = FormatEffect.On;
}
else
{
// CTRL + B should toggle bold mode on or off here.
}
}
}
}
When I highlight a piece of text, and press CTRL+B, it bolds the text. Result. However, anything I type after that point is also in bold.
This is not what I expected. According to the code above, I'm affecting the format of the Selection only.
When I select some text and press CTRL+B it should toggle bold formatting on that selection and leave the default format as is.
I've tried using FormatEffect.Toggle
format.Bold = FormatEffect.Toggle
I've tried saving out the Document Character format first, then reapplying
ITextCharacterFormat original_format = Editor.Document.GetDefaultCharacterFormat();
ITextCharacterFormat format = Editor.Document.Selection.CharacterFormat;
format.Bold = FormatEffect.On;
Editor.Document.SetDefaultCharacterFormat(original_format);
This should reset the default back to what it was after bolding. But it doesn't
I could set the selection to nothing, then set format.Bold = FormatEffect.Off again, then reselect the text, but that seems like the long way around (and it probably won't work). There must be a simple way to do this?
NOTE: I have tagged this with the RichTextBox tag as there is no RichEditBox tag. Can someone with >1500 rep add one?
That's the normal behaviour when using SelectionFont and setting bold style with richtextbox.
If the current text selection has more than one font specified, this
property is null. If no text is currently selected, the font specified
in this property is applied to the current insertion point and to all
text that is typed into the control after the insertion point.
It's probably the same issue that you have.
Wordpad and Word work this way too.
I have found a hack, that works. I'm posting this as an answer for anyone who's stuck, but I'm not accepting the answer, because there must be a better one.
private void RichEditBox_KeyDown(object sender, KeyRoutedEventArgs e)
{
var state = Window.Current.CoreWindow.GetKeyState(Windows.System.VirtualKey.Control);
if ((state & CoreVirtualKeyStates.Down) == CoreVirtualKeyStates.Down)
{
if (e.Key == Windows.System.VirtualKey.B)
{
if (Editor.Document.Selection.Text.Length > 0)
{
// text is selected. make it bold
ITextCharacterFormat format =
Editor.Document.Selection.CharacterFormat;
format.Bold = FormatEffect.On;
var start_pos = Editor.Document.Selection.StartPosition;
Editor.Document.Selection.StartPosition =
Editor.Document.Selection.EndPosition;
format.Bold = FormatEffect.Off;
// Editor.Document.Selection.StartPosition = start_pos;
// this is where I was re-selecting the text after switching bold OFF
// but doing so switches it back on again. which makes no sense
}
else
{
// no text selected. just enable bold mode
ITextCharacterFormat format =
Editor.Document.Selection.CharacterFormat;
format.Bold = FormatEffect.Toggle;
}
}
}
}
This isn't perfect, because after you've selected and bolded the text it is automatically de-selected. However in practice I find that this actually works fine for me. Still, it feels like a hack, because it is a hack
I am programatically adding text in a custom RichTextBox using a KeyPress event:
SelectedText = e.KeyChar.ToString();
The problem is that inserting text in such a way doesn't trigger the CanUndo flag.
As such, when I try to Undo / Redo text (by calling the Undo() and Redo() methods of the textbox), nothing happens.
I tried programatically evoking the KeyUp() event from within a TextChanged() event, but that still didn't flag CanUndo to true.
How can I undo text that I insert without having to create lists for Undo and Redo operations ?
Thanks
I finally decided to create my own undo-redo system using stacks.
Here's a quick overview of how I did it :
private const int InitialStackSize = 500;
private Stack<String> undoStack = new Stack<String>(InitialStackSize);
private Stack<String> redoStack = new Stack<String>(InitialStackSize);
private void YourKeyPressEventHandler(...)
{
// The user pressed on CTRL - Z, execute an "Undo"
if (e.KeyChar == 26)
{
// Save the cursor's position
int selectionStartBackup = SelectionStart;
redoStack.Push(Text);
Text = undoStack.Pop();
// Restore the cursor's position
SelectionStart = selectionStartBackup;
}
// The user pressed on CTRL - Y, execute a "Redo"
if (e.KeyChar == 25)
{
if (redoStack.Count <= 0)
return;
// Save the cursor's position
int selectionStartBackup = SelectionStart + redoStack.ElementAt(redoStack.Count - 1).Length;
undoStack.Push(Text);
Text = redoStack.Pop();
// Restore the cursor's position
SelectionStart = selectionStartBackup;
return;
}
undoStack.Push(Text);
SelectedText = e.KeyChar.ToString();
}
It's just an idea but what if you set the caret position to where you would insert your text and instead of modifying the Text property, just send the keys?
SendKeys.Send("The keys I want to send");
There are bound to be quirks but as I said, it's just an idea.
You can use TestBox.Paste. The documentation in the class overview, saying "Sets the selected text to the specified text without clearing the undo buffer.", seems confusing. I have just tried it and it sets the Undo as expected.
Is spite of its name it has no relation to Clipboard at all, it just replaces the currently selected text with the text you provide as an argument, and therefore seems just to do what the question asks for, in very simple manner.
Is it possible to force a textbox in a windows forms application to work in "overwrite mode", i.e. have characters replaced when the user types instead of added?
Otherwise, is there a standard way to get this behavior?
Try using a MaskedTextBox and set InsertKeyMode to InsertKeyMode.Overwrite.
MaskedTextBox box = ...;
box.InsertKeyMode = InsertKeyMode.Overwrite;
If you do not wish to use a Masked textbox you can do this when handling the KeyPress event.
private void Box_KeyPress(object sender, KeyPressEventArgs e)
{
TextBox Box = (sender as TextBox);
if (Box.SelectionStart < Box.TextLength && !Char.IsControl(e.KeyChar))
{
int CacheSelectionStart = Box.SelectionStart; //Cache SelectionStart as its reset when the Text property of the TextBox is set.
StringBuilder sb = new StringBuilder(Box.Text); //Create a StringBuilder as Strings are immutable
sb[Box.SelectionStart] = e.KeyChar; //Add the pressed key at the right position
Box.Text = sb.ToString(); //SelectionStart is reset after setting the text, so restore it
Box.SelectionStart = CacheSelectionStart + 1; //Advance to the next char
}
}
Standard way would be to select the existing text as you land in the textbox, then as the user types it will automatically replace the existing text
This code seems to have an error. I found that you need to set e.Handled in the Keypress event, otherwise the character is inserted twice. Here is my code (in VB) based on the above: -
Private Sub txtScreen_KeyPress(sender As Object, e As KeyPressEventArgs) Handles txtScreen.KeyPress
If txtScreen.SelectionStart < txtScreen.TextLength AndAlso Not [Char].IsControl(e.KeyChar) Then
Dim SaveSelectionStart As Integer = txtScreen.SelectionStart
Dim sb As New StringBuilder(txtScreen.Text)
sb(txtScreen.SelectionStart) = e.KeyChar
'Add the pressed key at the right position
txtScreen.Text = sb.ToString()
'SelectionStart is reset after setting the text, so restore it
'Advance to the next char
txtScreen.SelectionStart = SaveSelectionStart + 1
e.Handled = True
End If
End Sub
You could use a RichTextBox instead of a TextBox.
Source:
https://social.msdn.microsoft.com/Forums/en-US/966c3af9-6674-4f48-b487-7afbef05f0cb/overwrite-mode-in-a-textbox
thanks for all! this is mine now. ref :
https://www.syncfusion.com/faq/windowsforms/textbox/how-can-i-place-a-textbox-in-overwrite-mode-instead-of-insert-mode
int surrogate = 0;
private void textBox1_KeyPress(object sender, KeyPressEventArgs e)
{
if (!insertMode)
{
if (char.IsSurrogate(e.KeyChar))
{
surrogate++;
if (surrogate % 2 != 0)
{
return;
}
surrogate = 0;
}
if (textBox1.Text.Length != textBox1.MaxLength && textBox1.SelectedText == ""
&& textBox1.Text != "" && textBox1.SelectionStart != textBox1.Text.Length)
{
textBox1.SelectionLength = 1;//對於已經輸入完成的 surrogate C#應該會正確判斷其字長度;實際測試非然也
//for surrogate pairs input this is necessary or it will cut the pairs in wrong way above
if (char.IsSurrogate(textBox1.SelectedText.ToCharArray()[0]))
{
textBox1.SelectionLength = 2;
}
}
}
}
感恩感恩 南無阿彌陀佛
my applied repo :
https://github.com/oscarsun72/TextForCtext.git
WindowsFormsApp1/Form1.cs
阿彌陀佛
Not sure if using the KeyPress event messes up the normal overtype process, or maybe something specific being checked for within KeyPress, but this isn't quite how a normal windows text box should behave, in that when you start typing in a control with highlighted text, the selection should be removed, allowing you to type in that emptied space. Once I saw the If statement I realised the behaviour I was looking for was accomplished doing this:
If tb.SelectionStart < tb.TextLength AndAlso Not [Char].IsControl(e.KeyChar) Then
tb.SelectedText = ""
End If
not sure why you would want to preserve the selection, but the previous code is ideal if that's what you need
Sal