resize text to largest possible size to fit a drawing rect with wraping

Răspuns resize text to largest possible size to fit a drawing rect with wraping

  • 9 iulie 2012 23:05
     
      Are cod
    there are many similar questions asked to this but no good answer? i use context drawing to draw input text on a certain rect size "720,576", now i need to fit the whole text in that to the maximum font size, while maintaining the rows count?

    i tried to create an equation to calculate that but no use. i even tried to loop size till text is clipped but i couldn't test that condition, and i searched for a week to see something similar but no avail!

    lastly i tried to use a view box which is near what i want but it wont let the textblock inside to multiline as it always re-size the width contain all text in one line.

    here is what i got:
    <Window x:Class="MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="MainWindow" Height="720" Width="576">
    <Viewbox >
        <TextBlock TextWrapping="Wrap" HorizontalAlignment="Center" VerticalAlignment="Center Text="how are you doining how are you doining how are you doining how are you doining how are you doining how are you doining how are you doining how are you doining how are you doining" " />
    </Viewbox>

    any help would be appreciated, thanks.


Toate mesajele

  • 11 iulie 2012 08:05
    Moderator
     
     

    Hi Yousef Harfoush,

    In WPF, I think the only method to scale the font size is ViewBox, however, if you put a TextBlock into ViewBox, the TextBlock will lose the TextWrapping feature, this is a ViewBox default behavior, we could not change or workaround it, could you use multi TextBlock to show your text to achieve the text wrapping behavior, I think it is the only method.

    Additional, in silverlight, there is an auto scaling textBlock

    it can work in WPF, however, there are some incorrect scale, I think you could also refer to this sample to complete a WPF one.

    best regards,


    Sheldon _Xiao[MSFT]
    MSDN Community Support | Feedback to us
    Microsoft
    Please remember to mark the replies as answers if they help and unmark them if they provide no help.


  • 11 iulie 2012 15:46
     
     

    @Sheldon _Xiao:

    thanks, an interesting class there, while i try to do it in code and without controls i converted it to wpf vb and made a testing project:

    http://dl.dropbox.com/u/33417300/AutoScalingTextBlock.7z

    but it doesn't scale as the silverlight one! would appreciate if you could help.

    one other thing is the license (which is MIT), but i figured if i take the measure function and rewrite it in my code will i still apply to the LICENSE?

  • 12 iulie 2012 02:29
    Moderator
     
     

    Hi Yousef Harfoush,

    AutoScalingTextBlock is a direction, you could could copy and use it, you could based on that idea to write your own one, I think i could provide you direction instead of complete project.

    On the other hand, the method which scale TextBlock in WPF is ViewBox, there is no other build in method.

    Best regards,


    Sheldon _Xiao[MSFT]
    MSDN Community Support | Feedback to us
    Microsoft
    Please remember to mark the replies as answers if they help and unmark them if they provide no help.

  • 12 iulie 2012 06:29
     
      Are cod

    You can do something like this as well...

    <Grid>
        <Viewbox>
            <Grid Width="100" Height="100">
                <TextBlock TextWrapping="Wrap" Text="Some really really really really really long text"></TextBlock>
            </Grid>
        </Viewbox>
    </Grid>

    Regards,

    Matt

  • 15 iulie 2012 04:29
     
      Are cod

    ok, guys thanks for answering:

    i got a method works almost as i wanted, i use it to calculate the font size:

            Dim fText As New FormattedText(printText, New CultureInfo("en-us"), tbMSG.FlowDirection,
                New Typeface(tbMSG.FontFamily, tbMSG.FontStyle, tbMSG.FontWeight, tbMSG.FontStretch),
                tbMSG.FontSize, tbMSG.Foreground)
    
                fText.SetFontSize(1)
                fText.MaxTextWidth = 720
                fText.Trimming = 0
                fText.TextAlignment = TextAlignment.Center
                Dim j As Integer = 1
                Do
                    fText.SetFontSize(j)
                    j += 1
                Loop Until fText.Height > 576

    and then drawtext with dimension 720*576, still i need to control how much line i put the text into.

  • 17 iulie 2012 09:02
    Moderator
     
     

    Hi yousef,

    Have you tried Matt's solution, I think this solution is better.

    As for your last concern, you could get the height of your concern, and then you could get the height of one line based on fontsize, then you will know how much lines.

    Best regards,


    Sheldon _Xiao[MSFT]
    MSDN Community Support | Feedback to us
    Microsoft
    Please remember to mark the replies as answers if they help and unmark them if they provide no help.

  • 17 iulie 2012 16:48
     
     

    You can use FormattedText class to control all aspects of a text's drawing size I would think you can do your own calculations on how it would fit in the rect.  I would check out that class it's very powerful.


    Michael Stacie

  • 17 iulie 2012 22:51
     
     

    @sheldon xiao: yes i tried Matt's solution, but it doesn't scale the text to fill the area (which is the main function i want)

    @michael stacie: i already played with this class, i checked every property and function in it, but with no result

    actually my function above give very good results, try attache it to text box and edit the text and you'll find it scales down and up properly, the only thing i want now is to control how many line to display.

  • 18 iulie 2012 02:56
     
     Răspuns Are cod

    Hm, I knocked this together. Should at least give you an idea. Apologies for the poor coding, I don't have a lot of time today so just knocked it out AQAP.

    public class ScaledTextBlock : Canvas
    {
        public string Text { get; set; }
            
        protected override void OnRender(DrawingContext dc)
        {
            double fontSize = 0;
            FormattedText text = null;
               
            do
            {
                string alteredText = Text;
    
                fontSize++;
                    
                text = new FormattedText(alteredText, new CultureInfo("en-us"), FlowDirection.LeftToRight, new Typeface("Arial"), fontSize, Brushes.Black);
    
                while (text.Width > this.Width)
                {                   
                    string line = GetMaximumTextForWidth(alteredText, this.Width, fontSize);
    
                    alteredText = string.Format("{0}{1}{2}", line, Environment.NewLine, alteredText.Substring(line.Length));
    
                    text = new FormattedText(alteredText, new CultureInfo("en-us"), FlowDirection.LeftToRight, new Typeface("Arial"), fontSize, Brushes.Black);
                }
    
            } while (text.Height < this.Height);
    
            dc.DrawText(text, new Point(0, 0));
        }
    
    
        string GetMaximumTextForWidth(string s, double width, double fontSize)
        {
            for (int n = s.Length; n >= 0 ; n--)
            {
         
                FormattedText text = new FormattedText(s.Substring(0, n), new CultureInfo("en-us"), FlowDirection.LeftToRight, new Typeface("Arial"), fontSize, Brushes.Black);
    
                if (text.Width < width)
                {
                    return s.Substring(0, n);
                }
            }
    
            return s;
        }   
    }

    USAGE

    ScaledTextBlock scaled = new ScaledTextBlock();
    scaled.Text = "Split this onto a couple of lines";
    scaled.Width = 200;
    scaled.Height = 200;
    
    border.Child = scaled;

    Warm regards,

    Matt


  • 18 iulie 2012 15:49
     
     

    @matt searles:thanks for this class you wrote, i already attempted to something like it but with no use.

    i compiled the class into a project and tested the code, it scales ok, but there are problems:

    1.the result words are wrapped, see here:

    2.very slow (it doesn't really matter too much)

    3.no line count controlling

    thanks for helping, here is a ready project if you want to test, and sorry for converting it to vb:

    http://dl.dropbox.com/u/33417300/ScaledTextBlock.7z


  • 19 iulie 2012 07:13
     
      Are cod

    Still pretty slow but.. hows this?

    Not sure about 3 though... how do you mean line count controlling? Ie, Min and Max?

    public class AutoScaleTextBlock : Canvas
    {
        public string Text
        {
            get { return (string)GetValue(TextProperty); }
            set 
            { 
                SetValue(TextProperty, value); 
    
                InvalidateVisual();
            }
        }
    
        // Using a DependencyProperty as the backing store for Text.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty TextProperty =
            DependencyProperty.Register("Text", typeof(string), typeof(AutoScaleTextBlock), new UIPropertyMetadata(""));
    
        public FontFamily Font
        {
            get { return (FontFamily)GetValue(FontProperty); }
            set { SetValue(FontProperty, value); }
        }
    
        // Using a DependencyProperty as the backing store for Font.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty FontProperty =
            DependencyProperty.Register("Font", typeof(FontFamily), typeof(AutoScaleTextBlock), new UIPropertyMetadata(new FontFamily("Tahoma")));
    
        public FontWeight FontWeight
        {
            get { return (FontWeight)GetValue(FontWeightProperty); }
            set { SetValue(FontWeightProperty, value); }
        }
    
        // Using a DependencyProperty as the backing store for FontWeight.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty FontWeightProperty =
            DependencyProperty.Register("FontWeight", typeof(FontWeight), typeof(AutoScaleTextBlock), new UIPropertyMetadata(FontWeights.Normal));
    
        public FontStyle FontStyle
        {
            get { return (FontStyle)GetValue(FontStyleProperty); }
            set { SetValue(FontStyleProperty, value); }
        }
    
        // Using a DependencyProperty as the backing store for FontStyle.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty FontStyleProperty =
            DependencyProperty.Register("FontStyle", typeof(FontStyle), typeof(AutoScaleTextBlock), new UIPropertyMetadata(FontStyles.Normal));
    
        public FontStretch FontStretch
        {
            get { return (FontStretch)GetValue(FontStretchProperty); }
            set { SetValue(FontStretchProperty, value); }
        }
    
        // Using a DependencyProperty as the backing store for FontStretch.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty FontStretchProperty =
            DependencyProperty.Register("FontStretch", typeof(FontStretch), typeof(AutoScaleTextBlock), new UIPropertyMetadata(FontStretches.Normal));
    
    
    
        public Brush Foreground
        {
            get { return (Brush)GetValue(ForegroundProperty); }
            set { SetValue(ForegroundProperty, value); }
        }
    
        // Using a DependencyProperty as the backing store for Foreground.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty ForegroundProperty =
            DependencyProperty.Register("Foreground", typeof(Brush), typeof(AutoScaleTextBlock), new UIPropertyMetadata(Brushes.Black));
    
            
        protected override void OnRender(DrawingContext dc)
        {
            double fontSize = 72;
            FormattedText text = null;
            string alteredText;
    
            do
            {
                alteredText = Text;
    
                fontSize--;
    
                text = new FormattedText(alteredText, new CultureInfo("en-us"), FlowDirection.LeftToRight, new Typeface(Font, FontStyle, FontWeight, FontStretch), fontSize, Foreground);
    
                while (text.Width > this.Width)
                {
                    string line = GetMaximumTextForWidth(alteredText, this.Width, fontSize);
    
                    if (line.Length == 0) break;
                    alteredText = string.Format("{0}{1}{2}", line, Environment.NewLine, alteredText.Substring(line.Length));
                    alteredText = alteredText.Replace(Environment.NewLine + Environment.NewLine, Environment.NewLine);
                    alteredText = alteredText.Replace(Environment.NewLine + " ", Environment.NewLine);
    
                    text = new FormattedText(alteredText, new CultureInfo("en-us"), FlowDirection.LeftToRight, new Typeface(Font, FontStyle, FontWeight, FontStretch), fontSize, Foreground);                    
    
                    if (alteredText.Contains(Environment.NewLine + Environment.NewLine))
                    {
                        break;
                    }
                }
    
            } while (text.Height > this.Height || text.Width > this.Width);
    
            dc.DrawText(text, new Point(0, 0));
        }
    
        string GetMaximumTextForWidth(string s, double width, double fontSize)
        {
            for (int n = s.Length; n >= 0 ; n--)
            {
                FormattedText text = new FormattedText(s.Substring(0, n), new CultureInfo("en-us"), FlowDirection.LeftToRight, new Typeface(Font, FontStyle, FontWeight, FontStretch), fontSize, Foreground);                    
    
                if (text.Width < width)
                {
                    // roll back to next space
                    while (s[n] != ' ')
                    {
                        n--;
    
                        if (n == 0) break;
                    }
                    return s.Substring(0, n);
                }
            }
    
            return s;
        }    
    }
    

  • 20 iulie 2012 02:23
     
     

    @matt searles: now issue 1 is solved, thanks.

    about line controlling, i mean the best fit for the text in n lines to the width, in this case we ignore the height fit test, just the width is essential, well i suppose this to be easy, i should test when the full width of the text is half the scaled text, or am i on the wrong road?

    i'm trying to tackle this now, thanks for providing this class.

  • 20 iulie 2012 07:05
     
     Răspuns
    Oh, right, well that should be easy enough. I mean, in the above code, define a DesiredRows property, in the loop count how many Environment.NewLine are in the string, and exit the loop if there's DesiredRows of them.
  • 21 iulie 2012 14:25
     
      Are cod

    ok, thats it, now it works perfectly, i added this line to count for the rows limit:

                    If alteredText.Split(Environment.NewLine).Length = MaxRows Then
                        Exit Do
                    End If
    thanks for helping me out.
  • 24 iulie 2012 02:27
     
     
    Welcome, and good luck with the project :)