serhio
serhio

Reputation: 28586

Adding Text inside a Shape

I have a custom WPF control MyLine that should represent or not some text in its middle.

public class MyLine : Shape
{   
    public double X1, Y1, X2, Y2;
    public bool IsTextDisplayed;
    public string Caption;

    protected override System.Windows.Media.Geometry DefiningGeometry
    {
        get
        {
            var geometryGroup = new GeometryGroup();

            if (IsTextDisplayed)
            {
                // calculate text point
                var midPoint = new Point((X1 + X2) / 2.0, (Y1 + Y2) / 2.0);
                // add a TextBlock with the Caption text in that point
                // ???
            }

            // Add line
            geometryGroup.Children.Add(new LineGeometry(
                new Point(X1, Y1), new Point(X2, Y2)));

            return geometryGroup;

        }
    }
}

How should I add the a TextBlock (or Label) here?

I tried to add a FormattedText inside, but this is NOK, because it draws the text with the line fat brush and is impossible to read something.

EDIT

Adding a visual Child

public MyLine() : base()
{
    textBlock = new System.Windows.Controls.TextBlock();
    textBlock.Visibility = System.Windows.Visibility.Hidden;
    this.AddVisualChild(textBlock);
}

protected override System.Windows.Media.Geometry DefiningGeometry
{
    get
    {
    ...

if (IsTextDisplayed)
{
    var midPoint = new Point((X1 + X2) / 2.0, (Y1 + Y2) / 2.0);
    string text = "some custom text";

    Canvas.SetLeft(textBlock, midPoint.X);
    Canvas.SetBottom(textBlock, midPoint.Y);

    textBlock.Text = text;
    this.textBlock.Visibility = System.Windows.Visibility.Visible;    
}
else 
{
    this.textBlock.Visibility = System.Windows.Visibility.Hidden;
}

I don't see any label... :"/

EDIT2

Adding Adorner

public MyLine() : base()
{
    this.Loaded += new RoutedEventHandler(MyLine_Loaded);
}

void MyLine_Loaded(object sender, RoutedEventArgs e)
{
    AdornerLayer aLayer = AdornerLayer.GetAdornerLayer(this);
    if (aLayer != null)
        aLayer.Add(new TextAdorner(this));        
}

class TextAdorner : Adorner
{
    public TextAdorner(UIElement adornedElement) : base(adornedElement) 
    { }

    protected override void OnRender(DrawingContext drawingContext)
    {
        MyLine segment = (this.AdornedElement as MyLine);            
        if (segment != null && segment.IsLabelUsed)
        {
            Rect segmentBounds = new Rect(segment.DesiredSize);

            FormattedText ft = new FormattedText(
                "654 m", Thread.CurrentThread.CurrentCulture,
                System.Windows.FlowDirection.LeftToRight,
                new Typeface("Arial"), 12, Brushes.White);

            drawingContext.DrawText(ft, segmentBounds.BottomRight);
        }
    }
}

Now, apparently the code never enters in the OnRender adorner method...

Upvotes: 2

Views: 4651

Answers (2)

NVM
NVM

Reputation: 5552

I'd use an adorner to draw the text on top of the line.

http://msdn.microsoft.com/en-us/library/ms746703.aspx

Upvotes: 0

Philip Rieck
Philip Rieck

Reputation: 32578

If you don't want the text displayed in the same brush as the line, you probably don't want to use a Shape as the base class, as the Geometry returned from DefiningGeometry is rendered in one brush. If you really want to use a Shape as the base class, you probably want to add the text as a visual child rather than add it to the existing Geometry.

If you aren't going to be using a lot of these, and can afford a slightly heavy-weight container, I'd suggest simply creating a UserControl based control that contains this Shape you created, and a text element like TextBox on a Canvas (for absolute positioning). Again, this is not going to be a great solution if you have hundreds or thousands of these, but if you only have tens of them, it is probably the easiest and quickest solution.

Upvotes: 2

Related Questions