nullReferenceException when using RichTextBox.SelectionFont - c#

when the selection area is combined with 2 areas that has different fonts, it returns null. The code is below:
private Font myGetSelectionFont()
{
Font t = this.workspace.SelectionFont;
if (t == null)
return defaultFont;
else return t;
}
private void toolStripComboBox_size_SelectedIndexChanged(object sender, EventArgs e)
{
string sizeString = ((ToolStripComboBox)sender).SelectedItem.ToString();
float curSize = float.Parse(sizeString);
Font oldFont = myGetSelectionFont();
Font newFont = this.getFont(oldFont.FontFamily.Name, curSize, oldFont.Style);
this.setFontIcons(newFont);
this.workspace.SelectionFont = newFont;
this.workspace.Focus();
}
There is no problem to set a different font for the selected area, but i don't know the font size and other properties for the selected area. what should i do?
I think I can choose to set the font as the font before the selected area, or behind the selected area, but i don't know how to do it.

A possible solution would be to loop
private void toolStripComboBox_size_SelectedIndexChanged(object sender, EventArgs e)
{
float size = float.Parse(((ToolStripComboBox)sender).SelectedItem.ToString());
SetFontSize(richTextBox1, size);
}
private void SetFontSize(RichTextBox rtb, float size)
{
int selectionStart = rtb.SelectionStart;
int selectionLength = rtb.SelectionLength;
int selectionEnd = selectionStart + selectionLength;
for (int x = selectionStart; x < selectionEnd; ++x)
{
// Set temporary selection
rtb.Select(x, 1);
// Toggle font style of the selection
rtb.SelectionFont = new Font(rtb.SelectionFont.FontFamily, size, rtb.SelectionFont.Style);
}
// Restore the original selection
rtb.Select(selectionStart, selectionLength);
}

Related

How to resize/stretch a text box based on another text box content?

I would like to resize my text box based on another text box content.
This is what I've tried to do.
private void button1_Click(object sender, EventArgs e)
{
receive.AutoSize = true;
receive.Text = send.Text;
}
I want the text box to auto stretch or to resize on X and Y based on another text box content.
Ok, So I found something like this.
private void button1_Click(object sender, EventArgs e)
{
receive.Text = send.Text;
Size sz = new Size(receive.ClientSize.Width, int.MaxValue);
TextFormatFlags flags = TextFormatFlags.WordBreak;
int padding = 3;
int borders = receive.Height - receive.ClientSize.Height;
sz = TextRenderer.MeasureText(receive.Text, receive.Font, sz, flags);
int h = sz.Height + borders + padding;
if (receive.Top + h > this.ClientSize.Height - 10)
{
h = this.ClientSize.Height - 10 - receive.Top;
}
receive.Height = h;
}
But I need to set Maximum Height and Width and minimum. How should I do it?
private void sender_TextChanged/*or button1_Click*/(object sender, EventArgs e)
{
Graphics graph = CreateGraphics();
SizeF measuredStringSize = graph.MeasureString(tb_sender.Text, tb_sender.Font);
tb_receiver.Width = (int)measuredStringSize.Width;
tb_receiver.Text = tb_sender.Text;
}

How to resize label control

