I want to get the text between ';' character. However if there are 3 matches like that, i want to get the text between ';' character where current cursor position. I am using multine textbox.
Example;
select * from database;
----> **Lets say my cursor is here**
select
orders from
customer;
select * from employees;
So, i only want to get 'select orders from customer' text.
Could you please share your thoughts on it?
To achieve this, you first have to find all indicies of ;. To do this, iterate through all indicies (source):
private List<int> AllIndicesOf(string strToSearch, string fullText)
{
List<int> foundIndices = new List<int>();
for (int i = fullText.IndexOf(strToSearch); i > -1; i = fullText.IndexOf(strToSearch, i + 1))
{
foundIndices.Add(i + 1);
}
return foundIndices;
}
Then you have to compare your position to those indices, since you only want the index (of ;) that follows immediately after your cursor:
List<int> indicies = AllIndicesOf(";", txtBxText.Text);
try
{
if (indicies.Count > 0)
{
int cursorPos = txtBxText.SelectionStart;
var indicesBefore = indicies.Where(x => x < cursorPos);
int beginIndex = indicesBefore.Count() > 0 ? indicesBefore.Last() : 0;
int endIndex = indicies.Where(x => x > beginIndex).First();
txtBxSelected.Text = txtBxText.Text.Substring(beginIndex, endIndex - beginIndex);
}
}
catch { }
The try-catch statement is used to prevent an Exception if your cursors position is after all other indices.
A sample project can be downloaded here.
This solution perfectly works although you need to check it again and consider some possible exceptions. I did not consider them myself because I thought it was better to be handled by you. I also used richTextBox which is better than the multi line text box. Enjoy the code bro
private void button1_Click(object sender, EventArgs e)
{
var ultimateResult = string.Empty;
var cusrPosition = richTextBox1.SelectionStart;
var currentStr = string.Empty;
var strDic = new Dictionary<string,int>();
var textArr = richTextBox1.Text.ToCharArray();
for (var i = 0; i < textArr.Count(); i++)
{
if (textArr[i] != ';')
currentStr = currentStr + textArr[i];
else
{
strDic.Add(currentStr,i);
currentStr = string.Empty;
}
}
ultimateResult = strDic.First(item => item.Value >= cusrPosition).Key;
textBox1.Text = ultimateResult;
}
Related
In the constructor :
var tempFR = File.ReadAllText(file);
GetResults(tempFR);
Then :
private List<string> GetResults(string file)
{
List<string> results = new List<string>();
string word = textBox1.Text;
string[] words = word.Split(new string[] { ",," }, StringSplitOptions.None);
for(int i = 0; i < words.Length; i++)
{
int start = file.IndexOf(words[i], 0);
results.Add(file.Substring(start));
}
return results;
}
words contains in this case 3 words System , public , test
I want to find all the words in file and add them to the list results using indexof and substring.
The way it is now start value is -1 all the time.
To clear some things.
This is a screenshot of the textBox1 :
That is why I'm using two commas to split and get the words.
This screenshot showing the words after split them from the textBox1 :
And this is the file string content :
I want to add to the List results all the words in the file.
When looking at the last screenshot there should be 11 results.
Three time the word using three times the word system five times the word public.
but the variable start is -1
Update :
Tried Barns solution/s but for me it's not working good.
First the code that make a search and then loop over the files and reporting to backgroundworker :
int numberofdirs = 0;
void DirSearch(string rootDirectory, string filesExtension, string[] textToSearch, BackgroundWorker worker, DoWorkEventArgs e)
{
List<string> filePathList = new List<string>();
int numberoffiles = 0;
try
{
filePathList = SearchAccessibleFilesNoDistinct(rootDirectory, null, worker, e).ToList();
}
catch (Exception err)
{
}
label21.Invoke((MethodInvoker)delegate
{
label21.Text = "Phase 2: Searching in files";
});
MyProgress myp = new MyProgress();
myp.Report4 = filePathList.Count.ToString();
foreach (string file in filePathList)
{
try
{
var tempFR = File.ReadAllText(file);
_busy.WaitOne();
if (worker.CancellationPending == true)
{
e.Cancel = true;
return;
}
bool reportedFile = false;
for (int i = 0; i < textToSearch.Length; i++)
{
if (tempFR.IndexOf(textToSearch[i], StringComparison.InvariantCultureIgnoreCase) >= 0)
{
if (!reportedFile)
{
numberoffiles++;
myp.Report1 = file;
myp.Report2 = numberoffiles.ToString();
myp.Report3 = textToSearch[i];
myp.Report5 = FindWordsWithtRegex(tempFR, textToSearch);
backgroundWorker1.ReportProgress(0, myp);
reportedFile = true;
}
}
}
numberofdirs++;
label1.Invoke((MethodInvoker)delegate
{
label1.Text = string.Format("{0}/{1}", numberofdirs, myp.Report4);
label1.Visible = true;
});
}
catch (Exception err)
{
}
}
}
I have the words array already in textToSearch and the file content in tempFR then I'm using the first solution of Barns :
private List<string> FindWordsWithtRegex(string filecontent, string[] words)
{
var res = new List<string>();
foreach (var word in words)
{
Regex reg = new Regex(word);
var c = reg.Matches(filecontent);
int k = 0;
foreach (var g in c)
{
Console.WriteLine(g.ToString());
res.Add(g + ":" + k++);
}
}
Console.WriteLine("Results of FindWordsWithtRegex");
res.ForEach(f => Console.WriteLine(f));
Console.WriteLine();
return res;
}
But the results I'm getting in the List res is not the same output in Barns solution/s this is the results I'm getting the List res for the first file :
In this case two words system and using but it found only the using 3 times but there is also system 3 times in the file content. and the output format is not the same as in the Barns solutions :
Here is an alternative using Regex instead of using IndexOf. Note I have created my own string to parse, so my results will be a bit different.
EDIT
private List<string> FindWordsWithCountRegex(string filecontent, string[] words)
{
var res = new List<string>();
foreach (var word in words)
{
Regex reg = new Regex(word, RegexOptions.IgnoreCase);
var c = reg.Matches(filecontent).Count();
res.Add(word + ":" + c);
}
return res;
}
Simple change this part and use a single char typically a space not a comma:
string[] words = word.Split(' ');
int start = file.IndexOf(words[i],0);
start will be -1 if the word is not found.
MSDN: IndexOf(String, Int32)
for(int i = 0; i < words.Length; i++)
{
int start = file.IndexOf(words[i], 0);
// only add to results if word is found (index >= 0)
if (start >= 0) results.Add(file.Substring(start));
}
If you want all appearance of the words you need an extra loop
int fileLength = file.Length;
for(int i = 0; i < words.Length; i++)
{
int startIdx = 0;
while (startIdx < fileLength ){
int idx = file.IndexOf(words[i], startIdx]);
if (start >= 0) {
// add to results
results.Add(file.Substring(start));
// and let Word-search continue from last found Word Position Ending
startIdx = (start + words.Length);
}
}
int start = file.IndexOf(words[i], 0);
// only add to results if word is found (index >= 0)
if (start >= 0) results.Add(file.Substring(start));
}
MayBe you want a caseinsensitiv search
file.IndexOf(words[i], 0, StringComparison.CurrentCultureIgnoreCase); MSDN: StringComparer Class
I'm developing a text editor in C#, and I'm trying to make a line count.
private void updateNumberLabel()
{
Point pos = new Point(0, 0);
int firstIndex = Document.GetCharIndexFromPosition(pos);
int firstLine = Document.GetLineFromCharIndex(firstIndex);
pos.X = ClientRectangle.Width;
pos.Y = ClientRectangle.Height;
int lastIndex = Document.GetCharIndexFromPosition(pos);
int lastLine = Document.GetLineFromCharIndex(lastIndex);
int actualLine = Document.GetLineFromCharIndex(actualPos);
pos = Document.GetPositionFromCharIndex(lastIndex);
if (lastLine != actualLine)
{
numberLabel.Text = "";
for (int i = firstLine; i <= lastLine + 1; i++)
{
numberLabel.Text += i + 1 + "\n";
}
}
}
It works fine and adds the number of lines while you write them, but if you delete one, it will only update if you delete or add one more line.
I want make it instantaneous. If you delete one, the count shall be decreased instantaneously.
Maybe this is too easy, but what about that:
private void richTextBox1_TextChanged(object sender, EventArgs e)
{
var lineCount = richTextBox.Lines.Count();
numberLabel.Text = lineCount.ToString();
}
Make sure you assign it to the TextChanged event.
If this is not what you need, please add some more information what you are trying to achieve.
I'm really late, but Lines is an array of string. Just get the length.
richTexBox.Lines.Length.ToString();
I have found one open source and applied it to this problem.
I have confirmed that it works well and have implemented it.
RichTextBox Colums and Row
Simple Code(
The link has a demo source.):
this.rtb.CursorPositionChanged +=
new System.EventHandler(this.rtb_CursorPositionChanged);
this.rtb.SelectionChanged +=
new System.EventHandler(this.rtb_SelectionChanged);
.
.
.
private void rtb_CursorPositionChanged(object sender, System.EventArgs e)
{
int line = rtb.CurrentLine;
int col = rtb.CurrentColumn;
int pos = rtb.CurrentPosition;
statusBar.Text = "Line " + line + ", Col " + col +
", Position " + pos;
}
private void rtb_SelectionChanged(object sender, System.EventArgs e)
{
int start = rtb.SelectionStart;
int end = rtb.SelectionEnd;
int length = rtb.SelectionLength;
statusBar.Text = "Start " + start + ", End " + end +
", Length " + length;
}
namespace Nik.UserControls
{
public class RicherTextBox2 : System.Windows.Forms.RichTextBox
{
public event EventHandler CursorPositionChanged;
protected virtual void OnCursorPositionChanged( EventArgs e )
{
if ( CursorPositionChanged != null )
CursorPositionChanged( this, e );
}
protected override void OnSelectionChanged( EventArgs e )
{
if ( SelectionLength == 0 )
OnCursorPositionChanged( e );
else
base.OnSelectionChanged( e );
}
public int CurrentColumn
{
get { return CursorPosition.Column( this, SelectionStart ); }
}
public int CurrentLine
{
get { return CursorPosition.Line( this, SelectionStart ); }
}
public int CurrentPosition
{
get { return this.SelectionStart; }
}
public int SelectionEnd
{
get { return SelectionStart + SelectionLength; }
}
}
internal class CursorPosition
{
[System.Runtime.InteropServices.DllImport("user32")]
public static extern int GetCaretPos(ref Point lpPoint);
private static int GetCorrection(RichTextBox e, int index)
{
Point pt1 = Point.Empty;
GetCaretPos(ref pt1);
Point pt2 = e.GetPositionFromCharIndex(index);
if ( pt1 != pt2 )
return 1;
else
return 0;
}
public static int Line( RichTextBox e, int index )
{
int correction = GetCorrection( e, index );
return e.GetLineFromCharIndex( index ) - correction + 1;
}
public static int Column( RichTextBox e, int index1 )
{
int correction = GetCorrection( e, index1 );
Point p = e.GetPositionFromCharIndex( index1 - correction );
if ( p.X == 1 )
return 1;
p.X = 0;
int index2 = e.GetCharIndexFromPosition( p );
int col = index1 - index2 + 1;
return col;
}
}
}
Los799
Sorry for answering a "bit" late, I saw this question just now.
But if the problem still stands, here's a simple way to count lines, just use a foreach loop:
int CountOfLines = 1;//1 because min 1 line is always in a text of a control, that has a Text property
foreach (char c in YourText)
{
if (c == '\r' | c == '\n')//these are all equal the ENTER key
{
CountOfLines++;
}
}
You can also use foreach to count characters as well, but with foreach you can choose characters, that you don't want to be counted in the count of characters. For example:
int CountOfCharacters = 0;//0 because by default there are no characters in a textbox or label.
foreach (char c in YourText)
{
if (c != '\t' & c != '\n' & c != '\r')//in this example I want to count only the characters that are not an ENTER or a TAB.
{
CountOfCharacters++;
}
}
Hope this helps you and everybody else, who's reading this, even if it's a bit late answer. :)
Instead of trying to battle with the default rich text box, why don't you try making your own control so you have full control of the text formatting?
After all, if you're developing your own text editor, it would make sense to have text stored and managed in a way that makes sense to you, the developer, instead of trying to fight with a format designed for a slightly different purpose.
Newer solution to this question for WPF applications
DOK1 is a WPF flow document
TX1 is a TextBox control
TX1.Text = DOK1.Blocks.Count.ToString();
I'm making a calculator in GUI, and I need some help.
When I enter some data in a text box, I need to store it in an array. This is how I thought of it.
int numOfPackages;//used to get user input
private void button3_Click(object sender, EventArgs e)
{
int[] weight = new int[numOfPackages];
for(int i = 0; i < numOfPackages; i++)
{
weight[i] = Convert.ToInt32(weightBox.Text);
}
foreach (int i in weight)
totalCostLabel.Text = "" + weight[i];
}
And when I try to display the elements, it gives me the indexOutOfRange exception.
So, how do I display the elements of that array?
Thanks in advance.
This line
foreach (int i in weight)
totalCostLabel.Text = "" + weight[i];
should be
foreach (int w in weight)
totalCostLabel.Text = "" + w;
Your current code iterates the array of weights, and tries to use the weight as an index into the array of weights, causing an index out of range exception.
Another problem is with the first loop: you are setting all values of weight to the same number:
weight[i] = Convert.ToInt32(weightBox.Text); // That's the same for all i-s
If weights are to be different, they should come from different weight boxes, or the string from a single weightBox should be processed in such a way as to produce multiple numbers (for example, by using string.Split).
You have multiple problems here. First is this:
foreach (int i in weight)
totalCostLabel.Text = "" + weight[i];
This is iterating the weight array and using each value in that array. You then use that value as an index. Take the following example:
weight[0] = 0
weight[1] = 1
weight[2] = 15
In your code, the first two entries will work because there is an index of 0 and an index of 1. But when it gets to the last entry, it looks for an index of 15. You can fix this two ways, the first is to use a regular for loop:
for(int i=0; i < weight.Length; i++)
{
totalCostLabel.Text += weight[i];
}
This brings the second mistake. You aren't appending anything to your totalCostLabel in your code, you are just replacing the value. This will append all the values of weight together as one.
Another way to do this is to use the foreach loop:
foreach(int i in weight)
{
totalCostLabel.Text += i;
}
This is the same as above but you don't have to worry about indexing.
Bottom line, even after you fix your loop, you will probably need to fix the way that the label takes the text otherwise you won't get your desired result.
Maybe you wanted something more like?
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
btnAdd.Enabled = false;
}
int[] weight;
int entriesMade;
int numOfPackages;
private void btnReset_Click(object sender, EventArgs e)
{
if (int.TryParse(numEntriesBox.Text, out numOfPackages))
{
weight = new int[numOfPackages];
entriesMade = 0;
btnReset.Enabled = false;
btnAdd.Enabled = true;
totalCostLabel.Text = "";
}
else
{
MessageBox.Show("Invalid Number of Entries");
}
}
private void btnAdd_Click(object sender, EventArgs e)
{
int value;
if (int.TryParse(weightBox.Text, out value))
{
weight[entriesMade] = value;
weightBox.Clear();
totalCostLabel.Text = "";
int total = 0;
for (int i = 0; i <= entriesMade; i++)
{
total = total + weight[i];
if (i == 0)
{
totalCostLabel.Text = weight[i].ToString();
}
else
{
totalCostLabel.Text += " + " + weight[i].ToString();
}
}
totalCostLabel.Text += " = " + total.ToString();
entriesMade++;
if (entriesMade == numOfPackages)
{
btnAdd.Enabled = false;
btnReset.Enabled = true;
MessageBox.Show("Done!");
}
}
else
{
MessageBox.Show("Invalid Weight");
}
}
}
My code below here I used "SelectedLines" to find the current selected lines by user, AllLines to find the no of total lines in Multi line text box. in last for loop when i=1 it runs successfully but when i is +1 = 2 then it gives me error
Error : "Index was out of range. Must be non-negative and less than the size of the collection. Parameter name: index?"
// Retrieve selected lines
List<string> SelectedLines = Regex.Split(txtNewURLs.SelectedText, #"\r\n").ToList();
// Check for nothing, Regex.Split returns empty string when no text is inputted
if (SelectedLines.Count == 1)
{
if (String.IsNullOrWhiteSpace(SelectedLines[0]))
{
SelectedLines.Remove("");
}
}
// Retrieve all lines from textbox
List<string> AllLines = Regex.Split(txtNewURLs.Text, #"\r\n").ToList();
// Check for nothing, Regex.Split returns empty string when no text is inputted
if (AllLines.Count == 1)
{
if (String.IsNullOrWhiteSpace(AllLines[0]))
{
AllLines.Remove("");
}
}
string SelectedMessage = "The following lines have been selected";
int numSelected = 0;
// Find all selected lines
foreach (string IndividualLine in AllLines)
{
if (SelectedLines.Any(a => a.Equals(IndividualLine)))
{
SelectedMessage += "\nLine #" + AllLines.FindIndex(a => a.Equals(IndividualLine));
// changing the status of the selected lene from 0 to 1
AddURL objAddURL = new AddURL();
objAddURL.Where.SURL.Value = IndividualLine;
objAddURL.Where.ILicenseID.Value = CommonMethods.iLicenseID;
objAddURL.Query.Load();
if (objAddURL.RowCount > 0)
{
AddURL objaddurl1 = new AddURL();
objaddurl1.LoadByPrimaryKey(objAddURL.IAddURLID);
if (objaddurl1.RowCount > 0)
{
objaddurl1.IStatus = 1;
objaddurl1.Save();
}
// AllLines.Remove(IndividualLine);
}
numSelected++;
}
}
// int[] lineNo = new int[AllLines.Count];
int linesNO = SelectedLines.Count;
for (int i = 1; i <= linesNO; i++)
{
SelectedLines.RemoveAt(i);
}
// MessageBox.Show((numSelected > 0) ? SelectedMessage : "No lines selected.");
Could any body please help me to solve this issue or suggest me new code?
Indices in c# are zero based.
for (int i = 0; i < linesNO; i++)
SelectedLines.RemoveAt(i);
But if you want to delete the selected lines from your textBox your code should rather look like
List<string> SelectedLines = new List<string> { "b", "c" };
textBox1.Lines = textBox1.Lines.ToList().Except(SelectedLines).ToArray();
I have a Navigation-bar in my program that allows you to navigate the different sections in my TextBox, but the problem I have is that this doesn't work if the Text I am scrolling to is already visible on the screen.
Like in this example, if I try to jump from Section 1 to Section 3, it won't work as it's already visible.
But, in this example if I jump to Section 3, it works fine as it's not already visible.
The scrolling function I use is very simple:
if (nLine > 0 && nLine <= textBox.LineCount)
textBox.ScrollToLine(nLine - 1);
I hope that someone can shed some light on an alternative solution that allows me to scroll even if the text is already visible.
Edit: Added solution.
This is a code snippet from my project.
private static void ScrollToLineCallback(DependencyObject target, DependencyPropertyChangedEventArgs e)
{
var textBox = (TextBox)target;
int newLineValue;
if (Int32.TryParse(e.NewValue.ToString(), out newLineValue))
{
if (newLineValue > 0 && newLineValue <= textBox.LineCount) // Validate
{
textBox.ScrollToLine(newLineValue - 1); // Scroll to Line
// Check and see if we are at the line we want.
if (textBox.GetFirstVisibleLineIndex() <= newLineValue && textBox.GetLastVisibleLineIndex() >= newLineValue)
{
// If not lets move to the desired location
int newLineCorrectionValue = newLineValue - textBox.GetFirstVisibleLineIndex() - 2; // How much further do we need to scroll down?
for (int i = 0; i < newLineCorrectionValue; i++)
{
textBox.LineDown(); // Scroll down
}
}
}
}
}
You could use GetCharacterIndexFromLineIndex to get the index of the beginning of the desired line and then set the CaretIndex to that value.
Because I don't really know, what you are trying to achieve, another possibility is to use LineUp and LineDown in conjunction with GetFirstVisibleLineIndex and GetLastVisibleLineIndex.
private void TextBoxGotoLine(
TextBox textbox1,
int linenum)
{
var target_cpos = textbox1.GetCharacterIndexFromLineIndex(linenum);
var target_char_rect = textbox1.GetRectFromCharacterIndex(target_cpos);
var first_char_rect = textbox1.GetRectFromCharacterIndex(0);
textbox1.ScrollToVerticalOffset(
target_char_rect.Top -
first_char_rect.Top
);
}
I found out if Wrapping is enabled its more complications:
private void TextBoxGotoLine(TextBox textbox1, int linenum)
{
// int Linenum is the Absolute Line, not including
// effect of Textbox Wrapping.
if (textbox1.TextWrapping == TextWrapping.Wrap)
{
// If textbox Wrapping is on, we need to
// Correct the Linenum for Wrapping which adds extra lines
int cidx = 0;
bool found = false;
int ln = 0;
char[] tmp = textbox1.Text.ToCharArray();
for (cidx = 0; cidx < tmp.Length; cidx++)
{
if (tmp[cidx] == '\n')
{
ln++;
}
if (ln == linenum)
{
found = true;
break;
}
}
if (!found)
return;
linenum = textbox1.GetLineIndexFromCharacterIndex(cidx+1);
}
// Non-Wrapping TextBox
var target_cpos = textbox1.GetCharacterIndexFromLineIndex(linenum);
var target_char_rect = textbox1.GetRectFromCharacterIndex(target_cpos);
var first_char_rect = textbox1.GetRectFromCharacterIndex(0);
textbox1.ScrollToVerticalOffset(target_char_rect.Top - first_char_rect.Top);
}