displaying line number in rich text box c# - c#

I have a Multiline richtextbox control into which i want to integrate the feature of adding a line number. i have considered many approaches
Add a label and updating the line numbers as the line count changes
Add a picturebox along with to draw string on it.
Add another textbox along with and show line numbers on it
Add listbox along and display line numbers in it.
I got two doubts.
The richtextbox which i'm using is a custom made control and derieves from RichTextBox class. How can i add multiple controls to it.
What is the best approach to show line numbers for the multiline text in c#

My own example. All is fine, but wordwrap must be disabled :(
int maxLC = 1; //maxLineCount - should be public
private void rTB_KeyUp(object sender, KeyEventArgs e)
{
int linecount = rTB.GetLineFromCharIndex( rTB.TextLength ) + 1;
if (linecount != maxLC)
{
tB_line.Clear();
for (int i = 1; i < linecount+1; i++)
{
tB_line.AppendText(Convert.ToString(i) + "\n");
}
maxLC = linecount;
}
}
where rTB is my richtextbox and tB is textBox next to rTB
J.T. jr

this code helped me thank you, needed to convert visual basic but could:
Private Sub TextBox1_KeyUp(ByVal sender As Object, ByVal e As System.Windows.Forms.KeyEventArgs) Handles TextBox1.KeyUp
Dim maxlc As Integer = 1
Dim linecount As Integer = TextBox1.GetLineFromCharIndex(TextBox1.Height) + 1
If linecount <> maxlc Then
TextBox2.Clear()
For i = 0 To linecount - 1 Step 1
TextBox2.AppendText(Convert.ToString(i) + vbNewLine)
Next i
maxlc = linecount
End If
End Sub

public int getWidth()
{
int w = 25;
// get total lines of richTextBox1
int line = richTextBox1.Lines.Length;
if (line <= 99)
{
w = 20 + (int)richTextBox1.Font.Size;
}
else if (line <= 999)
{
w = 30 + (int)richTextBox1.Font.Size;
}
else
{
w = 50 + (int)richTextBox1.Font.Size;
}
return w;
}
public void AddLineNumbers()
{
// create & set Point pt to (0,0)
Point pt = new Point(0, 0);
// get First Index & First Line from richTextBox1
int First_Index = richTextBox1.GetCharIndexFromPosition(pt);
int First_Line = richTextBox1.GetLineFromCharIndex(First_Index);
// set X & Y coordinates of Point pt to ClientRectangle Width & Height respectively
pt.X = ClientRectangle.Width;
pt.Y = ClientRectangle.Height;
// get Last Index & Last Line from richTextBox1
int Last_Index = richTextBox1.GetCharIndexFromPosition(pt);
int Last_Line = richTextBox1.GetLineFromCharIndex(Last_Index);
// set Center alignment to LineNumberTextBox
LineNumberTextBox.SelectionAlignment = HorizontalAlignment.Center;
// set LineNumberTextBox text to null & width to getWidth() function value
LineNumberTextBox.Text = "";
LineNumberTextBox.Width = getWidth();
// now add each line number to LineNumberTextBox upto last line
for (int i = First_Line; i <= Last_Line + 2; i++)
{
LineNumberTextBox.Text += i + 1 + "\n";
}
}
private void Form1_Load(object sender, EventArgs e)
{
LineNumberTextBox.Font = richTextBox1.Font;
richTextBox1.Select();
AddLineNumbers();
}
private void richTextBox1_SelectionChanged(object sender, EventArgs e)
{
Point pt = richTextBox1.GetPositionFromCharIndex(richTextBox1.SelectionStart);
if (pt.X == 1)
{
AddLineNumbers();
}
}
private void richTextBox1_VScroll(object sender, EventArgs e)
{
LineNumberTextBox.Text = "";
AddLineNumbers();
LineNumberTextBox.Invalidate();
}
private void richTextBox1_TextChanged(object sender, EventArgs e)
{
if (richTextBox1.Text == "")
{
AddLineNumbers();
}
}
private void richTextBox1_FontChanged(object sender, EventArgs e)
{
LineNumberTextBox.Font = richTextBox1.Font;
richTextBox1.Select();
AddLineNumbers();
}
private void LineNumberTextBox_MouseDown(object sender, MouseEventArgs e)
{
richTextBox1.Select();
LineNumberTextBox.DeselectAll();
}
private void Form1_Resize(object sender, EventArgs e)
{
AddLineNumbers();
}

WORKS 100%!!! But you need to add richTextBox2 for line numbers, if you want change it to other
form like listbox, anyway it served me well.
private void richTextBox1_keyDown(object sender, KeyEventArgs e)
{
for (int i = 0; i <= richTextBox1.Lines.Count(); i++)
{
if (!(e.KeyCode == Keys.Back))
{
if (!richTextBox2.Text.Contains(i.ToString()))
{
richTextBox2.Text += i.ToString() + "\n";
}
}
else
{
richTextBox2.Clear();
}
}
}

Related

how to change color of equal signs in richtextbox c#

I want to change color of equal sign as it happens in notepad++ while the user writes the text . My code is working but the cursor is stuck on one place and user cannot write anything in between the text it only allows to write it in the end , also does not detect when newline is present after =. How to do it ?
private void richTextBox1_KeyPress(object sender, KeyPressEventArgs e)
{
equal();
}
public void equal()
{
start = richTextBox1.Text.Length - 1;
length = 1;
richTextBox1.SelectionStart = start;
richTextBox1.SelectionLength = length;
string settext = richTextBox1.SelectedText;
if ( settext ==Convert.ToString('='))
{
richTextBox1.SelectionColor = Color.Purple;
}
}
Add event to your richtext box for text changed:
private void richTextBox1_TextChanged(object sender, EventArgs e)
{
this.ChangeColor("=", Color.Purple);
}
private void ChangeColor(string word, Color color)
{
if (this.richTextBox1.Text.Contains(word))
{
int index = -1;
int selectStart = this.richTextBox1.SelectionStart;
while ((index = this.richTextBox1.Text.IndexOf(word, (index + 1))) != -1)
{
this.richTextBox1.Select((index), word.Length);
this.richTextBox1.SelectionColor = color;
this.richTextBox1.Select(selectStart, 0);
this.richTextBox1.SelectionColor = Color.Black;
}
}
}
Please use the richTextBox_TextChanged event for changing the color. I have met this problem once in my application.

How to use array of images with pictureBox and hscrollbar to scroll between the images?

In Form1 I have in the designer a pictureBox.
Then I added a timer in the tick event i did:
private void timer1_Tick(object sender, EventArgs e) {
if (savedall == true) {
for (int i = 0; i < filePaths.Length; i++) {
}
}
}
And the hscrollbar scroll event:
private void hScrollBar1_Scroll(object sender, ScrollEventArgs e) {
}
When the savedall is true then I want to be able to scroll between all the images in filePaths and display each image in the pictureBox.
You can prepare the list of images and a scrollbar imgScroller like this:
List<string> imageList = new List<string>();
imageList = Directory.GetFiles("d:\\", "*.jpg").ToList();
imgScroller.Minimum = 0;
imgScroller.Value = 0;
imgScroller.Maximum = imageList.Count - 1;
imgScroller.SmallChange = 1;
imgScroller.LargeChange = 1;
And the code the Scroll event like this:
private void imgScroller_Scroll(object sender, ScrollEventArgs e)
{
if (scrolledPB.Image != null) scrolledPB.Image.Dispose();
int index = (int)imgScroller.Value;
scrolledPB.Image = Image.FromFile(imageList[index]);
someLabel.Text = "Displaying image " + index + " of " + imageList.Count - 1;
}
Not sure what the timer is doing or if you already have the file paths..

Creating a game of Checkers

I am creating this in regards to an issue I just ran in with. I am trying to create a primitive game of checkers using only buttons and so far I am just testing how to get the program to recognize selecting a button and moving the piece.
My Code:
private void Checkers_Load(object sender, EventArgs e)
{
}
string selectedChecker = "";
string currentButton = "";
int blankSpace = 0;
int[] Board = new int[64];
private void gameBoard()
{
foreach(var control in Controls)
{
var button = control as Button;
if(button != null)
{
if(button.Name == currentButton)
{
button.Image = System.Drawing.Image.FromFile("Red Checker.png");
}
else
{
MessageBox.Show("Dead.");
}
}
else
{
MessageBox.Show("Dead.");
}
}
}
private void attemptMove()
{
string substringChecker = null;
substringChecker = selectedChecker.Substring(0,2);
int selectedCheckerNumber = Convert.ToInt32(substringChecker);
string substringButton = null;
substringButton = currentButton.Substring(0,2);
int currentButtonNumber = Convert.ToInt32(substringButton);
if((selectedCheckerNumber + 3 == currentButtonNumber) || (selectedCheckerNumber + 4 == currentButtonNumber))
{
Board[currentButtonNumber] = Board[selectedCheckerNumber];
Board[selectedCheckerNumber] = blankSpace;
gameBoard();
}
}
private void newGameToolStripMenuItem_Click(object sender, EventArgs e)
{
int w = pictureBox1.Size.Width;
int h = pictureBox1.Size.Height;
int count = 8;
Bitmap b = new Bitmap(w, h, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
w /= count;
h /= count;
Graphics g = Graphics.FromImage(b);
for (int i = 0; i < count; i++)
{
for (int j = 0; j < count; j++)
{
Color c = (i + j) % 2 == 0 ? Color.Red : Color.Black;
Brush br = new SolidBrush(c);
g.FillRectangle(br, i * w, j * h, w, h);
br.Dispose();
}
}
g.Dispose();
pictureBox1.Image = b;
pictureBox1.Refresh();
}
private void exitToolStripMenuItem_Click(object sender, EventArgs e)
{
//Close form
this.Close();
}
private void howToPlayToolStripMenuItem_Click(object sender, EventArgs e)
{
//MessageBox.Show("How to play a game of Checkers: Step 1 - Don't play.");
}
private void informationToolStripMenuItem_Click(object sender, EventArgs e)
{
}
private void checkerSpace18_Click(object sender, EventArgs e)
{
}
private void checkerSpace17_Click(object sender, EventArgs e)
{
Button btn = (Button)sender;
string buttonClicked = btn.Name;
if (selectedChecker == "")
{
selectedChecker = buttonClicked;
}
else
{
currentButton = buttonClicked;
attemptMove();
}
}
}
My question goes with regards to my attemptMove() method. I run into a runtime error:
"An unhandled exception of type 'System.FormatException' occurred in mscorlib.dll. Additional information: Input string was not in a correct format."
It seems to occur whenever I go to move the piece to the other cell adjacent to the piece after moving it once. It may be because my program is not updating the pictures of the buttons when the buttons switch, but I wanted to see if any guidance could be shed upon this issue.
You are setting the "selectedChecker" to the clicked button .Name property. Then you are using substring(0,2) to get the first 2 characters and try to convert that to an integer.
Since a Name property can't start with numbers, you are attempting to convert some random text to an integer property.
A better approach would be to use the Tag property to hold information about the number, this can be any value. You can then use the int.Parse to parse the Tag value of the button.
What is your naming convention for the buttons? Seems like you're trying to get a number from the name, but the conversion is failing.
If the button names are button00 through button63, then you should be able to get the number using Substring, starting at Name.Length - 2. Note that you must have all button names use a two digit number at the end (00, 01, 02, etc).
For example:
// Get the last two characters of the button name
string substringChecker = selectedChecker.Substring(selectedChecker.Length - 2);
// Convert the characters to an integer
int selectedCheckerNumber = Convert.ToInt32(substringChecker);

code improvement - naming textboxes generated form numericupdown & button click

I have a problem with a code that is regarding adding controls with numericUpDown ( for example- if numericUpDown value equals to 3, user recievs 3 textboxes).
Thanks to stackoverflow users I improved my code.
Before improvement it looked like this:
private void button1_Click(object sender, EventArgs e)
{
if (numericUpDown1.Value == 1)
{
txtbx1.AutoSize = true;
Controls.Add(txtbx1);
txtbx1.Location = new Point(70, 100);
}
else if (numericUpDown1.Value == 2)
{
txtbx1.AutoSize = true;
Controls.Add(txtbx1);
txtbx1.Location = new Point(70, 100);
txtbx2.AutoSize = true;
Controls.Add(txtbx2);
txtbx2.Location = new Point(70, 130);
}
else if (numericUpDown1.Value == 3)
{
txtbx1.AutoSize = true;
Controls.Add(txtbx1);
txtbx1.Location = new Point(70, 100);
txtbx2.AutoSize = true;
Controls.Add(txtbx2);
txtbx2.Location = new Point(70, 130);
txtx3.AutoSize = true;
Controls.Add(txtbx3);
txtbx3.Location = new Point(70, 160);
}
}
After improvement:
private void button1_Click(object sender, EventArgs e)
{
int y = 100;
int x = 70;
for (int i = 0; i < numericUpDown1.Value; i++)
{
var txtbx = new TextBox();
txtbx.AutoSize = true;
Controls.Add(txtbx);
txtbx.Location = new Point(x, y);
// Increase the y-position for next textbox.
y += 30;
}
}
Now the problem is that I don't know how assign names to genarated textboxes.
(before the improvement I could name them - txtbx1, txtbx2, txtbx3...)
Code to improve:
private void button3_Click(object sender, EventArgs e)
{
try
{
double a, b, c, sum;
a = double.Parse(txtbx1.Text);
b = double.Parse(txtbx2.Text);
c = double.Parse(txtbx3.Text);
sum = a + b + c;
label1.Text = sum.ToString();
}
catch (Exception ex)
{
MessageBox.Show("error");
}
Please note that I'm a beginner, learning c# by watching youtube tutorials ;) I do realize that my question might be silly but I couldn't handle this problem by myself.
In advance thank you for your time and help.
If you need to access them afterwards, you have some options.
I'll guess that your objective is to set label1's text to the sum of the values contained in the specified textbox(es).
On the ValueChanged event of your NumericUpDown, check the delta and consequentely add or remove the required number of TextBoxes to your Form's Controls. To obtain the delta, you'll need to store the previous value of the NumericUpDown, and then subtract it from the current value. (If it was 5, and now it's 4, 4 - 5 = -1. A textbox has been removed).
private int _oldNUDvalue = 0; //or assign it to the default value
private void numericUpDown1_ValueChanged(object sender, EventArgs e)
{
int delta = (int)numericUpDown1.Value - _oldNUDvalue;
if (delta < 0)
{
for (int i = -delta; i > 0; i--)
{
var tbox = Controls.Find("ntextBox" + (_oldNUDvalue - i), false)[0];
Controls.Remove(tbox);
}
}
else if (delta > 0)
{
for (int i = _oldNUDvalue; i < _oldNUDvalue + delta; i++)
{
var tbox = new TextBox();
tbox.Location = new Point(15, 55 + (30 * i));
tbox.Name = "ntextBox" + i;
Controls.Add(tbox);
}
}
_oldNUDvalue = (int)numericUpDown1.Value;
}
If, however, you only have a maximum number of 3, you could take a slightly different approach. My solution works with n-textboxes.
Finally, to get the TextBoxes' values from code, you have three approaches:
Loop through your Form's controls, check for TextBoxes with their name starting with "ntextBox", and add their values together
Use LINQ to do the same
Access them singularly via "Controls.Find("ntextBoxX", false)", where X is the number of course.
I'll show the LINQ approach as I like it better.
int sum = Controls.Cast<Control>().Sum(c => c.Name.StartsWith("ntextBox") ? int.Parse(c.Text) : 0);
I haven't tested the code, but it should work. Tell me if there are any problems.
EDIT: Tested and it works. For the sake of completeness, I'll add some event logic to the TextBox-adding loop, to make sure that their input is actually numeric.
tbox.TextChanged += HandleNTextBoxInput; // add this line
And elsewhere, add this method:
void HandleNTextBoxInput(object sender, EventArgs e)
{
string text = ((TextBox)sender).Text;
if (!Regex.IsMatch(text, "^[1-9][0-9]*$")) //Text is NOT numeric. Remove [1-9] if you want to keep leading zeros.
{
//Set the Text to 0, for example. Or show a message box. Or whatever.
((TextBox)sender).Text = "0";
}
}
As I mentioned in a comment- this code seems to be maybe too advanced for me.
I have no problem with adding the controls, bu still there is a problem how to get the sum from a button click to a textbox.
I probably made some simple mistakes, or something is missing but I really don't know how to fix this problem.
My code:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Text.RegularExpressions;
namespace testprogram
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e) // I'm guessing something is missing over here
{
int sum = Controls.Cast<Control>().Sum(c => c.Name.StartsWith("ntextBox") ? int.Parse(c.Text) : 0);
}
void HandleNTextBoxInput(object sender, EventArgs e)
{
string text = ((TextBox)sender).Text;
if (!Regex.IsMatch(text, "^[1-9][0-9]*$")) //Text is NOT numeric. Remove [1-9] if you want to keep leading zeros.
{
//Set the Text to 0, for example. Or show a message box. Or whatever.
((TextBox)sender).Text = "0";
}
}
private int _oldNUDvalue = 0; //or assign it to the default value
private void numericUpDown1_ValueChanged(object sender, EventArgs e)
{
{
int delta = (int)numericUpDown1.Value - _oldNUDvalue;
if (delta < 0)
{
for (int i = -delta; i > 0; i--)
{
var tbox = Controls.Find("ntextBox" + (_oldNUDvalue - i), false)[0];
Controls.Remove(tbox);
}
}
else if (delta > 0)
{
for (int i = _oldNUDvalue; i < _oldNUDvalue + delta; i++)
{
var tbox = new TextBox();
tbox.Location = new Point(15, 55 + (30 * i));
tbox.Name = "ntextBox" + i;
tbox.TextChanged += HandleNTextBoxInput;
Controls.Add(tbox);
}
}
_oldNUDvalue = (int)numericUpDown1.Value;
}
}
}
}