I'm trying to allow user to resize a label (the control, not the font) before printing it.
For this I'm using a LabelPrint class and I've set AutoSize property to false. I already made a code that would adjust font size to my label size. But I can't change the label size… Thanks for help, here's the code.
class LabelPrint : Label
{
public LabelPrint()
{
this.ResizeRedraw = true;
this.AutoSize = false;
this.TextAlign = ContentAlignment.MiddleCenter;
this.Font = new Font(this.Font.FontFamily, 12);
this.SizeChanged += new EventHandler(this.SizeLabelFont);
}
protected override void WndProc(ref Message m)
{
base.WndProc(ref m);
if (m.Msg == 0x84)
{ // Trap WM_NCHITTEST
var pos = this.PointToClient(new Point(m.LParam.ToInt32()));
if (pos.X >= this.Size.Width - grab && pos.Y >= this.Size.Height - grab)
m.Result = new IntPtr(17); // HT_BOTTOMRIGHT
}
}
private const int grab = 16;
public void SizeLabelFont(object sender, EventArgs e)
{
// Only bother if there's text.
string txt = this.Text;
if (txt.Length > 0)
{
int best_size = 100;
// See how much room we have, allowing a bit
// for the Label's internal margin.
int wid = this.DisplayRectangle.Width - 3;
int hgt = this.DisplayRectangle.Height - 3;
// Make a Graphics object to measure the text.
using (Graphics gr = this.CreateGraphics())
{
for (int i = 1; i <= 100; i++)
{
using (Font test_font =
new Font(this.Font.FontFamily, i))
{
// See how much space the text would
// need, specifying a maximum width.
SizeF text_size =
gr.MeasureString(txt, test_font);
if ((text_size.Width > wid) ||
(text_size.Height > hgt))
{
best_size = i - 1;
break;
}
}
}
}
// Use that font size.
this.Font = new Font(this.Font.FontFamily, best_size);
}
}
}
I tried to add this in LabelPrint class:
protected override void OnLayout(LayoutEventArgs levent)
{
base.OnLayout(levent);
base.AutoSize = false;
}
But I still get the same problem: AutoSize is set to false so when i increase the fontsize, i can't see all my label.Text but I can't resize the label itself… I mean with the mouse and ResizeRedraw().

how can I split a panel to clickable segments in c# winform?

