locked
How can I compute a TextBlock's ActualWidth in CodeBehind? RRS feed

  • Question

  • Hello all,

     

    I'm trying to position a TextBlock on a Canvas and I need to know the width of the TextBlock.

    I thought I could coerce the Measure pass of the layout engine to give me the ActualWidth.

    My test code is:

     

    using System;

    using System.Collections.Generic;

    using System.Windows;

    using System.Windows.Controls;

    using System.Windows.Media;

    using System.Windows.Shapes;

    using System.Diagnostics;

    namespace Test {

      public partial class Window1 : Window {

        Canvas canvasObj = new Canvas();

        public Window1() {

          InitializeComponent();

          InitChart();

        }

     

        private void InitChart() {

          this.Content = canvasObj;

          Text(150.0, 175.0, "Hello World", Colors.Red);

        }

     

        private void Text(double x, double y, string text, Color color) {

          TextBlock textBlock = new TextBlock();

          textBlock.Text = text;

          textBlock.Foreground = new SolidColorBrush(color);

          Canvas.SetLeft(textBlock, x);

          Canvas.SetTop(textBlock, y);

          textBlock.Measure(new Size(double.PositiveInfinity,

                                     double.PositiveInfinity));

          double w = textBlock.ActualWidth;

          canvasObj.Children.Add(textBlock);

          Debug.WriteLine("w: " + w);

        }

      }

    }

     

    The Debug printout shows that "w" is "0".

     

    Any help will be greatly appreciated.

     

    Charles

    Friday, March 28, 2008 2:13 AM

Answers

  • I have a possible solution.  The code below draws a vertical line with text centered below the line.

    To accomplish this, the width of the TextBlock must be found so that the x-coordinate can be adjusted.

     

    using System;

    using System.Collections.Generic;

    using System.Windows;

    using System.Windows.Controls;

    using System.Windows.Media;

    using System.Windows.Shapes;

    using System.Diagnostics;

    namespace Test {

      public partial class Window1 : Window {

        Canvas canvasObj = new Canvas();

        double canvasHeight, canvasWidth;

        public Window1() {

          InitializeComponent();

        }

        protected override void OnRenderSizeChanged(SizeChangedInfo sizeInfo) {

          canvasWidth = Convert.ToInt32(this.Width);

          canvasHeight = Convert.ToInt32(this.Height);

          InitChart();

        }

        private void InitChart() {

          this.Content = canvasObj;

          Line(150.0, 170.0, 150.0, 150.0, Colors.DarkBlue);

          Text(150.0, 145.0, "25.75", Colors.Red);

        }

        public double GetChartHeight() {

          return canvasHeight;

        }

        // Line in Cartesian coordinate space.

        private void Line(double x1, double y1, double x2, double y2, Color color) {

          double height = (float) GetChartHeight();

          float X1 = (float) (x1);

          float X2 = (float) (x2);

          float Y1 = (float) (height - y1);

          float Y2 = (float) (height - y2);

          Line lineObj = new Line();

          lineObj.Stroke = new SolidColorBrush(color);

          lineObj.X1 = X1;

          lineObj.X2 = X2;

          lineObj.Y1 = Y1;

          lineObj.Y2 = Y2;

          lineObj.StrokeThickness = 1;

          canvasObj.Children.Add(lineObj);

        }

        // Draw Text at an X,Y location in Cartesian coordinate space.

        private void Text(double x, double y, string text, Color color) {

          float X = (float) x;

          float Y = (float) (GetChartHeight() - y);

          TextBlock textBlock = new TextBlock();

          textBlock.FontSize = 10.0;

          textBlock.FontFamily = new FontFamily("Arial");

          textBlock.Text = text;

          text Block.Foreground = new SolidColorBrush(color);

          Rect finalRect = new Rect();

          textBlock.Arrange(finalRect);

          double w = textBlock.ActualWidth;

          // For this example, the text is centered below X,Y

          X -= (float) ( w/2.0 );

          Canvas.SetLeft(textBlock, X);

          Canvas.SetTop(textBlock, Y);

          canvasObj.Children.Add(textBlock);

        }

      }

    }

     

    Please comment on this approach.

     

    Charles

     

    Friday, March 28, 2008 6:44 PM

