I got some problems with getting caret index of TextBox in Windows Store App(WP 8.1).
I need to insert specific symbols to the text when button is pressed.
I tried this:
text.Text = text.Text.Insert(text.SelectionStart, "~");
But this code inserts symbol to the beginning of text, not to the place where caret is.
UPDATE
I updated my code thanks to Ladi. But now I got another problem: I'm building HMTL editor app so my default TextBlock.Text is: <!DOCTYPE html>\r\n<html>\r\n<head>\r\n</head>\r\n<body>\r\n</body>\r\n</html>
So for example when user inserts symbol to line 3, symbol is inserted 2 symbols before caret; 3 syms before when caret is in line 4 and so on. Inserting works properly when symbol is inserted to the first line.
Here's my inserting code:
Index = HTMLBox.SelectionStart;
HTMLBox.Text = HTMLBox.Text.Insert(Index, (sender as AppBarButton).Label);
HTMLBox.Focus(Windows.UI.Xaml.FocusState.Keyboard);
HTMLBox.Select(Index+1,0);
So how to solve this? I guess new line chars making trouble.
For your first issue I assume you changed the TextBox.Text before accessing SelectionStart. When you set the text.Text, text.SelectionStart is reset to 0.
Regarding your second issue related to new lines.
You could say that what you observe is by design. SelectionStart will count one "\r\n" as one character for reasons explained here (see Remarks section). On the other hand, method string.Insert does not care about that aspect and counts "\r\n" as two characters.
You need to change slightly your code. You cannot use the value of SelectionStart as the insert position. You need to calculate the insert position accounting for this behavior of SelectionStart.
Here is a verbose code sample with a potential solution.
// normalizedText will allow you to separate the text before
// the caret even without knowing how many new line characters you have.
string normalizedText = text.Text.Replace("\r\n", "\n");
string textBeforeCaret = normalizedText.Substring(0, text.SelectionStart);
// Now that you have the text before the caret you can count the new lines.
// that need to be accounted for.
int newLineCount = textBeforeCaret.Count(c => c == '\n');
// Knowing the new lines you can calculate the insert position.
int insertPosition = text.SelectionStart + newLineCount;
text.Text = text.Text.Insert(insertPosition, "~");
Also you should make sure that SelectionStart does not exhibit similar behavior with other combinations beside "\r\n". If it does you will need to update the code above.
Related
With the following code, I delete line by line in a TextBox (0, 39). Now there is on the last place a money amount (1 any Articel 10.00) which I want to deduct from the total amount. For that, I use the Substring. But there I get errors, as probably the empty spaces are not interpreted. Is there a simple solution to this? Thanks
private void btnDelete_Click(object sender, EventArgs e)
{
if (TextBox1.Text.Length > 0)
{
txtTotal.Text = (Double.Parse(txtTotal.Text) - Double.Parse(TextBox1.Text.Substring(8, 2))).ToString("0.00");
TextBox1.Text = TextBox1.Text.Remove(0, 39);
}
if (TextBox1.Text.Length == 0)
{
MessageBox.Show("The cart is empty");
Few things you can do to make your life easier (assuming you have to keep a TextBox as you have stated to others.)
Before I get into the details however, the issue seems to be you're having trouble parsing text that represents lines of data, data which contains amounts which you want to act on. If this is an incorrect assumption, disregard this answer.
Ok, back to it...
Rather than trying to work with the text directly in the TextBox, start by reading in your entire string as a list of lines (i.e. List<String>). You can do that with the Split function or with RegEx expressions. See here
Use RegEx expressions for each line to identify not just the type of line it is (an 'item' line or the 'All' line at the bottom) as well as the various parts of those lines. For instance, you can use a RegEx that starts at the end of the line and goes backwards looking for a number (in the form of a string.) Use the result of that for your Parse method to get the actual numeric value.
Finally, if you still need to remove the lines of text (I'm not sure if you're removing the text just for your logic or if you need to display it) simply remove them from your list of strings for the lines. If it needs to be displayed back in the UI (doubtful as it seems it should be blank at the end of processing) just use Join to convert the lines back to a string, then set that back to the TextBox.Text property.
Hope this helps!
Mark
P.S. To (try and) avoid comments such as the ones you got about your design, it may help to start your question by saying something like 'Unfortunately I'm restricted to using a TextBox due to issues outside of this question, hence I'm looking for an answer here.' At least that should cut back on those responses telling you to 'Do it differently!' instead of answering your question.
I'm working on my own syntax highlighter using a Richtextbox. It's already working, but I've noticed that the typing slows down a lot when there's to many lines of code. This is because my syntax highlight function is coloring all the words in the entire Richtextbox on every change made to it. Here's a minimal example of the function to see how it works:
private void colorCode()
{
// getting keywords/functions
string keywords = #"\b(class|function)\b";
MatchCollection keywordMatches = Regex.Matches(codeBox.Text, keywords);
// saving the original caret position + forecolor
int originalIndex = codeBox.SelectionStart;
int originalLength = codeBox.SelectionLength;
Color originalColor = Color.Black
// focuses a label before highlighting (avoids blinking)
titleLabel.Focus();;
// removes any previous highlighting (so modified words won't remain highlighted)
codeBox.SelectionStart = 0;
codeBox.SelectionLength = codeBox.Text.Length;
codeBox.SelectionColor = originalColor;
foreach (Match m in keywordMatches)
{
codeBox.SelectionStart = m.Index;
codeBox.SelectionLength = m.Length;
codeBox.SelectionColor = Color.Blue;
}
// restoring the original colors, for further writing
codeBox.SelectionStart = originalIndex;
codeBox.SelectionLength = originalLength;
codeBox.SelectionColor = originalColor;
// giving back the focus
codeBox.Focus();
}
To solve the problem, I want to write a function that doesn't change the entire Richtextbox, but just the line of the cursor position instead. I realise this will still cause the same issue on minified code, but that's not a problem for me. The problem is, I can't seem to get it working. This is what I've got so far:
void changeLine(RichTextBox RTB, int line, Color clr, int curPos){
string testWords = #"\b(test1|test2)\b";
MatchCollection testwordMatches = Regex.Matches(RTB.Lines[line], testWords);
foreach (Match m in testwordMatches)
{
//RTB.SelectionStart = m.Index;
//RTB.SelectionLength = m.Length;
RTB.SelectionColor = Color.Blue;
}
RTB.SelectionStart = curPos;
RTB.SelectionColor = Color.Black;
}
The problem is that it does the coloring when a word in testWords is found, but it colors the entire line instead of just the word. This is because I can't figure out a way of doing the selections right. So I'm hoping you guys can help me out with this.
Edit:
I'd like to add that I did thought about other solutions, like putting the lines in a List, or using a Stringbuilder. But those will turn the lines into strings and don't allow me to do color formatting like the Richtextbox does.
Well, you obviously need language lexer and parser. This task is not solvable by using Regex. It's just doesn't capable to accomplish this because of some fundamental grammar rules (or "power levels" of grammars) (read about Thomsky hierarchy of grammars).
What you need is to use some grammar toolkit. For example ANTLR4 provide grammar lexer/parser generator and set of already predefined grammars.
For example, you can find a lot of user-written grammars in here (including latest C# syntax): https://github.com/antlr/grammars-v4
Then just generate parser/lexer by it and feed it your string. It will output full hierarchy with indexes and lengths of each token, and you can colorize them without jumping across entire rich box.
Also, consider to use some timeout between user input, so you don't colorize your output every symbol (just save color from previous token, and use it for some time, until you recolorize output, then refresh). This way it will go as smoothly as it is in Visual Studio.
For Windows Forms.
I am trying to insert text into the .rtf field of a RichTextBox.
I have tried two methods. When I use .Rtf.insert, nothing happens at all.
When I edit the .rtf string based on the selected text positions myself, I either end up adding gibberish to the thing or getting an error that says that the file format is invalid. My best guess is that this is because the .rtf string is in .rtf format and the selection index that I am using is based on the plain text string and so I am inserting the text in the wrong location in the .rtf string and messing up the RTF code.
But knowing what the problem is (if I am correct) hasn't helped me solve it.
Is there a way to get .rtf.insert to work correctly, or is there a way to translate the selected text indexes to the actual .rtf text positions so that something like the code below would work? I am assuming that the RichTextBox itself must know how to translate the one index into another because it can insert characters when the user types just fine.
Here is my code snippet. The point of the code is to insert a marker into the text that will later be parsed and replaced with a student's first name. There will be other such codes. "codeLeader" and "codeEnder" are just the strings I use to surround the codes with. In this case I am using "[*" and *]" to indicate that there is a code I will need to parse, but I put them into separate strings so that I can easily change it if I wish. I have actually already written the parsing code, which works just fine on rich text. It is just inserting the text into the richTextBox itself that is the problem. In other words, if I were to type the codes by hand it would work just fine. But this would be troublesome for the user because some of the codes will use index numbers.
private void studentFirstNameCode_Click(object sender, EventArgs e)
{
string ins = f1ref.codeLeader;
ins += "SNFirst" + f1ref.codeEnder;
int start = editorField.richTextBox1.SelectionStart;
if (start == -1) { start = 0; }
int end = start + editorField.richTextBox1.SelectionLength;
if (end == -1) { end = 0; }
string pre = editorField.richTextBox1.Rtf.Substring(0, start);
string post = editorField.richTextBox1.Rtf.Substring(end);
string newstring = pre + ins + post;
editorField.richTextBox1.Rtf = newstring;
// this also doesn't work. gives no result at all.
// editorField.richTextBox1.Rtf.Insert(start, newstring);
}
I don't think that you need to use the RTF property to simple insert a text inside the RichTextBox actual text. In particular because you don't seem to add an RTF formatted text.
If you don't want to use RTF then the simplest way to accomplish your goal is just one line of code
editorField.SelectedText = yourParameterText;
This will work as you have pasted the text from the clipboard in the selected position (eventually replacing text if something is selected) and the base work of correctly formatting your text inside the RTF is done by the control itself
I have found a work-around by using .SendKeys. This makes the text appear a bit slowly (as if typed very quickly) so isn't optimal, but it does work.
It is enough for a workable solution, but I am still troubled by the problem. It seems like this issue should have a more elegant solution than this.
The "description" part of the labels I'm printing are usually short like "super" and "superman", but once in while they are long like "supercalifragilisticexpealidocious"
In the latter case, the text wraps around, but does not "crlf", thus printing over portions of the original line.
crlif'ing would not be good, either, because that would "throw off" the alignment of the rest of the label.
I came up with a workaround to truncate the description if it's too long:
public void PrintLabel(string price, string description, string barcode)
{
const int MAX_CAPS_DESC_LEN = 21;
const int MAX_LOWERCASE_DESC_LEN = 32;
try
{
// Users were getting wrapped descriptions causing legibility/alignment
//problems; this code could be elegantized, but at the cost of readability
bool descAllUpper = HHSUtils.IsAllUpper(description);
if (descAllUpper)
{
if (description.Length > MAX_CAPS_DESC_LEN)
{
description = description.Substring(0, MAX_CAPS_DESC_LEN);
}
}
else // not all upper
{
if (description.Length > MAX_LOWERCASE_DESC_LEN)
{
description = description.Substring(0, MAX_LOWERCASE_DESC_LEN);
}
}
. . .
...but is there a way (a property/setting/configuration/command or such) that would tell the printer to stop at the edge of the label?
In windows > control panel > printers and devices, right click and printer properties. You can set margins and stuff. The zebra printers have lots of settings in there.
Also, we use smaller fonts to allowore characters, and uline.com sells 4x8 labels I believe instead of using standard 4x6.
Unfortunately, there is not an option for Text Field Word Wrap in the current Zebra Designer Software as of 2012-12-17 date. If you want to achieve this you must also add ZPL code into the PRN file as per the instructions taken from the ZPL Programming Manual Below.
EDIT: Had to indent all because stack was being weird.
Source Easyworks.com
Sample PRN File With and Without Code Code containing Word Wrap [highlighted in red]
CT~~CD,~CC^~CT~
^XA~TA000~JSN^LT0^MNW^MTD^PON^PMN^LH0,0^JMA^PR2,2~SD8^JUS^LRN^CI0^XZ
^XA
^MMT
^PW448
^LL0488
^LS0
^BY1,3,34^FT84,244^BCN,,Y,N
^FD>:<%EXP%>0^FS
^BY1,3,34^FT84,175^BCN,,Y,N
^FD>:<%UID%>0^FS
^BY1,3,34^FT84,106^BCN,,Y,N
^FD>:<%PARTNO%>0^FS
^FT84,344^AAN,27,15^FH\^FD<%EXP%>^FS
^FB500,3,,, //This is what the below is referencing
^FT84,293^AAN,18,10^FH\^FD<%WARN%>^FS
^PQ1,0,1,Y^XZ
Code Without Word Wrap
CT~~CD,~CC^~CT~
^XA~TA000~JSN^LT0^MNW^MTD^PON^PMN^LH0,0^JMA^PR2,2~SD8^JUS^LRN^CI0^XZ
^XA
^MMT
^PW448
^LL0488
^LS0
^BY1,3,34^FT84,244^BCN,,Y,N
^FD>:<%EXP%>0^FS
^BY1,3,34^FT84,175^BCN,,Y,N
^FD>:<%UID%>0^FS
^BY1,3,34^FT84,106^BCN,,Y,N
^FD>:<%PARTNO%>0^FS
^FT84,344^AAN,27,15^FH\^FD<%EXP%>^FS
^FT84,293^AAN,18,10^FH\^FD<%WARN%>^FS
^PQ1,0,1,Y^XZ
ZPL Programming Manual Excerpt Here is also an excerpt from the programming manual about the ^FB command
^FB - Field Block
Description The ^FB command allows you to print text into a defined block type format.
This command formats an ^FD or ^SN string into a block of text using the origin, font, and
rotation specified for the text string. The ^FB command also contains an automatic word-wrap function.
Format ^FBa,b,c,d,e
This table identifies the parameters for this format:
Parameters
a = width of text block line (in dots)
Accepted Values: 0 to the width of the label
Default Value: 0
If the value is less than font width or not specified, text does not print.
b = maximum number of lines in text block
Accepted Values: 1 to 9999
Default Value: 1
Text exceeding the maximum number of lines overwrites the last line. Changing the font size automatically increases or decreases the size of the block.
c = add or delete space between lines (in dots)
Accepted Values: -9999 to 9999
Default Value: 0
Numbers are considered to be positive unless preceded by a minus sign. Positive values add space; negative values delete space.
d = text justification Accepted Values:
L = left
C = center
R = right
J = justified
Default Value: L
If J is used the last line is left-justified.
e = hanging indent (in dots) of the second and remaining lines
Accepted Values: 0 to 9999
Default Value: 0
Comments on the ^FB Command This scheme can be used to facilitate special functions:
\& = carriage return/line feed
\(*) = soft hyphen (word break with a dash)
\\ = backslash (\)
Item 1: ^CI13 must be selected to print a backslash (\).
Item 2: If a soft hyphen escape sequence is placed near the end of a line, the hyphen is printed. If it is not placed near the end of the line, it is ignored.
(*) = any alphanumeric character
" If a word is too long to print on one line by itself (and no soft hyphen is specified), a hyphen is automatically placed in the word at the right edge of the block. The remainder of the word is on the next line. The position of the hyphen depends on word length, not a syllable boundary. Use a soft hyphen within a word to control where the hyphenation
occurs.
" Maximum data-string length is 3K, including control characters, carriage returns, and line feeds.
" Normal carriage returns, line feeds, and word spaces at line breaks are discarded.
" When using ^FT (Field Typeset), ^FTuses the baseline origin of the last possible line of text. Increasing the font size causes the text block to increase in size from bottom to top. This could cause a label to print past its top margin.
" When using ^FO (Field Origin), increasing the font size causes the text block to increase in size from top to bottom.
" ^FS terminates an ^FB command. Each block requires its own ^FB command.
Consider a RichTextBox which has 400 lines and includes a number of words and lines in diffident colours.
Is it possible to remove the first 100 lines of this text box, while the colour of remaining words are reserved. Currently, I am using the below code to remove lines, but It is not able to keep colours.
if (rtb.Lines.Count() > 400)
rtb.Lines = rtb.Lines.Skip(100).ToArray();
Use the SelectionText property. First select the lines you want to remove, then remove them by setting SelectionText to an empty string. Like this:
richTextBox1.SelectionStart = 0;
richTextBox1.SelectionLength = richTextBox1.GetFirstCharIndexFromLine(200);
richTextBox1.SelectedText = "";
This preserves the formatting of all the other lines. This can cause visible flicker on the UI, you can suppress that by implementing the Begin/EndUpdate methods as shown here.
You can't use the Lines property if you want to preserve the formatting. Lines is derived from TextBoxBase. You need to use the Rtf property and parse the lines yourself in the string you get back. If you want to just get the line count and then parse the RTF then you could do something like:
// NOTE: I am using Length rather than Count() because the array already knows its length
if (rtb.Lines.Length > 400)
{
// Parse the rtf here to remove the unwanted lines and preserve the format
}
You would need to look at the RTF specification to accurately pull out the actual lines. A line break is indicated by the tag \par. The line that would be tricky to deal with is the first line because it may contain extra information before the actual first line text.
.SelectedText = "" throws a Windows ding in my application
So I found a second solution which is to play with .Lines property
if (nbLines > maxLines)
{
Array.Copy(rtfBox.Lines, 1,
rtfBox.Lines, 0, rtfBox.Lines.Length - 1);
}