locked
How to position a textBlock in the Canvas RRS feed

  • Question

  • Hi,
     
    I am having trouble trying to exactly position a text block on a canvas.

    I have a text block, and I want to position it on canvas so that it's right hand side align with certain x value.

    I first tried using Canvas.SetLeft, But it only line up the left side of the text block.

    So I believe that I need to get the ActualWidth of the text block, and if I want to line up the right side of the block with 100, I can do canvas.setLeft(Label, 100 - Label.ActualWidth).

    But in order to get the layout pass to calculate the AcutalWidth, my understanding is you actually have to put the control on the canvas, let layout pass do the trick, and get the value, otherwise it will be 0.

    Here is my dilemma, in order to position the control on canvas, I need to get it's size; but in order to get it's size, you have to put it on the canvas.

    On the other hand, If I know a text block's font, size, and what characters are there. It should be pretty reasonable to be able to calculate what the size of the control would be before you render it. Is that correct? Why do you have to actually invoke the layout process?

    B
    Tuesday, September 23, 2008 2:04 PM

Answers

All replies

  • Have you tried Canvas.SetRight(..)?

    Note that you cannot use both Canvas.SetLeft and Canvas.SetRight at the same time to get any kind of 'Anchoring' behavior as you did in Windows Forms.
    • Proposed as answer by DutchMarcel Wednesday, September 24, 2008 9:19 AM
    Tuesday, September 23, 2008 2:34 PM
  • Hi,

    Thanks for helping.

    Canvas.SetRight only line up the left side of the control with the right side of the canvas, thus the size of the text block itself is not needed.

    I think my case is that I need to know the size of the block itself and all trouble starts from there.

    Thanks.

    B
    Tuesday, September 23, 2008 2:41 PM
  • What you are trying to do is called Panel.

    You have two options:
    1. Use out of the box panel such as DockPanel or a Grid:

    <Grid> 
       <TextBlock Text="Text" 
                  HorizontalAlignment="Right" 
                  VerticalAlignment="Bottom" 
                  Margin="0,0,25,25" /> 
    </Grid> 

    2. Create a custom Panel.

    NOTE: You can always call Measure and Arrange methods directly on your element. This will set its ActualHeight and ActualWidth properties, and is usually done by the Panel MeasureOverride and ArrangeOverride methods.

    Regards,
    Tomer
    Tomer Shamam | .NET Technologies Expert | Smart Client Leader | Sela Group
    • Proposed as answer by תומר שמם Tuesday, September 23, 2008 5:50 PM
    Tuesday, September 23, 2008 5:49 PM
  • Thanks. I will try it out and let you know.

    B
    Tuesday, September 23, 2008 5:55 PM
  •  If you want to use a canvas you can still do it using a converter which will convert the width of the textblock to a position.

    <Window x:Class="PositionTest.Window1" 
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
        xmlns:local="clr-namespace:PositionTest" 
        Height="300" Width="300">  
          
        <Window.Resources> 
            <local:AdjustPositionConverter x:Key="AdjustPositionConverter" /> 
        </Window.Resources> 
          
        <Canvas> 
            <TextBlock Text="Hello" Canvas.Left="{Binding Path=ActualWidth, RelativeSource={x:Static RelativeSource.Self}, Converter={StaticResource AdjustPositionConverter}, ConverterParameter=100}" Background="Yellow" /> 
            <Line X1="100" Y1="0" X2="100" Y2="100" Stroke="Black"/>  
        </Canvas> 
    </Window> 
     

    using System;  
    using System.Windows.Data;  
    using System.Globalization;  
     
    namespace PositionTest  
    {  
        class AdjustPositionConverter : IValueConverter  
        {  
            public object Convert(object value, Type targetType, object parameter, CultureInfo culture)  
            {  
                return double.Parse((string)parameter) - (double)value;  
            }  
     
            public object ConvertBack(object v, Type t, object p, CultureInfo c)  
            {   
                throw new NotImplementedException();   
            }  
        }  
    }  
     

    Patric
    Tuesday, September 23, 2008 7:28 PM
  • I believe ToreS gave the right answer. In xaml this puts the right edge of the TextBlock exactly 100 pixels from the right of the Canvas.

    <Page  
      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">  
      <Canvas>    
        <TextBlock Text="Some Text" Canvas.Right="100"/>  
      </Canvas> 
    </Page> 
     

    The TextBlock sizes itself to the size of its content, but the right edge stays at 100 pixels from the right. You can copy/paste this example to Kaxaml or XamlPad and see for yourself. ToreS gave the same answer in C# instead of Xaml.

    hth,
    Marcel
    • Edited by DutchMarcel Wednesday, September 24, 2008 9:20 AM typo
    Wednesday, September 24, 2008 9:18 AM
  •  Yes. SetRight seems to have worked to set the right edge of the text block against the canvas right edgq.

    But Canvas only has setLeft and SetRight. How about if I need to set the center of the textblock along a predefined X location?

    It seems to me that we need something like  

    Canvas.SetLeft(label, XLocation - label.length / 2)

    or

    Canvas.SetRight(label, CanvasWidth - XLocation +label.length / 2)

    So either way, we still would want to be able to calculate the width of the textblock before it is rendered. Right?

    I am still wondering why we cannot using a direct function call to get the length of the sreing, if we know its font, size. Instead we have to render it.

    Patric. Your method seems to imply that we have to first get ActualWidth, by rendering it, and then use the ValueConverter to retrieve this value and readjust the left position of the TextBlock.

    But if I need to do everything in C#, to get ActualWidth would require me to fire away the layout pass already.

    Is that correct?

    So, is there a way that I define a label in C#, define it's font, and then directly calculate how much Device Independence Pixel it will occupy?

    SetRight help. But If I need to place the string so that its center aligh with something, then I would still prefer to know its size.

    Bing
    Wednesday, September 24, 2008 7:14 PM
  • These articles may help you to better understand the layout system that wpf uses:
    http://wpfopoly.blogspot.com/2007/08/measure-and-arrange.html
    http://msdn.microsoft.com/en-us/library/ms745058.aspx

    The point is that your TextBlock will only know its own final size after the Arrange pass of the Canvas it is in. A solution with bindings and converters, like WallStreetProgrammer showed, will update itself during the layout phase and will therefore give you the most precise positioning.

    I think there are ways to calculate the size of a string (use the System.Windows.Media.FormattedText class), but you may have to add a few pixels extra because of paddings and/or margins. 
    http://social.msdn.microsoft.com/forums/en-US/wpf/thread/69d90b25-637b-4ec2-8021-81dbe24b2ef9/

    See which method you like best.

    hth,
    Marcel

    • Marked as answer by Marco Zhou Monday, September 29, 2008 10:37 AM
    Thursday, September 25, 2008 6:59 AM