How to draw simple shapes onto a WPF xaml-defined canvas programatically - c#

I am porting (or attempting to) legacy code from a very slow WinForms app to WPF. The WinForms app falls down at the rendering of large images, and it has to rerender very frequently as the user pans around so it has to go.
I have a system in place to draw a canvas in xaml that allows zooming and panning with a custom child of Border, in the xaml it is named imaginatively as "canvas". I am having issues drawing simple ellipses to the canvas from other classes.
namespace ZoomPan
{
public class DisplayManager
{
protected void DrawIcon(Locale locale, Color color)
{
Brush sensorBrush = new SolidColorBrush(color);
Brush sensorOutline = new SolidColorBrush(Colors.Black);
int x = locale.x;
int y = locale.y;
halfSize = 10;
DrawEllipse(locale.Name, sensorBrush, sensorOutline, x - halfSize, y - halfSize, 2 * halfSize, 2 * halfSize);
}
public void DrawEllipse(string name, Brush sensorBrush, Brush sensorPen, int x, int y, int width, int height)
{
double thickness = 5;
Ellipse ellipse = new Ellipse();
/*
Define ellipse
*/
canvas.Children.Add(ellipse);
}
}
}
This was my first attempt, which throws up errors around the final canvas.Children line, namely "The name 'canvas' does not exist in the current context"
I tried moving DrawEllipse to a separate class here, which throws up different errors
namespace ZoomPan
{
public class DisplayManager
{
protected void DrawIcon(Locale locale, Color color)
{
Brush sensorBrush = new SolidColorBrush(color);
Brush sensorOutline = new SolidColorBrush(Colors.Black);
int x = locale.x;
int y = locale.y;
halfSize = 10;
DrawEllipse(locale.Name, sensorBrush, sensorOutline, x - halfSize, y - halfSize, 2 * halfSize, 2 * halfSize);
}
}
public class MainWindow : Window
{
public void DrawEllipse(string name, Brush sensorBrush, Brush sensorPen, int x, int y, int width, int height)
{
double thickness = 5;
Ellipse ellipse = new Ellipse();
/*
Define ellipse
*/
canvas.Children.Add(ellipse);
}
}
}
These errors are around the call to DrawEllipse in the first class:
"The name 'DrawEllipse' does not exist in the current context"
and when I add MainWindow. before the DrawEllipse:
"An object reference is required for the non-static field, method, or property 'ZoomPan.MainWindow.DrawEllipse(string, System.Windows.Media.Brush, System.Windows.Media.Brush, int, int, int, int)'"
When I first played around with WPF, I could get it to display onto the canvas when the DrawEllipse method was inside the public class MainWindow:Window of the MainWindow.xaml.cs file, hence the second attempt above.
I feel I am missing something obvious, and couldn't find any exact dupes of this question on the site.

The other classes need to be passed the canvas object to be able to use it.
Either pass the canvas to the DisplayManager through the constructor once so it can be stored and then used in all methods:
public class DisplayManager
{
Canvas canvas;
public DisplayManager(Canvas canvas)
{
this.canvas = canvas;
}
}
Or pass it into each method each time as the first parameter:
public class DisplayManager
{
public void DrawEllipse(Canvas canvas, string name, Brush sensorBrush, Brush sensorPen, int x, int y, int width, int height)
{
}
}

Related

Replacement for CSS3 function repeating-linear-gradient() in .NET (WinForms)

