views:

72

answers:

2

Hi, I have a custom Panel for laying out text. There is a DependancyProperty called "Text" and when that value changes, this piece of code runs:

if( !string.IsNullOrEmpty(Text))
{
    Children.Clear();

    foreach (char ch in Text)
    {
        TextBlock textBlock = new TextBlock();
        textBlock.Text = ch.ToString();
        textBlcok.Foreground = Foreground;

        //The rest of these are DPs in the panel
        textBlock.FontFamily = FontFamily;
        textBlock.FontStyle = FontStyle;     
        textBlock.FontWeight = FontWeight;
        textBlock.FontStretch = FontStretch;
        textBlock.FontSize = FontSize;

        Children.Add(textBlock);
        }
    }
}

Now, with font size of 15 and font Arial, these should be giving me a desired size of around 8 width and 10 height. However, when I do a Measure() and check the desired size, I get 40,18 every time!

So in trying to figure out what could've possibly changed the size, I put this code before and after the Children.Add in the code above:

textBlock.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
Console.WriteLine(ch.ToString() + ": " + textBlock.DesiredSize);

Children.Add(textBlock);

textBlock.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
Console.WriteLine(ch.ToString() + ": " + textBlock.DesiredSize);

What this gave me, was the proper desired size before it's added to the children collection, and a size of 40,18 (regardless of letter) after it's added to the collection.

What is causing this to happen?

Edit: You can find the full source for the control here:

using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using IQ.Touch.Resources.Classes.Helpers;

/* TextOnAPath.cs
 * 
 * A slightly modified version of the control found at
 * http://www.codeproject.com/KB/WPF/TextOnAPath.aspx
 */

namespace IQ.Touch.Resources.Controls
{
    public class TextOnAPath : Panel
    {
        // Fields
        PathFigureHelper pathFigureHelper = new PathFigureHelper();
        Size totalSize;

        // Dependency properties
        public static readonly DependencyProperty TextProperty =
            DependencyProperty.Register("Text",
                typeof(string),
                typeof(TextOnAPath),
                new PropertyMetadata(OnFontPropertyChanged));

        public static readonly DependencyProperty FontFamilyProperty =
            DependencyProperty.Register("FontFamily",
                typeof(FontFamily),
                typeof(TextOnAPath),
                new PropertyMetadata(new FontFamily("Portable User Interface"), OnFontPropertyChanged));

        public static readonly DependencyProperty FontStyleProperty =
            DependencyProperty.Register("FontStyle",
                typeof(FontStyle),
                typeof(TextOnAPath),
                new PropertyMetadata(FontStyles.Normal, OnFontPropertyChanged));

        public static readonly DependencyProperty FontSizeProperty =
            DependencyProperty.Register("FontSize",
                typeof(double),
                typeof(TextOnAPath),
                new PropertyMetadata(12.0, OnFontPropertyChanged));

        public static readonly DependencyProperty FontWeightProperty =
            DependencyProperty.Register("FontWeight",
                typeof(FontWeight),
                typeof(TextOnAPath),
                new PropertyMetadata(FontWeights.Normal, OnFontPropertyChanged));

        public static readonly DependencyProperty FontStretchProperty =
            DependencyProperty.Register("FontStretch",
                typeof(FontStretch),
                typeof(TextOnAPath),
                new PropertyMetadata(FontStretches.Normal, OnFontPropertyChanged));

        public static readonly DependencyProperty ForegroundProperty =
            DependencyProperty.Register("Foreground",
                typeof(Brush),
                typeof(TextOnAPath),
                new PropertyMetadata(new SolidColorBrush(Colors.Black), OnFontPropertyChanged));

        public static readonly DependencyProperty PathFigureProperty =
            DependencyProperty.Register("PathFigure",
                typeof(PathFigure),
                typeof(TextOnAPath),
                new PropertyMetadata(OnPathFigureChanged));

