When working with WPF and highly 2D graphic oriented applications, you will definitely have a requirement to mix the text with graphic.
For example you want to draw a line an write some text on the top of line.
It is not a big science to make this somehow working. However the programming model to create one line and one peace of text are a bit different. This can make your code ugly.
For example, imagine you are drawing a line and wants to put the text at the line. Next code sample shows how to draw the line.
Line line1 = new Line()
line1.X1 = xMin;
line1.Y1 = yMin;
line1.X2 = xMax;
line1.Y2 = yMin;
line1.Stroke = new SolidColorBrush(controller.ViewData.AxisColor);
line1.StrokeThickness = 2;
m_Grid.Children.Add(line1);
Now, it you would like to to have a same code to draw the text. For example:
TextBlock text = new TextBlock(number, xMin, yMin, "Segoi UI", fontSize, color);
m_Grid.Children.Add(text);
Unfortunately this will not work, because there is no Text Control which would work this way. To make it working you could put a canvas-element around parent (in this sample m_Grid) and set properties like Canvas.Left, Canvas.Top etc..This is a nice workaround, but if your graphic elements behave in a dimension which is not a pixel (for example millimeter), such kind of code would be very difficult and probably ugly for implementation at least when you have to rescale form your dimension back to pixels.
Solution
We implemented the class DrawableTextBlock which is common TextBox with few more arguments.
The custom control has to derive from FramworkElement. Adiitionally it has to override VisualChildrenCount and GetVisualChild.
In the constructor we create VisualCollection and put a single element which is drawn text.
/// <summary>
/// Creats the instance of drawable text control.
/// </summary>
/// <param name="text"></param>
/// <param name="x">X-Position of control</param>
// <param name="y">y-Position of control</param>
/// <param name="font">The font family like 'Verdana', 'Segoi UI', etc.</param>
/// <param name="fontSize">The size of the font.</param>
/// <param name="color">The color of the font.</param>
public DrawableTextBlock(string text, double x, double y, string font, double fontSize, Color color)
{
m_Elements = new VisualCollection(this);
m_Elements.Add(createDrawingText(text, x, y, font, fontSize, color));
}
The text is drown as follows.
private Visual createDrawingText(string text, double x, double y, string font, double fontSize, Color color)
{
FormattedText formattedText = new FormattedText(
text,
CultureInfo.GetCultureInfo("en-us"),
FlowDirection.LeftToRight,
new Typeface(font),
fontSize,
Brushes.Black);
DrawingVisual drawingVisual = new DrawingVisual();
using (DrawingContext drawingContext = drawingVisual.RenderOpen())
{
drawingContext.DrawText(formattedText, new Point(x, y));
return drawingVisual;
}
}
Two remaining methods must be overridden to make all working:
/// <summary>
/// Mus be override to provide visuals to be rendered.
/// </summary>
protected override int VisualChildrenCount
{
get { return m_Elements.Count; }
}
/// <summary>
/// Gets the specific visual.
/// </summary>
/// <param name="index"></param>
/// <returns></returns>
protected override Visual GetVisualChild(int i)
{
if (I < 0 || i>= m_Elements.Count)
{
throw new ArgumentOutOfRangeException();
}
return m_Elements;
}
The full sample for this post can be downloaded here.
Posted
Dec 31 2011, 01:31 AM
by
Damir Dobric