How do I determine the cell being clicked on in a TableLayoutPanel?

I have a TableLayoutPanel and I want to add a control to the cell that I click on.
The problem is that I can't determine the cell that I click on at run time.
How do I determine the cell being clicked on?
You can use GetColumnWidths and GetRowHeights methods to calculate the cell row and column index:
Point? GetRowColIndex(TableLayoutPanel tlp, Point point)
{
if (point.X > tlp.Width || point.Y > tlp.Height)
return null;
int w = tlp.Width;
int h = tlp.Height;
int[] widths = tlp.GetColumnWidths();
int i;
for (i = widths.Length - 1; i >= 0 && point.X < w; i--)
w -= widths[i];
int col = i + 1;
int[] heights = tlp.GetRowHeights();
for (i = heights.Length - 1; i >= 0 && point.Y < h; i--)
h -= heights[i];
int row = i + 1;
return new Point(col, row);
}
Usage:
private void tableLayoutPanel1_Click(object sender, EventArgs e)
{
var cellPos = GetRowColIndex(
tableLayoutPanel1,
tableLayoutPanel1.PointToClient(Cursor.Position));
}
But notice that the click event only is raised if the cell does not already contain a control.
This worked for me:
public TableLayoutPanel tableLayoutPanel { get; set; }
private void Form_Load(object sender, EventArgs e)
{
foreach (Panel space in this.tableLayoutPanel.Controls)
{
space.MouseClick += new MouseEventHandler(clickOnSpace);
}
}
public void clickOnSpace(object sender, MouseEventArgs e)
{
MessageBox.Show("Cell chosen: (" +
tableLayoutPanel.GetRow((Panel)sender) + ", " +
tableLayoutPanel.GetColumn((Panel)sender) + ")");
}
Note that my tableLayoutPanel is declared globally so that I can just use it without having to pass it to each function. Also, both the tableLayoutPanel and each Panel within it are created completely programatically elsewhere (my form [design] is completely blank).
My answer is based on #Mohammad Dehghan's answer above but has a couple of advantages:
It now takes into account vertical scrolling
The columns are in the correct order (starts at i=0 instead of i=length), meaning columns of different widths or heights are processed in the correct order
Here is the updated version of the code:
public Point? GetIndex(TableLayoutPanel tlp, Point point)
{
// Method adapted from: stackoverflow.com/a/15449969
if (point.X > tlp.Width || point.Y > tlp.Height)
return null;
int w = 0, h = 0;
int[] widths = tlp.GetColumnWidths(), heights = tlp.GetRowHeights();
int i;
for (i = 0; i < widths.Length && point.X > w; i++)
{
w += widths[i];
}
int col = i - 1;
for (i = 0; i < heights.Length && point.Y + tlp.VerticalScroll.Value > h; i++)
{
h += heights[i];
}
int row = i - 1;
return new Point(col, row);
}
Nick's answer was the best solution, except that it can be made generic for TableLayoutPanels that contain different kinds of controls in the cells. Just change the explicit "Panel" type to "Control":
public TableLayoutPanel tableLayoutPanel { get; set; }
private void Form_Load(object sender, EventArgs e)
{
foreach (Control c in this.tableLayoutPanel.Controls)
{
c.MouseClick += new MouseEventHandler(ClickOnTableLayoutPanel);
}
}
public void ClickOnTableLayoutPanel(object sender, MouseEventArgs e)
{
MessageBox.Show("Cell chosen: (" +
tableLayoutPanel.GetRow((Control)sender) + ", " +
tableLayoutPanel.GetColumn((Control)sender) + ")");
}
This works great and doesn't require doing coordinate math to find which cell was clicked.

Categories

Resources