        // Properties
        public string Text
        {
            set { SetValue(TextProperty, value); }
            get { return (string)GetValue(TextProperty); }
        }

        public FontFamily FontFamily
        {
            set { SetValue(FontFamilyProperty, value); }
            get { return (FontFamily)GetValue(FontFamilyProperty); }
        }

        public FontStyle FontStyle
        {
            set { SetValue(FontStyleProperty, value); }
            get { return (FontStyle)GetValue(FontStyleProperty); }
        }

        public double FontSize
        {
            set { SetValue(FontSizeProperty, value); }
            get { return (double)GetValue(FontSizeProperty); }
        }

        public FontWeight FontWeight
        {
            set { SetValue(FontWeightProperty, value); }
            get { return (FontWeight)GetValue(FontWeightProperty); }
        }

        public FontStretch FontStretch
        {
            set { SetValue(FontStretchProperty, value); }
            get { return (FontStretch)GetValue(FontStretchProperty); }
        }

        public Brush Foreground
        {
            set { SetValue(ForegroundProperty, value); }
            get { return (Brush)GetValue(ForegroundProperty); }
        }

        public PathFigure PathFigure
        {
            set { SetValue(PathFigureProperty, value); }
            get { return (PathFigure)GetValue(PathFigureProperty); }
        }

        // Property-changed handlers
        static void OnFontPropertyChanged(DependencyObject obj,
                                          DependencyPropertyChangedEventArgs args)
        {
            (obj as TextOnAPath).OnFontPropertyChanged(args);
        }

        void OnFontPropertyChanged(DependencyPropertyChangedEventArgs args)
        {
            Children.Clear();

            if (String.IsNullOrEmpty(Text))
                return;

            foreach (char ch in Text)
            {
                TextBlock textBlock = new TextBlock();
                textBlock.Text = ch.ToString();
                textBlock.FontFamily = FontFamily;
                textBlock.FontStyle = FontStyle;
                textBlock.FontWeight = FontWeight;
                textBlock.FontStretch = FontStretch;
                textBlock.FontSize = FontSize;
                textBlock.Foreground = Foreground;
                textBlock.HorizontalAlignment = HorizontalAlignment.Center;
                textBlock.VerticalAlignment = VerticalAlignment.Bottom;
                textBlock.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
                Console.WriteLine(ch.ToString() + ": " + textBlock.DesiredSize);
                Children.Add(textBlock);
                textBlock.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
                Console.WriteLine(ch.ToString() + ": " + textBlock.DesiredSize);
            }
            CalculateTransforms();
            InvalidateMeasure();
        }

        static void OnPathFigureChanged(DependencyObject obj,
                                        DependencyPropertyChangedEventArgs args)
        {
            (obj as TextOnAPath).OnPathFigureChanged(args);
        }

        void OnPathFigureChanged(DependencyPropertyChangedEventArgs args)
        {
            pathFigureHelper.SetPathFigure(args.NewValue as PathFigure);
            CalculateTransforms();
            InvalidateMeasure();
        }