All replies

  • You cannot measure any element without connecting it to the visual tree.

    element.Measure() is supposed to call from the parent MeasureOverride() method.

     

    After element.Measure() is called the calculated size is stored in element.DesiredSize

     

    ActualWidth/Height is calculated after the Arrage pass so it is too early to query for that.

     

    Measure and Arrange on the visual tree happens async when layout needs update.

    You can attach a Loaded event which fires after the element layout is completed the first time.

    Use textBlock.Loaded += ... and in your handler you can check the DesiredSize, ActualWidth/Height.

     

    What is your scenario? Why do you need to measure the TextBox. There are lots of panel that do various layouts (I almost never use Canvas).

    Friday, March 28, 2008 6:59 AM
  • You asked: "What is your scenario?"

    The answer is that I'm trying to convert 2D charting routines from GDI+ to WPF.

     

    Do you have any code samples, or article references on how to position Text on a panel?

    Why do I need ActualWidth?  Because on a chart I may need to center the Text relative to a line.

    This is usually done when labeling the horizontal axis of a chart.

     

    Bye the way, it was suggested to me to use a TextBlock not a TextBox.

     

    Charles

    Friday, March 28, 2008 5:26 PM
  • I have a possible solution.  The code below draws a vertical line with text centered below the line.

    To accomplish this, the width of the TextBlock must be found so that the x-coordinate can be adjusted.

     

    using System;

    using System.Collections.Generic;

    using System.Windows;

    using System.Windows.Controls;

    using System.Windows.Media;

    using System.Windows.Shapes;

    using System.Diagnostics;

    namespace Test {

      public partial class Window1 : Window {

        Canvas canvasObj = new Canvas();

        double canvasHeight, canvasWidth;

        public Window1() {

          InitializeComponent();

        }

        protected override void OnRenderSizeChanged(SizeChangedInfo sizeInfo) {

          canvasWidth = Convert.ToInt32(this.Width);

          canvasHeight = Convert.ToInt32(this.Height);

          InitChart();

        }

        private void InitChart() {

          this.Content = canvasObj;

          Line(150.0, 170.0, 150.0, 150.0, Colors.DarkBlue);

          Text(150.0, 145.0, "25.75", Colors.Red);

        }

        public double GetChartHeight() {

          return canvasHeight;

        }

        // Line in Cartesian coordinate space.

        private void Line(double x1, double y1, double x2, double y2, Color color) {

          double height = (float) GetChartHeight();

          float X1 = (float) (x1);

          float X2 = (float) (x2);

          float Y1 = (float) (height - y1);

          float Y2 = (float) (height - y2);

          Line lineObj = new Line();

          lineObj.Stroke = new SolidColorBrush(color);

          lineObj.X1 = X1;

          lineObj.X2 = X2;

          lineObj.Y1 = Y1;

          lineObj.Y2 = Y2;

          lineObj.StrokeThickness = 1;

          canvasObj.Children.Add(lineObj);

        }

        // Draw Text at an X,Y location in Cartesian coordinate space.

        private void Text(double x, double y, string text, Color color) {

          float X = (float) x;

          float Y = (float) (GetChartHeight() - y);

          TextBlock textBlock = new TextBlock();

          textBlock.FontSize = 10.0;

          textBlock.FontFamily = new FontFamily("Arial");

          textBlock.Text = text;

          text Block.Foreground = new SolidColorBrush(color);

          Rect finalRect = new Rect();

          textBlock.Arrange(finalRect);

          double w = textBlock.ActualWidth;

          // For this example, the text is centered below X,Y

          X -= (float) ( w/2.0 );

          Canvas.SetLeft(textBlock, X);

          Canvas.SetTop(textBlock, Y);

          canvasObj.Children.Add(textBlock);

        }

      }

    }

     

    Please comment on this approach.

     

    Charles

     

    Friday, March 28, 2008 6:44 PM