Is there any replacement (analogue) for CSS3 function repeating-linear-gradient() in .NET (WinForms, not WPF)?
I need to paint repeating "zebra stripes" (e.g. red, blue, green, red, blue, green, ...) at an angle 45 degrees.
UPD:
Following Jimi's advice I managed to solve the problem only partially:
private void DrawRepeatingStripes(int degree, int stripeWidth, Color[] colors, Rectangle rect, Graphics graphics)
{
using (var img = new Bitmap(colors.Length * stripeWidth, rect.Height))
{
using (var g = Graphics.FromImage(img))
{
for (int i = 0; i < colors.Length; i++)
{
// TODO: cache SolidBrush
g.FillRectangle(new SolidBrush(colors[i]), stripeWidth * i, 0, stripeWidth, rect.Height);
}
}
using (var tb = new TextureBrush(img, WrapMode.Tile))
{
using (var myMatrix = new Matrix())
{
myMatrix.Rotate(degree);
graphics.Transform = myMatrix;
graphics.FillRectangle(tb, rect);
graphics.ResetTransform();
}
}
}
}
Usage (in some form's code):
protected override void OnPaintBackground(PaintEventArgs e)
{
base.OnPaintBackground(e);
DrawRepeatingStripes(45, 10, new Color[] { Color.Red, Color.Yellow, Color.Green }, e.ClipRectangle, e.Graphics);
}
The problem is that rotation is... well, a rotation, so part of rect is filled with stripes and part is empty. Have no idea how to solve it :(
An example about using a TextureBrush to fill the surface of a Control used as canvas.
The LinearRepeatingGradient class exposes a bindable ColorBands Property (of Type BindingList<ColorBand>) that allows to add or remove ColorBand objects, a record that defines the Color and size of each band you want to generate.
The RotationAngle Property specifies the rotation to apply to the rendering.
In the Paint event of the Control used as canvas, call the Fill(Graphics g) method, passing the e.Graphics object provided by the PaintEventArgs argument.
A new Bitmap is generated, based on the content of the ColorBands Property.
When the rotation angle cannot be exactly divided by 90, the canvas' dimensions are inflated by a third of its diagonal (as the maximum distance from the non-rotated rectangle).
The TextureBrush fills this inflated surface, so no blank space is left on the sides of the canvas.
Since this test sample is built with .NET 7, I'm using record to store the color bands' settings. You can replace it with a class object without changes to the rest of the code.
public record ColorBand(Color Color, int Size) {
public override string ToString() => $"Color: {Color.Name} Size: {Size}";
}
Same as above: using declaration instead of using statements
using System.Drawing;
using System.Drawing.Drawing2D;
public class LinearRepeatingGradient
{
public LinearRepeatingGradient(float rotation = .0f)
{
ColorBands = new BindingList<ColorBand>();
RotationAngle = rotation;
}
public float RotationAngle { get; set; }
[Bindable(true), ListBindable(BindableSupport.Default)]
public BindingList<ColorBand> ColorBands { get; }
public void Fill(Graphics g) => Fill(g, g.ClipBounds);
public void Fill(Graphics g, Rectangle fillArea) => Fill(g, new RectangleF(fillArea.Location, fillArea.Size));
protected virtual void Fill(Graphics g, RectangleF display)
{
if (ColorBands is null || ColorBands.Count == 0 || g.Clip.IsInfinite(g)) return;
var canvas = InflateCanvas(display);
var centerPoint = new PointF(canvas.X + canvas.Width / 2, canvas.Y + canvas.Height / 2);
using var texture = GetTexture(canvas.Width);
if (texture is null) return;
using var brush = new TextureBrush(texture, WrapMode.Tile);
using var mx = new Matrix();
mx.RotateAt(RotationAngle, centerPoint);
g.Transform = mx;
g.FillRectangle(brush, canvas);
g.ResetTransform();
}
private RectangleF InflateCanvas(RectangleF rect)
{
if (RotationAngle % 90.0f == 0) return rect;
float maxInflate = (float)Math.Sqrt(Math.Pow(rect.X - rect.Right, 2) +
Math.Pow(rect.Y - rect.Bottom, 2)) / 3.0f;
var canvas = rect;
canvas.Inflate(maxInflate, maxInflate);
return canvas;
}
private Bitmap? GetTexture(float width)
{
int height = ColorBands!.Sum(c => c.Size);
if (height <= 0) return null;
var texture = new Bitmap((int)(width + .5f), height);
int startPosition = 0;
using var g = Graphics.FromImage(texture);
for (int i = 0; i < ColorBands!.Count; i++) {
var rect = new Rectangle(0, startPosition, texture.Width, ColorBands![i].Size);
using var brush = new SolidBrush(ColorBands![i].Color);
g.FillRectangle(brush, rect);
startPosition += ColorBands![i].Size;
}
return texture;
}
}
This is how it works:
Since the ColorBands property is bindable, you can use data bindings to perform actions, when a ColorBand object is added or removed and also bind the ColorBands collection to Controls, as shown in the animation:
public partial class SomeForm : Form {
LinearRepeatingGradient gradient = new();
public SomeForm()
{
InitializeComponent();
[DataGridView].DataSource = gradient.ColorBands;
gradient.ColorBands.ListChanged += (s, e) => someControl.Invalidate();
}
private void someControl_Paint(object sender, PaintEventArgs e) => gradient.Fill(e.Graphics);
As a consequence, when you add a new ColorBand (or remove it), the internal collection changes and the Control used as canvas is invalidated, showing the new fill:
gradient.ColorBands.Add(new ColorBand(Color.Red, 45f));
The RotationAngle property doesn't use data bindings, so you have to invalidate the canvas manually when you change it. You can of course change that and make this property bindable:
gradient.RotationAngle = 215f;
someControl.Invalidate();

How to draw a matrix on winform

I have an empty class Box that i make a matrix of like you se below and i want to draw this matrix on winform how can i do that ?
For example if Box[i,j] have red color and i change the color of the box like thisBox[i.j].color = Color.Black så should my winform change the color of Box[i,j] to black after it draw the whole matrix.
Box[,] boxes = new Box[100, 100];
MainForm form;
Timer timer;
public Game()
{
form = new MainForm();
timer = new Timer();
}
public void Run(int size)
{
form.Paint += new PaintEventHandler(Draw);
timer.Tick += new EventHandler(TimerEventHandler);
timer.Interval = 1000 / 25;
timer.Start();
form.Visible = true;
}
private void TimerEventHandler(Object obj, EventArgs args)
{
form.Refresh();
}
private void Draw(Object obj, PaintEventArgs args)
{
}
Here's an approach to doing it (not the only way):
First, give Box a method to draw itself to a Graphics context:
class Box
{
public Color color { get; set; } = Color.Red;
// Note: Conventionally, property names begin with a capital letter.
/// <summary>
/// Draw this block to the given graphics context.
/// </summary>
/// <param name="g">The graphics context to draw to.</param>
/// <param name="x">X-coordinate of the block within the graphics context.</param>
/// <param name="y">Y-coordinate of the block within the graphics context.</param>
/// <param name="width">Width of the area in which to draw the block.</param>
/// <param name="height">Height of the area in which to draw the block.</param>
public void DrawTo(Graphics g, int x, int y, int width, int height)
{
// Fill in the color of this block:
Brush brush = new SolidBrush(color); // fill style, color etc.
g.FillRectangle(brush, x, y, width, height);
// Black outline:
Pen pen = new Pen(Color.Black); // line style, color, etc.
g.DrawRectangle(pen, x, y, width, height);
// look up the documentation of the System.Drawing.Graphics class for other methods for drawing.
}
}
Now, if you wanted to draw the matrix to a bitmap image, you could do this:
private void DrawMatrix(System.Drawing.Bitmap bm)
{
Graphics g = Graphics.FromImage(bm);
try
{
for (int y = 0; y < MatrixHeight; y++)
for (int x = 0; x < MatrixWidth; x++)
{
boxes[x, y].DrawTo(g, x * CellWidth, y * CellHeight, CellWidth, CellHeight);
}
}
finally
{
g.Dispose();
}
}
// Size of the matrix:
const int MatrixWidth = 100;
const int MatrixHeight = 100;
// Size of each cell in pixels:
const int CellWidth = 20;
const int CellHeight = 20;
But that would be too slow to do each frame (for a 100x100 matrix).
You could update an off-screen image like this if you were updating only individual cells whenever they changed.
You could then place a PictureBox control on the form, and set its Image property to the Bitmap updated here. You can create that bitmap (parameter to this method) with new Bitmap(MatrixWidth * CellWidth, MatrixHeight * CellHeight) .
Alternatively, you could draw to the form's canvass in the OnPaint event handler (the Draw method in your question).
Assuming that the window scrolls (and not all of the matrix is visible within the viewport at any time),
you would loop through only the visible cells and make them draw themselves to the form's Graphics context.
The form's Graphics context is the property Graphics on the event arguments object. This is what you need for the Box.Draw method above. When drawing each cell, you have to take account of its position in the grid and how the window is scrolled, in order to calculate the coordinates to be passed to that method.
On each invocation of the OnPaint event, only part of the window has to be redrawn. This part is given by the property ClipRectangle . (Windows breaks down the area to be redrawn into one or more rectangles, and does a call for each rectangle.)
(ClipRectangle could be the whole window.)
Drawing outside of this is allowed but can be inefficient.
So, for efficiency, you should loop through only all cells that fall at least partly within ClipRectangle, calculate their position within the visible area and call their Box.Draw.
The coordinates used with the Graphics class are the visible coordinates, so (0,0) is the top left of the window, regardless of scrolling.
See these properties for the scroll position:
Form.HorizontalScroll.Value
Form.VerticalScroll.Value

Extract and display parameter in a dialog?

I am working on a small project and packing it into GUI. The reference source code is DrawTools(Download source code - 61.1 Kb).
The reference source code demos a drawing tool in C# WinForms.
The function is to draw different figures like rectangle, ellipse, polygon, etc.
I want to use the location and size information of these figures to do further work, so if I draw a rectangle in the draw area, could C# WinForms returns the parameter of this figure(eg. x,y,width,height in the DrawRectangle.cs)?
Code as follow:
public DrawRectangle(int x, int y, int width, int height)
{
rectangle.X = x;
rectangle.Y = y;
rectangle.Width = width;
rectangle.Height = height;
Initialize();
}
Further more, How to get the returned parameters and then displayed in a new dialog?
when you draw these shapes: rectangle, ellipse, polygon, etc. you are using the location and width and height of them. if you want to save them to an object create one and save them in a list of some other structure...
for example:
List<object> shapes = new List<object>();
private void drawSquare(int x1, int y1, int x2, int y2)
{
shapes.Add(new Rectangle(x1, y1, x2, y2));
}
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
Graphics g = e.Graphics;
foreach (var shape in shapes)
{
if (shape is Rectangle)
{
g.DrawRectangle(new Pen(Color.Black), (Rectangle)shape);
}
}
}
this is just a small example, you should check the OnPaint method and Graphics to get more information on what you can and should do
You can add some event to support notifying what is happening, something like this:
public class InitRectangleEventArgs : EventArgs {
public Rectangle Rectangle {get;set;}
}
public delegate void InitRectangleEventHandler(object sender, InitRectangleEventArgs e);
public event InitRectangleEventHandler InitRectangle;
public DrawRectangle(int x, int y, int width, int height)
{
rectangle.X = x;
rectangle.Y = y;
rectangle.Width = width;
rectangle.Height = height;
if(InitRectangle != null) InitRectangle(this, new InitRectangleEventArgs { Rectangle = new Rectangle(x,y,width,height)});
Initialize();
}
//To use it, just subscribe the event so that you can know the
//info of the Rectangle everytime it is initialized
InitRectangle += (s,e) => {
//Get the info from the Rectangle property of e: e.Rectangle
//....
};

Move Graphics axis or get a sub-graphics at an arbitrary point?

I often need to draw items in a Graphics object and the way I've been doing it is to have a function DrawItem that receives the Graphics object and an offsetX and offsetY parameters, which determine at which point the item will be drawn.
The problem is that the code inside DrawItem would look a lot better if there would be a method in Graphics that would give me a version of the Graphics where the X and Y axis zeroes are at some other point, something like myGraphics.DisplacedGraphics(offsetX, offsetY). This way I would just pass this Graphics objects to my DrawItem method which wouldn't need to receive the other two parameters. Is there such function or what's the closest thing?
Edit: On the meanwhile this is what I wrote, but seems like such a basic requirement I still hope there already exists such functionality (I'd still need to add a bunch of methods but these are all I need for now) (note the DisplacedCanvas method):
public class Canvas
{
private readonly Graphics _Graphics;
private readonly int _OriginX = 0;
private readonly int _OriginY = 0;
public Canvas(Graphics graphics, int originX, int originY)
{
_Graphics = graphics;
_OriginX = originX;
_OriginY = originY;
}
public Canvas(Graphics graphics) : this(graphics, 0, 0) { }
public SizeF MeasureString(string text, Font font)
{
return _Graphics.MeasureString(text, font);
}
public void FillRectangle(Brush brush, int x, int y, int width, int height)
{
_Graphics.FillRectangle(brush, _OriginX + x, _OriginY + y, width, height);
}
public void DrawString(string s, Font font, Brush brush, float x, float y)
{
_Graphics.DrawString(s, font, brush, _OriginX + x, _OriginY + y);
}
public Canvas DisplacedCanvas(int x, int y)
{
return new Canvas(_Graphics, _OriginX + x, _OriginY + y);
}
}
I'm pretty sure that the TranslateTransform() method will do what you're asking for.
The origin is typically the upper-left-hand corner of the drawing surface. The translation operation consists of multiplying the transformation matrix by a matrix whose translation part is the dx and dy parameters. This method applies the translation by prepending the translation matrix to the transformation matrix.
So if you want the new origin to be at 100, 50, then you would first call graphics.TranslateTransform(100, 50) before drawing your image.

How to customise rendering of a ToolStripTextBox?

I like the ToolStripProfessionalRenderer style quite a lot, but I do not like the way it renders a ToolStripTextBox. Here, ToolStripSystemRenderer does a better job IMO. Now is there a way to combine both renderers' behaviour to use system style for text boxes and pro style for everything else? I have successfully managed to use pro style for buttons and system style for the rest (by deriving both classes). But text boxes in a ToolStrip don't seem to be handled by the renderer. Using .NET Reflector, those text boxes don't even seem to have a Paint event handler, although it's called by the ToolStrip.OnPaint method. I'm wondering where's the code to paint such a text box at all and how it can be configured to draw a text box like all other text boxes.
If you just want system rendering, the easiest approach is to use ToolStripControlHost instead:
class ToolStripSystemTextBox : ToolStripControlHost
{
public ToolStripSystemTextBox : base(new TextBox()) { }
[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
[TypeConverter(typeof(ExpandableObjectConverter))]
public TextBox TextBox { get { return Control as TextBox; } }
}
I've taken the easy way out here and exposed the underlying TextBox directly to the form designer, instead of delegating all its properties. Obviously you can write all the property delgation code if you want.
On the other hand, if anyone wants to do truly custom rendering, I'll tell you what ToolStripTextBox does. Instead of hosting a TextBox directly, it hosts a private derived class called ToolStripTextBoxControl. This class overrides its WndProc in order to directly handle WM_NCPAINT. And then instead of delegating the actual drawing to the Renderer, it checks the Renderer's Type, and then branches to different rendering code inside of ToolStripTextBoxControl. It's pretty ugly.
It may not be necessary to dive into "WndProc" either. This was done without it:
The Question really is how do you make a "nice looking" TextBox, because as described by j__m, you can just use ToolStripControlHost, to host a custom control in your tool strip.
More here:
http://msdn.microsoft.com/en-us/library/system.windows.forms.toolstripcontrolhost.aspx
And as documented, the control you use can be a Custom Control.
Firstly, It's insanely tricky to make a custom TextBox Control. If you want to go:
public partial class TextBoxOwnerDraw : TextBox
You are in for HUGE trouble! But it doesn't have to be. Here is a little trick:
If you make a custom control as a Panel, then add the TextBox to the Panel, then set the Textbox borders to None... you can achieve the result as above, and best of all, its just a regular old TextBox, so cut copy paste all works, right click works!
Ok, here is the code for a nice looking textbox:
public partial class TextBoxOwnerDraw : Panel
{
private TextBox MyTextBox;
private int cornerRadius = 1;
private Color borderColor = Color.Black;
private int borderSize = 1;
private Size preferredSize = new Size(120, 25); // Use 25 for height, so it sits in the middle
/// <summary>
/// Access the textbox
/// </summary>
public TextBox TextBox
{
get { return MyTextBox; }
}
public int CornerRadius
{
get { return cornerRadius; }
set
{
cornerRadius = value;
RestyleTextBox();
this.Invalidate();
}
}
public Color BorderColor
{
get { return borderColor; }
set
{
borderColor = value;
RestyleTextBox();
this.Invalidate();
}
}
public int BorderSize
{
get { return borderSize; }
set
{
borderSize = value;
RestyleTextBox();
this.Invalidate();
}
}
public Size PrefSize
{
get { return preferredSize; }
set
{
preferredSize = value;
RestyleTextBox();
this.Invalidate();
}
}
public TextBoxOwnerDraw()
{
MyTextBox = new TextBox();
this.Controls.Add(MyTextBox);
RestyleTextBox();
}
private void RestyleTextBox()
{
double TopPos = Math.Floor(((double)this.preferredSize.Height / 2) - ((double)MyTextBox.Height / 2));
MyTextBox.BackColor = Color.White;
MyTextBox.BorderStyle = BorderStyle.None;
MyTextBox.Multiline = false;
MyTextBox.Top = (int)TopPos;
MyTextBox.Left = this.BorderSize;
MyTextBox.Width = preferredSize.Width - (this.BorderSize * 2);
this.Height = MyTextBox.Height + (this.BorderSize * 2); // Will be ignored, but if you use elsewhere
this.Width = preferredSize.Width;
}
protected override void OnPaint(PaintEventArgs e)
{
if (cornerRadius > 0 && borderSize > 0)
{
Graphics g = e.Graphics;
g.SmoothingMode = SmoothingMode.AntiAlias;
Rectangle cRect = this.ClientRectangle;
Rectangle safeRect = new Rectangle(cRect.X, cRect.Y, cRect.Width - this.BorderSize, cRect.Height - this.BorderSize);
// Background color
using (Brush bgBrush = new SolidBrush(MyTextBox.BackColor))
{
DrawRoundRect(g, bgBrush, safeRect, (float)this.CornerRadius);
}
// Border
using (Pen borderPen = new Pen(this.BorderColor, (float)this.BorderSize))
{
DrawRoundRect(g, borderPen, safeRect, (float)this.CornerRadius);
}
}
base.OnPaint(e);
}
#region Private Methods
private GraphicsPath getRoundRect(int x, int y, int width, int height, float radius)
{
GraphicsPath gp = new GraphicsPath();
gp.AddLine(x + radius, y, x + width - (radius * 2), y); // Line
gp.AddArc(x + width - (radius * 2), y, radius * 2, radius * 2, 270, 90); // Corner (Top Right)
gp.AddLine(x + width, y + radius, x + width, y + height - (radius * 2)); // Line
gp.AddArc(x + width - (radius * 2), y + height - (radius * 2), radius * 2, radius * 2, 0, 90); // Corner (Bottom Right)
gp.AddLine(x + width - (radius * 2), y + height, x + radius, y + height); // Line
gp.AddArc(x, y + height - (radius * 2), radius * 2, radius * 2, 90, 90); // Corner (Bottom Left)
gp.AddLine(x, y + height - (radius * 2), x, y + radius); // Line
gp.AddArc(x, y, radius * 2, radius * 2, 180, 90); // Corner (Top Left)
gp.CloseFigure();
return gp;
}
private void DrawRoundRect(Graphics g, Pen p, Rectangle rect, float radius)
{
GraphicsPath gp = getRoundRect(rect.X, rect.Y, rect.Width, rect.Height, radius);
g.DrawPath(p, gp);
gp.Dispose();
}
private void DrawRoundRect(Graphics g, Pen p, int x, int y, int width, int height, float radius)
{
GraphicsPath gp = getRoundRect(x, y, width, height, radius);
g.DrawPath(p, gp);
gp.Dispose();
}
private void DrawRoundRect(Graphics g, Brush b, int x, int y, int width, int height, float radius)
{
GraphicsPath gp = getRoundRect(x, y, width, height, radius);
g.FillPath(b, gp);
gp.Dispose();
}
private void DrawRoundRect(Graphics g, Brush b, Rectangle rect, float radius)
{
GraphicsPath gp = getRoundRect(rect.X, rect.Y, rect.Width, rect.Height, radius);
g.FillPath(b, gp);
gp.Dispose();
}
#endregion
}
Now for the ToolStripControlHost
public partial class ToolStripTextBoxOwnerDraw : ToolStripControlHost
{
private TextBoxOwnerDraw InnerTextBox
{
get { return Control as TextBoxOwnerDraw; }
}
public ToolStripTextBoxOwnerDraw() : base(new TextBoxOwnerDraw()) { }
public TextBox ToolStripTextBox
{
get { return InnerTextBox.TextBox; }
}
public int CornerRadius
{
get { return InnerTextBox.CornerRadius; }
set
{
InnerTextBox.CornerRadius = value;
InnerTextBox.Invalidate();
}
}
public Color BorderColor
{
get { return InnerTextBox.BorderColor; }
set
{
InnerTextBox.BorderColor = value;
InnerTextBox.Invalidate();
}
}
public int BorderSize
{
get { return InnerTextBox.BorderSize; }
set
{
InnerTextBox.BorderSize = value;
InnerTextBox.Invalidate();
}
}
public override Size GetPreferredSize(Size constrainingSize)
{
return InnerTextBox.PrefSize;
}
}
Then When you want to use it, just add it to the tool bar:
ToolStripTextBoxOwnerDraw tBox = new ToolStripTextBoxOwnerDraw();
this.toolStripMain.Items.Add(tBox);
or however you want to add it. If you are in Visual Studio, the preview window supports rendering this Control.
There is only one thing to remember, when accessing the TextBox with the actual text in it, its:
tBox.ToolStripTextBox.Text;

Categories