        void CalculateTransforms()
        {
            double pathLength = pathFigureHelper.Length;
            double textLength = 0;
            double textDesiredWidth = 9;
            totalSize = new Size();

            foreach (UIElement child in Children)
            {
                child.Measure(new Size(Double.PositiveInfinity,
                                       Double.PositiveInfinity));
                textLength += child.DesiredSize.Width;
            }
            //textLength = Children.Count * textDesiredWidth;

            if (pathLength == 0 || textLength == 0)
                return;

            //double scalingFactor = pathLength / textLength;
            double baseline = FontSize;     // * FontFamily.Baseline;
            double progress = 0;

            if (textLength <= pathLength)
            {
                progress = ((pathLength - textLength) / 2) / pathLength;
            }

            foreach (UIElement child in Children)
            {
                double width = child.DesiredSize.Width;
                //double width = textDesiredWidth;
                progress += width / 2 / pathLength;
                Point point, tangent;

                pathFigureHelper.GetPointAtFractionLength(progress,
                                                out point, out tangent);

                TransformGroup transformGroup = new TransformGroup();

                //ScaleTransform scaleTransform = new ScaleTransform();
                //scaleTransform.ScaleX = scalingFactor;
                //scaleTransform.ScaleY = scalingFactor;
                //transformGroup.Children.Add(scaleTransform);

                RotateTransform rotateTransform = new RotateTransform();
                rotateTransform.Angle = Math.Atan2(tangent.Y, tangent.X) * 180 / Math.PI;
                rotateTransform.CenterX = width / 2;
                rotateTransform.CenterY = baseline;
                transformGroup.Children.Add(rotateTransform);

                TranslateTransform translateTransform = new TranslateTransform();
                translateTransform.X = point.X - width / 2;
                translateTransform.Y = point.Y - baseline;
                transformGroup.Children.Add(translateTransform);

                child.RenderTransform = transformGroup;

                BumpUpTotalSize(transformGroup.Value, new Point(0, 0));
                BumpUpTotalSize(transformGroup.Value, new Point(0, child.DesiredSize.Height));
                BumpUpTotalSize(transformGroup.Value, new Point(child.DesiredSize.Width, 0));
                BumpUpTotalSize(transformGroup.Value, new Point(child.DesiredSize.Width, child.DesiredSize.Height));

                progress += width / 2 / pathLength;
            }

            Point endPoint, endTangent;
            pathFigureHelper.GetPointAtFractionLength(1, out endPoint, out endTangent);
            totalSize.Width = Math.Max(totalSize.Width, endPoint.X);
        }

        void BumpUpTotalSize(Matrix matrix, Point point)
        {
            point = matrix.Transform(point);
            totalSize.Width = Math.Max(totalSize.Width, point.X);
            totalSize.Height = Math.Max(totalSize.Height, point.Y);
        }

        protected override Size MeasureOverride(Size availableSize)
        {
            foreach (UIElement child in Children)
                child.Measure(new Size(Double.PositiveInfinity, Double.PositiveInfinity));

            // return the size calculated during CalculateTransforms
            return totalSize;
        }

        protected override Size ArrangeOverride(Size finalSize)
        {
            foreach (UIElement child in Children)
                child.Arrange(new Rect(new Point(0, 0), child.DesiredSize));

            return finalSize;
        }
    }
}
A: 

You should horizontally align you TextBox to the left, right, or center. It is aligned as strech per default, thus expandig it to the available area.

Edit

Just testet it with a little class:

public class TextOnAPath : Panel
{
    public TextOnAPath() {
        var textBlock = new TextBlock();
        textBlock.Text = "Test";
        textBlock.Background = Brushes.Blue;
        textBlock.VerticalAlignment = System.Windows.VerticalAlignment.Top;
        textBlock.HorizontalAlignment = System.Windows.HorizontalAlignment.Left;
        this.Children.Add(textBlock);

        this.Background = Brushes.Gray;
    }

    protected override Size MeasureOverride(Size availableSize) {
        return availableSize;
    }

    protected override Size ArrangeOverride(Size finalSize) {
        foreach (UIElement child in this.Children)
            child.Arrange(new Rect(0, 0, finalSize.Width, finalSize.Height));
        return finalSize;
    }
}

Removing the alignments takes up all available space ... could it be that your CalculateTransforms method causes the effect? Especially, as the outcome is then used in the MeasureOverride method.

Obalix
Unfortunately, that didn't fix the problem :(
Willson Haw
After the Children.Add() call is when the size gets changed, so it doesn't even get to the CalculateTransforms or MeasureOverride methods.
Willson Haw
A: 

I figured out the problem, it turns out that the problem was not related to the control at all, but somewhere in the code the default template for a textblock got changed, and MinWidth and MinHeight got set to 40,18 for some reason... Now to find the suspect and to yell at them.

Thanks guys

Willson Haw