I draw some Ellipse and add them to a Grid.
Then I'd like to add some FormattedText to each Ellipse. This i could do by getting the RectangleBounds of the Ellipse.
But following this example:
http://msdn.microsoft.com/en-us/library/bb613560.aspx#FormattedText_Object
I need a DrawingContext to draw the text. But if I don't wantto override onRender, how can I get a DrawingContext?
You can use a DrawingGroup instead. It has an Open method that returns a DrawingContext and you can use that to construct a drawing.
You'll then need to arrange for that drawing to appear in the UI somehow. The easiest way is to wrap it in a DrawingBrush and use that to paint some existing element in the UI. For example, if you've got an ellipse called myEllipse, this will set its Fill property to be a DrawingBrush based on a DrawingGroup that contains a single bit of FormattedText:
var drawing = new DrawingGroup();
using (var context = drawing.Open())
{
var text = new FormattedText("This is some text",
CultureInfo.CurrentCulture,
FlowDirection.LeftToRight,
new Typeface("Calibri"),
30,
Brushes.Green);
context.DrawText(text, new Point(0, 0));
}
var db = new DrawingBrush(drawing);
db.Stretch = Stretch.None;
myEllipse.Fill = db;
If you've already filled the Ellipse with something else, you have two choices. You could either add more content to this drawing - you can make as many calls into the context as you like. For example, if I add this immediately before the call to context.DrawText:
context.DrawRectangle(Brushes.Cyan, null, new Rect(0, 0, 300, 100));
I'll get a cyan background behind the text. (You'd need to adjust the coordinates in these examples to suit your layout, of course.)
But it's probably simpler to add an extra element to host the drawing, rather than trying to piggy-back it into an element that's there to do something else. So you could put a Rectangle element positioned directly over the Ellipse, and use a DrawingBrush such as this as the Fill for that Rectangle. The Rectangle won't actually look rectangular, because this DrawingBrush doesn't paint in its whole area. So the effect will look much the same as it would if you'd layered a TextBlock over an Ellipse.
Related
Because a normal TreeView doesn't fit my needs, I created my own TreeView, inherit from TreeView and Draw lines between my TreeViewItems. Something like this
So far so good, but I would like to ReDraw (Remove add lines) after the tree has been built and drawn. Currently I do everything in the OnRender method, which already provides the DrawingContext to draw lines.
//Point connections from the parent to the childs.
Point parentStart = parentCenter;
Point parentEnd = new Point(parentCenter.X, middleParentChild);
Point childEnd = new Point(childCenter.X, middleParentChild);
Point childStart = childCenter;
drawingContext.DrawLine(Pen, parentStart, parentEnd);
drawingContext.DrawLine(Pen, parentEnd, childEnd);
drawingContext.DrawLine(Pen, childEnd, childStart);
//recursivly do this for all children
DrawConnections(Pen, drawingContext, item);
But I have no access to DrawingContext after the control has been rendered once. Saved in a lokal variable, I am not able to remove already drawn shapes nor redraw anything, because the DrawingContext is already disposed.
You can use YourTreeView.InvalidateVisual() to redraw your tree.
I want to draw a rectangle on a canvas in WPF. For drawing a line I can do this:
line.X1 = ls.P0.X;
line.Y1 = ls.P0.Y;
line.X2 = ls.P1.X;
line.Y2 = ls.P1.Y;
MyCanvas.Children.Add(line);
...in other words the location is a property of the line itself. I want to draw a rectangle the same way, i.e., assign its coordinates and add it to my canvas. But the examples I've seen online so far seem to look like this:
rect = new Rectangle
{
Stroke = Brushes.LightBlue,
StrokeThickness = 2
};
Canvas.SetLeft(rect,startPoint.X);
Canvas.SetTop(rect,startPoint.X);
canvas.Children.Add(rect);
...in other words it doesn't look like the rectangle has an inherent location, but instead its location is set by calling a method of Canvas. Is this true - Lines have inherent coordinates but Rectangles do not? Is there any way to have a rectangle in WPF with an inherent location, like a line, or do I have to roll my own (using lines)?
You could use a Path control with a RectangleGeometry like this:
var rect = new Path
{
Data = new RectangleGeometry(new Rect(x, y, width, height)),
Stroke = Brushes.LightBlue,
StrokeThickness = 2
};
canvas.Children.Add(rect);
...in other words it doesn't look like the rectangle has an inherent location, but instead its location is set by calling a method of Canvas. Is this true - Lines have inherent coordinates but Rectangles do not?
Locations in WPF are relative, which begs the question: coordinates relative to what?
Line, Rectangle, and Path all inherit from Shape, and in the case of any Shape object, the coordinates of the defining geometry are relative to the top-left corner of the Shape itself. Thus, when you create a Line object from (100, 300) to (300, 100), the resulting element is 300x300 points in size, even though the visible line has bounds of 200x200:
In this case, it is unnecessary to place the Line within a Canvas, as you are not using the coordinate system of the Canvas.
Some shapes like Line and Path allow you to place geometry at any "internal" coordinates you like. Others, like Rectangle and Ellipse, always position their defining geometry at (0, 0) internally, forcing you to use other layout properties to position the shapes within the greater scene (e.g., Canvas.Top/Left, Margin, etc.).
In your example, if you were to define a Rectangle of 200x200 points, and use the Canvas attached properties to position the rectangle at (100, 100), the resulting Rectangle element would measure 200x200, while the parent Canvas would measure itself to be at least 300x300, which is arguably more intuitive:
(shading added for clarity)
You are correct in that this is rather inconsistent. You may find it useful to always use layout properties (e.g., Canvas.Left/Top) to position shapes within a scene such that all elements are using the same coordinate system.
I have made a control which inherits TextBox, and I'm trying to give a notebook grid:
I already have the code that will specify where to draw the lines including all of the grids features, but I'm not sure what to draw it to.
I've Googled a lot and searched for a brush that will let me have the same interface as a DrawingContext (so I could call drawingContext.DrawLine() etc.), or something familiar, but I couldn't find any!
So how can I achieve to get my grid background?
P.S I can't create a static bmp file and load it, because the grid color and spacing would most certainly change
You could try using DrawingVisual to get your DrawingContext then create a VisualBrush to assign to your Background. Something like this.
DrawingVisual dv = new DrawingVisual();
DrawingContext dc = dv.RenderOpen();
dc.DrawLine( new Pen(Brushes.Red,5),new Point(0,0),new Point(50,50));
dc.Close();
VisualBrush vb = new VisualBrush(dv);
textBox1.Background = vb;
You can intercept the Paint event for your control. You get a PaintEventArgs argument, which includes a ClipRectangle and a Graphics object.
Check out Control.Paint Event.
Once you have your Graphics object, you can call DrawLine and FillRectangle on it directly.
You're looking to make a custom-drawn control. You'll want to override the OnPaint method of your control class and draw the background in that method. Here's an example on how to do it: http://msdn.microsoft.com/en-us/library/b818z6z6(v=vs.90).aspx
To draw your background, grab the drawing context and draw your background after first calling the base OnPaint method:
protected override void OnPaint(PaintEventArgs pe)
{
// Call the OnPaint method of the base class.
base.OnPaint(pe);
// Declare and instantiate a new pen.
System.Drawing.Pen myPen = new System.Drawing.Pen(Color.Aqua);
// Draw an aqua rectangle in the rectangle represented by the control.
pe.Graphics.DrawRectangle(myPen, new Rectangle(this.Location, this.Size));
}
EDIT:
Since you're using WPF, you can take a look here to see a full example on custom designs: WPF .NET 3.5 Drawing Customized Controls and Custom UI Elements
How do put rectangular box around few controls on my winform? (i dont want the grouping thing).
If you don't want to use a GroupBox, you can put your controls in a Panel and set its BorderStyle property to BorderStyle.FixedSingle or BorderStyle.Fixed3D.
What's wrong with the GroupBox control? Grouping together a related set of controls is exactly what it's intended for. Your users have seen it in every other application they use, and throughout the Windows shell. They're much more likely to recognize what it means than your own custom-drawn rectangle. Deviating from standard platform conventions is rarely a good idea. I strongly recommend using the GroupBox control, even if it's not exactly the perfect look that you had in mind.
That being said, it's certainly possible to draw your own box around a group of controls on a form. To do so, you'll need to override your form's OnPaint method and write some code to draw a rectangle. Doing it this way gives you complete control over the color of your box, as well as the line thickness.
protected override void OnPaint(System.Windows.Forms.PaintEventArgs e)
{
// Call the base class
base.OnPaint(e);
// Create your drawing pen
using (Pen p = new Pen(SystemColors.WindowText, 2.0))
{
// Calculate the position and dimensions of the box
Rectangle rect = new Rectangle(10, 10, 30, 30);
// Draw the rectangle
e.Graphics.DrawRectangle(p, rect);
}
}
The only thing you'll need to add is the code that calculates the dimensions of your rectangle, relative to the controls you want it to surround. Use the Location property of each control to get this information.
You can put your controls into a panel and set its BorderStyle from None to FixedSingle - it is the easiest way
http://msdn.microsoft.com/en-us/library/cyh3c8h8.aspx
Pen pen = new Pen(Color.FromArgb(255, 0, 0, 0));
e.Graphics.DrawLine(pen, 20, 10, 300, 100);
You can draw lines on a windows form this way. This would hook into the Paint method where e is PaintEventArgs
I have created a UserControl which simply defines a new GraphicsPath and sets that to be the Region of the control. Its primary use is to act as a backdrop for other controls so typically the only thing set on it is BackColor. When I call DrawToBitmap on the control's parent, the child control is drawn as a rectangle rather than getting clipped to region I expected. Am I missing something here?
Standard use of the control does not exhibit this problem.
Also, I need to be able to draw this control to an image while it is not currently shown on screen. I have seen some workarounds would could have worked had I not had this additional requirement.
DrawToBitmap does not honor regions. But Graphics.CopyFromScreen does.
Bitmap bitmap = new Bitmap(this.Width, this.Height);
using (Graphics graphics = Graphics.FromImage(bitmap))
{
graphics.CopyFromScreen(this.Location, new Point(0, 0), this.Size);
}