I am currently working on a chat window and have created tiles for messages.
I have a problem because the text in tiles draws using the DrawString method and I`m not really sure if there is any such possibility that this text can be copied? How can it enable the user to select and copy text? Below is part of the source code.
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
var graphics = e.Graphics;
...
using (SolidBrush brush = new SolidBrush(this.ForeColor))
{
if (_BoxPosition == BoxPositionEnum.Left)
{
graphics.DrawString(Text, Font, brush, new Rectangle(20 , Height / 2, renderWidth - 5, Height / 2 - 5));
}
else
{
StringFormat format = new StringFormat()
{
LineAlignment = StringAlignment.Center,
Alignment = StringAlignment.Far
};
graphics.DrawString(Text, Font, brush,
new Rectangle(_mc.BoxIndent - 10 , Height / 2, renderWidth - 5, Height / 2 - 5),format);
}
}
For simple: just use TextBox or RichTextBox and set ReadOnly = true and color as what you want.
For more advanced: create your own message box user control, and you can do many thing with it (react, select all, ...).
Here the tutorial for create your own user control: https://www.c-sharpcorner.com/UploadFile/7d3362/user-control-in-C-Sharp/
Related
I'm working on a GUI for a CNC machine. We have load cells that output a voltage depending on how much force is applied to the cell, which the machine can read and then display to the operator so they know how much force they are clamping a part with.
Microsoft's website says .NET Framework 4.6.1 (which I'm building with) progress bars have a text property, but setting the text itself doesn't display it. I found a different way of doing it like this:
int loadVal = 0;
string progBarText = "";
SizeF textSize;
Graphics graphics = CreateGraphics();
Font font = new Font("Lucida Console", FontHeight = 11, FontStyle.Regular);
leftClampProgBar.SuspendLayout();
rightClampProgBar.SuspendLayout();
//~~~~Left Clamp~~~~~~
loadVal = (PLC_ushLeftClampLoad * 500) / 65535;
leftClampProgBar.Value = (loadVal * 100) / 500;
//setting the text for the progress bar
progBarText = loadVal.ToString() + " Lb(s)";
//have to figure out how big the text is
textSize = graphics.MeasureString(progBarText, font);
//drawing the text to the progress bar
leftClampProgBar.CreateGraphics().DrawString(
progBarText,
font,
Brushes.Black,
new PointF((leftClampProgBar.Width - textSize.Width) / 2,
(leftClampProgBar.Height - textSize.Height) / 2));;
//~~~~~Right Clamp~~~~~~
loadVal = (PLC_ushRightClampLoad * 500) / 65535;
rightClampProgBar.Value = (loadVal * 100) / 500;
//setting the text for the progress bar
progBarText = loadVal.ToString() + " Lb(s)";
//have to figure out how big the text is
textSize = graphics.MeasureString(progBarText, font);
//drawing the text to the progress bar
rightClampProgBar.CreateGraphics().DrawString(
progBarText,
font,
Brushes.Black,
new PointF((rightClampProgBar.Width - textSize.Width) / 2,
(rightClampProgBar.Height - textSize.Height) / 2));
//AddNotification("Right Clamp: " + loadVal, Color.Purple);
leftClampProgBar.ResumeLayout();
rightClampProgBar.ResumeLayout();
However, this leads to the the text sometimes being printed wrong, or it is not refreshing correctly. The method the code above is in gets called by a timer every 500ms and causes the GUI to act a little slower than before. I could make a different timer for this specifically that has a larger interval, but I wanted to know if there was a more efficient way to display the text at all, not worrying about how often the timer repaints it.
progress bar text
You could create custom progress bar to do this. The following class assumes ProgressBarRenderer.IsSupported results in true and does not animate progress bar.
public class TextProgressBar : ProgressBar
{
public TextProgressBar() : base()
{
SetStyle(ControlStyles.UserPaint, true);
DoubleBuffered = true; // remove flicker
}
// unhide Text/Font Properties and force changes to re-render control
[Browsable(true)]
[EditorBrowsable(EditorBrowsableState.Always)]
public override string Text
{
get => base.Text;
set
{
base.Text = value;
Refresh();
}
}
[Browsable(true)]
[EditorBrowsable(EditorBrowsableState.Always)]
public override Font Font
{
get => base.Font;
set
{
base.Font = value;
if (!string.IsNullOrWhiteSpace(Text)) Refresh();
}
}
protected override void OnPaintBackground(PaintEventArgs pevent)
{
base.OnPaintBackground(pevent);
// draw progress bar background
ProgressBarRenderer.DrawHorizontalBar(pevent.Graphics, new Rectangle(0, 0, Width, Height));
}
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
// draw progress on progress bar
double percentage = ((double)(Value - Minimum)) / ((double)(Maximum - Minimum));
ProgressBarRenderer.DrawHorizontalChunks(e.Graphics, new Rectangle(0, 0, (int)(Width * percentage), Height));
// draw text on progress bar
using (Brush brush = new SolidBrush(ForeColor))
{
// get rendered size of text
var size = e.Graphics.MeasureString(Text, Font, new SizeF(Width, Height));
// calculate location to center text on progress bar
var location = new PointF((Width - size.Width) * 0.5f, (Height - size.Height) * 0.5f);
// draw text
e.Graphics.DrawString(Text, Font, brush, new RectangleF(location, size));
}
}
}
I would recommend just having a separate label above or to the side of the progress-bar that you update. This should be far simpler, and probably also being easier to read.
If you insist on printing the text on top of the progress-bar you should consider creating your own class that derives from progress-bar and override the onPaint method. But if you use this approach you need to consider contrast and legibility as the text and progress overlaps.
in my program i have a group box, i didnt like that the groupbx provided in visual studio doesnt have a border colour property so i used this code to create my own group box.
public class MyGroupBox : GroupBox
{
private Color _borderColor = Color.Black;
public Color BorderColor
{
get { return this._borderColor; }
set { this._borderColor = value; }
}
protected override void OnPaint(PaintEventArgs e)
{
//get the text size in groupbox
Size tSize = TextRenderer.MeasureText(this.Text, this.Font);
Rectangle borderRect = e.ClipRectangle;
borderRect.Y = (borderRect.Y + (tSize.Height / 2));
borderRect.Height = (borderRect.Height - (tSize.Height / 2));
ControlPaint.DrawBorder(e.Graphics, borderRect, this._borderColor, ButtonBorderStyle.Solid);
Rectangle textRect = e.ClipRectangle;
textRect.X = (textRect.X + 6);
textRect.Width = tSize.Width;
textRect.Height = tSize.Height;
e.Graphics.FillRectangle(new SolidBrush(this.BackColor), textRect);
e.Graphics.DrawString(this.Text, this.Font, new SolidBrush(this.ForeColor), textRect);
}
}
which works "fine", i got myself a black border group box instead of grey, except when the window is moved the group box glitches out like so,
is there a fix for this or will i have to use the visual studio group box to prevent this issue? i am using C# winforms
The documentation for PaintEventArgs.ClipRectangle is misleading - Gets the rectangle in which to paint.. Actually this property represents the invalidated rectangle of the window, which is not always the full rectangle. It can be used to skip painting of elements that are outside that rectangle, but not as base for painting.
But the base rectangle for all painting should be the ClientRectangle property of the control being painted. So simply replace e.ClipRectangle with this.ClientRectangle.
This question already has answers here:
how to change the check image on a checkbox
(5 answers)
Closed 2 years ago.
I want to change the border color and the background of the square and the color of the check mark, but not the text. To better understand, what I want to accomplish is the following example:
checkBox1.Checked = false
checkBox1.Checked = true
Thanks so much to everyone of you responds exactly to this request!
You can simply draw the checkmark in the Paint event:
private void checkBox1_Paint(object sender, PaintEventArgs e)
{
Point pt = new Point(e.ClipRectangle.Left + 2, e.ClipRectangle.Top + 4);
Rectangle rect = new Rectangle(pt, new Size(22, 22));
if (checkBox1.Checked)
{
using (Font wing = new Font("Wingdings", 14f))
e.Graphics.DrawString("ü", wing, Brushes.DarkOrange,rect);
}
e.Graphics.DrawRectangle(Pens.DarkSlateBlue, rect);
}
for this to work you need to:
set Apperance = Appearance.Button
set FlatStyle = FlatStyle.Flat
set TextAlign = ContentAlignment.MiddleRight
set FlatAppearance.BorderSize = 0
set AutoSize = false
If you want to re-use this it will be best to subclass the checkbox and override the OnPaint event there. Here is an example:
public ColorCheckBox()
{
Appearance = System.Windows.Forms.Appearance.Button;
FlatStyle = System.Windows.Forms.FlatStyle.Flat;
TextAlign = ContentAlignment.MiddleRight;
FlatAppearance.BorderSize = 0;
AutoSize = false;
Height = 16;
}
protected override void OnPaint(PaintEventArgs pevent)
{
//base.OnPaint(pevent);
pevent.Graphics.Clear(BackColor);
using (SolidBrush brush = new SolidBrush(ForeColor))
pevent.Graphics.DrawString(Text, Font, brush, 27, 4);
Point pt = new Point( 4 , 4);
Rectangle rect = new Rectangle(pt, new Size(16, 16));
pevent.Graphics.FillRectangle(Brushes.Beige, rect);
if (Checked)
{
using (SolidBrush brush = new SolidBrush(ccol))
using (Font wing = new Font("Wingdings", 12f))
pevent.Graphics.DrawString("ü", wing, brush, 1,2);
}
pevent.Graphics.DrawRectangle(Pens.DarkSlateBlue, rect);
Rectangle fRect = ClientRectangle;
if (Focused)
{
fRect.Inflate(-1, -1);
using (Pen pen = new Pen(Brushes.Gray) { DashStyle = DashStyle.Dot })
pevent.Graphics.DrawRectangle(pen, fRect);
}
}
You may need to tweek the sizes of the control and the font.. And if you want to you expand the code to honor the TextAlign and the CheckAlign properties.
And if you need a three-state control you can adapt the code to display a third state appearance, especially if you think of one that looks better than the original..
You have to write your own checkbox. By making a custom control in which there's a blue square(possibly inherits from Button) that toggles between checked and not checked images by an OnClick event and place a label next to it.
I applied the solution provided by an user to solve the problem, but it's happening another problem. Previously I owned an icon along with the text, but after I used the code described here, my icon no longer appears. What can it be?
And how can I use the icon while using a different color other than the default on tab header?
The solution I used is:
private void tabControl1_DrawItem(object sender, System.Windows.Forms.DrawItemEventArgs e)
{
TabPage CurrentTab = tabControl1.TabPages[e.Index];
Rectangle ItemRect = tabControl1.GetTabRect(e.Index);
SolidBrush FillBrush = new SolidBrush(Color.Red);
SolidBrush TextBrush = new SolidBrush(Color.White);
StringFormat sf = new StringFormat();
sf.Alignment = StringAlignment.Center;
sf.LineAlignment = StringAlignment.Center;
//If we are currently painting the Selected TabItem we'll
//change the brush colors and inflate the rectangle.
if (System.Convert.ToBoolean(e.State & DrawItemState.Selected))
{
FillBrush.Color = Color.White;
TextBrush.Color = Color.Red;
ItemRect.Inflate(2, 2);
}
//Set up rotation for left and right aligned tabs
if (tabControl1.Alignment == TabAlignment.Left || tabControl1.Alignment == TabAlignment.Right)
{
float RotateAngle = 90;
if (tabControl1.Alignment == TabAlignment.Left)
RotateAngle = 270;
PointF cp = new PointF(ItemRect.Left + (ItemRect.Width / 2), ItemRect.Top + (ItemRect.Height / 2));
e.Graphics.TranslateTransform(cp.X, cp.Y);
e.Graphics.RotateTransform(RotateAngle);
ItemRect = new Rectangle(-(ItemRect.Height / 2), -(ItemRect.Width / 2), ItemRect.Height, ItemRect.Width);
}
//Next we'll paint the TabItem with our Fill Brush
e.Graphics.FillRectangle(FillBrush, ItemRect);
//Now draw the text.
e.Graphics.DrawString(CurrentTab.Text, e.Font, TextBrush, (RectangleF)ItemRect, sf);
//Reset any Graphics rotation
e.Graphics.ResetTransform();
//Finally, we should Dispose of our brushes.
FillBrush.Dispose();
TextBrush.Dispose();
}
You need to paint the Icon yourself also. An example would be to add something like the following after the painting of the tabs Background in you code (i assume that an image list was used here)
int imageLeftOffset = 4;
Point imagePos = new Point(imageLeftOffset, ItemRect.Top + (ItemRect.Height - tabControl1.ImageList.ImageSize.Height + 1) / 2);
tabControl1.ImageList.Draw(e.Graphics, imagePos, CurrentTab.ImageIndex);
You may need to reajust the drawing of the Text so that text and image aren't overlapping.
I have overridden the OnPaint method of my Label control in VS2008:
void Label_OnPaint(object sender, PaintEventArgs e) {
base.OnPaint(e);
dim lbl = sender as Label;
if (lbl != null) {
string Text = lbl.Text;
e.Graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
if (myShowShadow) { // draw the shadow first!
e.Graphics.DrawString(Text, lbl.Font, new SolidBrush(myShadowColor), myShadowOffset, StringFormat.GenericDefault);
}
e.Graphics.DrawString(Text, lbl.Font, new SolidBrush(lbl.ForeColor), 0, 0, StringFormat.GenericDefault);
}
}
This works, but I really want to find out how to center the text both vertically and horizontally. I've heard of the MeasureString() method, but my "Text" complicates matters because it could include page breaks.
Could someone guide me with how to do this?
Alternatively you can create your own StringFormat object and pass it in using an overload of DrawString that supports a RectangleF:
StringFormat formatter = new StringFormat();
formatter.LineAlignment = StringAlignment.Center;
formatter.Alignment = StringAlignment.Center;
RectangleF rectangle = new RectangleF(0, 0, lbl.Width, lbl.Height);
e.Graphics.DrawString(Text, lbl.Font, new SolidBrush(lbl.ForeColor), rectangle, formatter);
You can call TextRenderer.DrawText with the HorizontalCenter and VerticalCenter flags.
Here is the code i'm using at the moment,
SizeF size;
string text = "Text goes here";
size = e.Graphics.MeasureString(text, font);
x = (lineWidth / 2) - (size.Width / 2);
y = top;
e.Graphics.DrawString(text, font, Brushes.Black, x, y);
I just wanted to add (a year later) a tool I created because StringAlignment turned out to be not very dependable. It turns out to be very similar to Neo's version.
The code below does an excellent job of centering the text both vertically and horizontally. Also, I wrote it with various overloads so that different options could be supplied to make this control behave exactly like I want.
Here are my overloads:
private static void DrawCenter(Label label, Graphics graphics) {
DrawCenter(label.Text, label, label.Location, label.ForeColor, graphics);
}
private void DrawCenter(string text, Label label, Graphics graphics) {
DrawCenter(text, label, label.Location, label.ForeColor, graphics);
}
private static void DrawCenter(string text, Label label, Point location, Graphics graphics) {
DrawCenter(text, label, location, label.ForeColor, graphics);
}
private static void DrawCenter(string text, Label label, Point location, Color fontColor, Graphics graphics) {
Rectangle rect = new Rectangle(location, label.Size);
SizeF lSize = graphics.MeasureString(text, label.Font, rect.Width);
PointF lPoint = new PointF(rect.X + (rect.Width - lSize.Width) / 2, rect.Y + (rect.Height - lSize.Height) / 2);
graphics.DrawString(text, label.Font, new SolidBrush(fontColor), lPoint);
}
To use these for the Label's OnPaint event, simply modify my original code in the question to following:
private void Label_OnPaint(object sender, PaintEventArgs e) {
base.OnPaint(e);
Label lbl = sender as Label;
if (lbl != null) {
string txt = lbl.Text;
e.Graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
if (myShowShadow) { // draw the shadow first!
Point offset = new Point(lbl.Location.X - 1, lbl.Location.Y - 1)
DrawCenter(txt, lbl, offset, myShadowColor, e.Graphics);
}
DrawCenter(lbl, e.Graphics);
}
}
For a Print_Document event, I have a version that will also print a box around the label if there is already a box around it in the designer:
private static void DrawCenter(string text, Label label, Point location, Color fontColor, Graphics graphics) {
Rectangle rect = new Rectangle(location, label.Size);
SizeF lSize = graphics.MeasureString(text, label.Font, rect.Width);
PointF lPoint = new PointF((rect.Width - lSize.Width) / 2, (rect.Height - lSize.Height) / 2);
graphics.DrawString(text, label.Font, new SolidBrush(fontColor), lPoint);
if (label.BorderStyle != BorderStyle.None) {
using (Pen p = new Pen(Color.Black)) {
graphics.DrawRectangle(p, rect);
}
}
}
If you find this at all useful, give me a +1.
~Joe