I am trying to simulate a LED display board with c# . I need a control which contains 1536 clickable controls to simulate LEDs (96 in width and 16 in Height). I used a panel named pnlContainer for this and user will add 1536 tiny customized panels at runtime. These customized panels should change their color by click event at runtime. Everything works . But adding this number of tiny panels to the container takes long time ( about 10 secs). What is your suggestion to solve this issue? Any tips are appreciated.
this is my custome panel:
public partial class LedPanel : Panel
{
public LedPanel()
{
InitializeComponent();
}
protected override void OnPaint(PaintEventArgs pe)
{
base.OnPaint(pe);
}
protected override void OnMouseDown(MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
if (this.BackColor == Color.Black)
{
this.BackColor = Color.Red;
}
else
{
this.BackColor = Color.Black;
}
}
}
}
and this is piece of code which adds tiny panels to the pnlContainer :
private void getPixels(Bitmap img2)
{
pnlContainer.Controls.Clear();
for (int i = 0; i < 96; i++)
{
for (int j = 0; j < 16; j++)
{
Custom_Controls.LedPanel led = new Custom_Controls.LedPanel();
led.Name = i.ToString() + j.ToString();
int lWidth = (int)(pnlContainer.Width / 96);
led.Left = i * lWidth;
led.Top = j * lWidth;
led.Width = led.Height = lWidth;
if (img2.GetPixel(i, j).R>numClear.Value)
{
led.BackColor = Color.Red;
}
else
{
led.BackColor = Color.Black;
}
led.BorderStyle = BorderStyle.FixedSingle;
pnlContainer.Controls.Add(led);
}
}
}
Is there any better approach or better control instead of panelto do this?
I agree with what #TaW recommends. Don't put 1000+ controls on a form. Use some sort of data structure, like an array to keep track of which LEDs need to be lit and then draw them in the Paint event of a Panel.
Here's an example. Put a Panel on a form and name it ledPanel. Then use code similar to the following. I just randomly set the values of the boolean array. You would need to set them appropriately in response to a click of the mouse. I didn't include that code, but basically you need to take the location of the mouse click, determine which array entry needs to be set (or unset) and then invalidate the panel so it will redraw itself.
public partial class Form1 : Form
{
//set these variables appropriately
int matrixWidth = 96;
int matrixHeight = 16;
//An array to hold which LEDs must be lit
bool[,] ledMatrix = null;
//Used to randomly populate the LED array
Random rnd = new Random();
public Form1()
{
InitializeComponent();
ledPanel.BackColor = Color.Black;
ledPanel.Resize += LedPanel_Resize;
//clear the array by initializing a new one
ledMatrix = new bool[matrixWidth, matrixHeight];
//Force the panel to repaint itself
ledPanel.Invalidate();
}
private void LedPanel_Resize(object sender, EventArgs e)
{
//If the panel resizes, then repaint.
ledPanel.Invalidate();
}
private void button1_Click(object sender, EventArgs e)
{
//clear the array by initializing a new one
ledMatrix = new bool[matrixWidth, matrixHeight];
//Randomly set 250 of the 'LEDs';
for (int i = 0; i < 250; i++)
{
ledMatrix[rnd.Next(0, matrixWidth), rnd.Next(0, matrixHeight)] = true;
}
//Make the panel repaint itself
ledPanel.Invalidate();
}
private void ledPanel_Paint(object sender, PaintEventArgs e)
{
//Calculate the width and height of each LED based on the panel width
//and height and allowing for a line between each LED
int cellWidth = (ledPanel.Width - 1) / (matrixWidth + 1);
int cellHeight = (ledPanel.Height - 1) / (matrixHeight + 1);
//Loop through the boolean array and draw a filled rectangle
//for each one that is set to true
for (int i = 0; i < matrixWidth; i++)
{
for (int j = 0; j < matrixHeight; j++)
{
if (ledMatrix != null)
{
//I created a custom brush here for the 'off' LEDs because none
//of the built in colors were dark enough for me. I created it
//in a using block because custom brushes need to be disposed.
using (var b = new SolidBrush(Color.FromArgb(64, 0, 0)))
{
//Determine which brush to use depending on if the LED is lit
Brush ledBrush = ledMatrix[i, j] ? Brushes.Red : b;
//Calculate the top left corner of the rectangle to draw
var x = (i * (cellWidth + 1)) + 1;
var y = (j * (cellHeight + 1) + 1);
//Draw a filled rectangle
e.Graphics.FillRectangle(ledBrush, x, y, cellWidth, cellHeight);
}
}
}
}
}
private void ledPanel_MouseUp(object sender, MouseEventArgs e)
{
//Get the cell width and height
int cellWidth = (ledPanel.Width - 1) / (matrixWidth + 1);
int cellHeight = (ledPanel.Height - 1) / (matrixHeight + 1);
//Calculate which LED needs to be turned on or off
int x = e.Location.X / (cellWidth + 1);
int y = e.Location.Y / (cellHeight + 1);
//Toggle that LED. If it's off, then turn it on and if it's on,
//turn it off
ledMatrix[x, y] = !ledMatrix[x, y];
//Force the panel to update itself.
ledPanel.Invalidate();
}
}
I'm sure there can be many improvements to this code, but it should give you an idea on how to do it.
#Chris and #user10112654 are right.
here is a code similar to #Chris but isolates the displaying logic in a separate class. (#Chris answered your question when I was writing the code :))))
just create a 2D array to initialize the class and pass it to the Initialize method.
public class LedDisplayer
{
public LedDisplayer(Control control)
{
_control = control;
_control.MouseDown += MouseDown;
_control.Paint += Control_Paint;
// width and height of your tiny boxes
_width = 5;
_height = 5;
// margin between tiny boxes
_margin = 1;
}
private readonly Control _control;
private readonly int _width;
private readonly int _height;
private readonly int _margin;
private bool[,] _values;
// call this method first of all to initialize the Displayer
public void Initialize(bool[,] values)
{
_values = values;
_control.Invalidate();
}
private void MouseDown(object sender, MouseEventArgs e)
{
var firstIndex = e.X / OuterWidth();
var secondIndex = e.Y / OuterHeight();
_values[firstIndex, secondIndex] = !_values[firstIndex, secondIndex];
_control.Invalidate(); // you can use other overloads of Invalidate method for the blink problem
}
private void Control_Paint(object sender, PaintEventArgs e)
{
if (_values == null)
return;
e.Graphics.Clear(_control.BackColor);
for (int i = 0; i < _values.GetLength(0); i++)
for (int j = 0; j < _values.GetLength(1); j++)
Rectangle(i, j).Paint(e.Graphics);
}
private RectangleInfo Rectangle(int firstIndex, int secondIndex)
{
var x = firstIndex * OuterWidth();
var y = secondIndex * OuterHeight();
var rectangle = new Rectangle(x, y, _width, _height);
if (_values[firstIndex, secondIndex])
return new RectangleInfo(rectangle, Brushes.Red);
return new RectangleInfo(rectangle, Brushes.Black);
}
private int OuterWidth()
{
return _width + _margin;
}
private int OuterHeight()
{
return _height + _margin;
}
}
public class RectangleInfo
{
public RectangleInfo(Rectangle rectangle, Brush brush)
{
Rectangle = rectangle;
Brush = brush;
}
public Rectangle Rectangle { get; }
public Brush Brush { get; }
public void Paint(Graphics graphics)
{
graphics.FillRectangle(Brush, Rectangle);
}
}
this is how it's used in the form:
private void button2_Click(object sender, EventArgs e)
{
// define the displayer class
var displayer = new LedDisplayer(panel1);
// define the array to initilize the displayer
var display = new bool[,]
{
{true, false, false, true },
{false, true, false, false },
{false, false, true, false },
{true, false, false, false }
};
// and finally
displayer.Initialize(display);
}

