My question has three parts:
I Can draw shapes like line, Circle, Rectangle and .. on WPF Canvas. I want to use InkCanvas features like erasing and moving strokes.
Is it possible to convert this shape to a collection of stylus
points and add this collection to InkCanvas?
If it's possible how can I do that?
Is there a better approach to this situation?
Guide me please.
first of all, the answer is Yes. you can convert paths to stroke collection and then add them to the InkCanvas.
For the second part of your question, the answer should be something like this:
Point mypoint;
Point tg;
var pointCollection = new List<Point>();
for (var i = 0; i < 500; i++)
{
SomePath.Data.GetFlattenedGeometryPath()
.GetPointAtFractionLength(i / 500f, out mypoint, out tg);
pointCollection.Add(p);
}
For stylus point and stylus point collection:
StylusPointCollection StPoints = new StylusPointCollection();
add stylus points during converting path to collection of points by:
StPoints.Add(new StylusPoint(p.X, P.Y));
And after this step calling Stroke method to create a collection of strokes from your stylus collection:
Stroke st = null;
st = new Stroke(StPoints);
Update
Yes! there is a better ways for adding shapes to inkCanvas.
You can define this stylus points shape directly and add them using MouseDown, MouseMove.. for example for drawing a Rectangle:
pts.Add(new StylusPoint(mouseLeftDownPoint.X, mouseLeftDownPoint.Y));
pts.Add(new StylusPoint(mouseLeftDownPoint.X, currentPoint.Y));
pts.Add(new StylusPoint(currentPoint.X, currentPoint.Y));
pts.Add(new StylusPoint(currentPoint.X, mouseLeftDownPoint.Y));
pts.Add(new StylusPoint(mouseLeftDownPoint.X, mouseLeftDownPoint.Y));
Or Override DrawCore method of Stroke Class and define a new stroke type.
Custom Rendering Ink (MSDN)
void MainWindow_MouseMove(object sender, MouseEventArgs e)
{
Ellipse el = new Ellipse();
el.Fill = ellipse.Fill;
el.Width = ellipse.Width;
el.Height = ellipse.Height;
Point p = e.GetPosition(this.canvasPrint);
Canvas.SetLeft(el, p.X);
Canvas.SetTop(el, p.Y);
canvasPrint.Children.Add(el);
}
This is the sample code of the program that I made. I am adding ellipses on a canvas. The point of each ellipse is the point of the mouse cursor. When the mouse is moving, a new ellipse is generated and added so it works like a brush tool in MS Paint.
However, it consumes too much memory. So after a while, the program becomes slower. Is there any way to solve this problem? Can't I reuse the same ellipse object when I add it on the canvas?
You should use DrawingVisual and DrawingContext to draw your brush path. DrawingContext is similar to Windows Forms Graphics object. So in your situation, just draw your brush path on the same DrawingContext.
Hi I want to have a rectangle like the below picture to fill the entire canvas with different sizes and at different position scatter through out the canvas. My solution environment is WPF C#
Could some one please guide me of how to do it? Till now what I have done is canvas_loaded
Rectangle rect = new Rectangle();
rect.Fill = new SolidColorBrush(Colors.Black);
rect.Width = 100;
rect.Height = 100;
rect.Stroke = new SolidColorBrush(Colors.Black);
But the problem here how will I position it to the different locaion of the canvas, the size and width I can provide at run time with different value but I need to position the rectangles (Square) at diffrent XY co-ordinates so that none of the rectangles or Squares overlapp each other.
Please help.
You can use
Canvas.SetLeft(rect, <offset>) Canvas.SetRight(...), Canvas.SetTop(...), Canvas.SetBottom(...)
to position UIElement in the Canvas container.
Use Random class to generate the xy co-ordinates
Random r=new Random();
r.Next(1,100);
As you all know, a label is usually in a square or rectangle shape. I really need to make circle shaped label. Can anyone please tell me is this possible or at least point me in a right direction?
Sorry, just to make things clear. I want a circle shaped label. Not just drawing a circle on the screen.
You can set the Region property of your Label :
var path = new System.Drawing.Drawing2D.GraphicsPath();
path.AddEllipse(0, 0, label1.Width, label1.Height);
this.label1.Region = new Region(path);
System.Drawing.Graphics graphics = this.CreateGraphics();
System.Drawing.Rectangle rectangle = new System.Drawing.Rectangle(100, 100, 200, 200);
graphics.DrawEllipse(System.Drawing.Pens.Black, rectangle);
Can I delete the old rectangle which I have drawn and draw a new rectangle?
private void panel1_MouseClick(object sender, MouseEventArgs e)
{
Graphics g = this.panel1.CreateGraphics();
Pen pen = new Pen(Color.Black, 2);
g.DrawRectangle(pen, 100,100, 100, 200);
g.dispose();
}
No, you cannot "delete" something that's already been drawn. You can overwrite it with something else, but drawing with Graphics objects is like painting in real-life: once the paint is dry, you can only paint over it with another colour, you can't "erase" it.
You probably shouldn't be drawing things in response to a MouseClick, either. It's best to only draw things in response to a Paint event. What I would do in this situation is add a Rectangle structure to a list on the MouseClick and then call panel1.Invalidate() to ask it to redraw itself. Then in the Paint event for the panel, do the drawing there.
This will kill two birds with one stone, because you will be able to "erase" thing by simply removing them from the list of stuff to draw.
This is usually done by maintaining a collection of objects you want drawn. The mouse click should update this collection and then tell the window (or the affect region) to refresh. This has the enormous advantage of preserving whatever you've drawn if the window is moved off-screen, hidden behind other windows, minimized, etc.
For a rudimentary solution, create a hierarchy of drawable shape types derived from a common abstract Shape class, and use, e.g., a List for the collection. The base Shape class will have an abstract Draw method that the derived classes override.
For a more industrial-strength solution, look around for 2-D scene graphs.
One can use Graphics.Save() and Graphics.Restore(state) methods for that. For example:
private void SaveRestore2(PaintEventArgs e)
{
// Translate transformation matrix.
e.Graphics.TranslateTransform(100, 0);
// Save translated graphics state.
GraphicsState transState = e.Graphics.Save();
// Reset transformation matrix to identity and fill rectangle.
e.Graphics.ResetTransform();
e.Graphics.FillRectangle(new SolidBrush(Color.Red), 0, 0, 100, 100);
// Restore graphics state to translated state and fill second
// rectangle.
e.Graphics.Restore(transState);
e.Graphics.FillRectangle(new SolidBrush(Color.Blue), 0, 0, 100, 100);
}
http://msdn.microsoft.com/en-us/library/system.drawing.graphics.restore.aspx
Also, depending on the application, you might look at using DrawReversibleFrame. You can change the rectangle location by calling the Offset method.
Instead of calling g.DrawRectangle(pen, 100,100, 100, 200); , maintain the rectangle as a object which will be drawn by the graphics object. Each time you will update this rectangle object with new one and graphics object will draw the new one.
The refresh should clear the old rectangle and graphics will draw the new one.
You can just use VisualBasic PowerPacks, it is included with my version of Visual Studio 2008
Here's a sample code that will draw a rectangle over a TextBox, i.e. I am giving it a custom border
Dim x = TextBox1.Location.X
Dim y = TextBox1.Location.Y
Dim width = TextBox1.Width
Dim height = TextBox1.Height
Dim ShapeContainer1 As New Microsoft.VisualBasic.PowerPacks.ShapeContainer
Me.Controls.Add(ShapeContainer1)
Dim RectangleShape1 As New Microsoft.VisualBasic.PowerPacks.RectangleShape
ShapeContainer1.Shapes.AddRange(New Microsoft.VisualBasic.PowerPacks.Shape() {RectangleShape1})
RectangleShape1.Location = New System.Drawing.Point(x - 1, y - 1)
RectangleShape1.Size = New System.Drawing.Size(width + 1, height + 1)
RectangleShape1.BorderColor = Color.MistyRose
ShapeContainer1.Refresh()
Code is self describing but if you'd have any problem, just leave a message...
I think using DrawReversibleFrame is the right solution.
The first call draw the rectangle, the second call undraw it and so on.
Here is a sample code, a clic on the button will make the rectangle appear/disapper.
Rectangle pRect = new Rectangle(10, 10, 20, 20);
private void rect_Click(object sender, EventArgs e)
{
ControlPaint.DrawReversibleFrame(pRect, this.BackColor, FrameStyle.Thick);
}