OnFontChange method

I have my control MyLabel and when I change the Font Size must perform this code in the constructor. How to make this code worked?
protected override void OnFontChanged(EventArgs e)
{
if (AutoSize_)
{
this.AutoSize = true;
remember_size = this.Size;
this.AutoSize = false;
this.Size = new Size(remember_size.Width, remember_size.Height);
remember_size = this.Size;
}
...
this.Invalidate();
}
But don't work. For example this code work:
protected override void OnFontChanged(EventArgs e)
{
if (AutoSize_)
{
this.AutoSize = true;
}
...
this.Invalidate();
}
If your goal is to resize the label so the text is visible, regardless of the font size, the AutoSize property will do this for you.. However if you for some reason wish to handle this with your own code, you could try setting the AutoSize property to false (and not change it..)
The following code method can be called from any Form, UserControl, or Control. It will return the size of the specified text in the specified font.
public static Size MeasureText(Graphics graphicsDevice, String text, Font font)
{
System.Drawing.SizeF textSize = graphicsDevice.MeasureString(text, font);
int width = (int)Math.Ceiling(textSize.Width);
int heigth = (int)Math.Ceiling(textSize.Height);
Size size = new Size(width, heigth);
return size;
}
Now you will need to check that the label has not outgrown the parent container, which would cause some of the labels text to be cut off. Something like the following will accomplish this:
private void ResizeParentAccordingToLabelSize(Label resizedLabel)
{
int necessaryWidth = resizedLabel.Location.X + resizedLabel.Width;
int necessaryHeight = resizedLabel.Location.Y + resizedLabel.Height;
if (necessaryWidth > this.Width)
{
this.Width = necessaryWidth;
}
if (necessaryHeight > this.Height)
{
this.Height = necessaryHeight;
}
}

Change font color as the user types in RichTextBox

I want to create a simple text editor but supports multicolor font like a "Compiler"
Assume my program keywords are: "dog","cow","cat","bird"
I have a RichTextBox that implements TextChanged event.
now, My problem is I don't know how to change the font color when a keyword is encountered.
Example String: A Big Dog and a Cat
Dog will be color RED while cat will be color GREEN.
I'm not sure how effecient this will be when you have massive amounts of text, but it works fairly well to the extent that I have tested it.
private void CheckKeyword(string word, Color color, int startIndex)
{
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 + startIndex), word.Length);
this.richTextBox1.SelectionColor = color;
this.richTextBox1.Select(selectStart, 0);
this.richTextBox1.SelectionColor = Color.Black;
}
}
}
private void richTextBox1_TextChanged(object sender, EventArgs e)
{
this.CheckKeyword("dog", Color.Red, 0);
this.CheckKeyword("cat", Color.Green, 0);
}

Categories