I need apply a ScaleTransform to some textblocks (flip the text, ScaleY = -1) depending on where the textblock is located in the canvas.
My first action was to use the Canvas.GetLeft()/GetTop() but learned that, unless I had positioned the objects with SetLeft()/SetTop(), those functions will return NaN. This is not my scenario because I am applying a stack of transforms using TransformGroup, then learned that I should use TransformToVisual which will provide the measurement I was looking for.
Then I now have 2 problems:
- The measurement will only be provided after everything is already on screen, but I need the transform already applied before ths screen shows, and have no idea which event I should manually trigger, otherwise the offsets will be 0 even with the transform already applied;
- The offsets are provided as relative values, as per the sample codes over MSDN and S.O., but I’d rather have the absolute positions in the canvas.
See the XAML and C# of a sample program that shows this behavior. The TranslateTransform is applied in the constructor and then I immediately show the offsets, which are 0. Then the button does the same thing but with the screen already up, so the relative offsets are displayed.
XAML
<Window x:Class="Canvas_AbsPosition.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:Canvas_AbsPosition"
mc:Ignorable="d"
Title="MainWindow" Height="600" Width="500">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="500"></RowDefinition>
<RowDefinition Height="5"></RowDefinition>
<RowDefinition Height="25"></RowDefinition>
<RowDefinition Height="25"></RowDefinition>
</Grid.RowDefinitions>
<Canvas Grid.Row="0" x:Name="cv"></Canvas>
<Separator Grid.Row="1"></Separator>
<Button Grid.Row="2" x:Name="bt_translate" Content="Move text to center of canvas" Width="200" Click="bt_translate_Click"></Button>
<Button Grid.Row="3" x:Name="bt_getpos" Content="Get text position in the canvas" Width="200" Click="bt_getpos_Click"></Button>
</Grid>
</Window>
C#
namespace Canvas_AbsPosition
{
public partial class MainWindow : Window
{
TextBlock text = new TextBlock();
public MainWindow()
{
InitializeComponent();
text.Text = "Our TextBlock To Be Moved";
text.FontSize = 20;
text.FontWeight = FontWeights.Bold;
text.Measure(new Size());
text.Arrange(new Rect()); // text.ActualWidth and text.ActualHeight are now available
cv.Width = cv.Height = 500;
cv.Children.Add(text);
cv.Measure(new Size(cv.Width, cv.Height));
cv.Arrange(new Rect(0, 0, cv.DesiredSize.Width, cv.DesiredSize.Height)); // Forces the computation of cv.ActualWidth and cv.ActualHeight
// Positions the text in the center of canvas before the window is shown
text.RenderTransform = new TranslateTransform() { X = (cv.ActualWidth - text.ActualWidth) / 2, Y = (cv.ActualHeight - text.ActualHeight) / 2 };
var offset = cv.TransformToVisual(text).Transform(new Point());
MessageBox.Show("X offset: " + offset.X.ToString() + ", Y offset: " + offset.Y.ToString()); // The offsets will be 0 even though the text is already moved
}
private void bt_translate_Click(object sender, RoutedEventArgs e)
{
text.RenderTransform = new TranslateTransform() { X = (cv.ActualWidth - text.ActualWidth) / 2, Y = (cv.ActualHeight - text.ActualHeight) / 2 };
}
private void bt_getpos_Click(object sender, RoutedEventArgs e)
{
var offset = cv.TransformToVisual(text).Transform(new Point());
MessageBox.Show("X offset: " + offset.X.ToString() + ", Y offset: " + offset.Y.ToString()); // After the window is shown, the offsets are available (relative, not absolute)
}
}
}
How can I address those 2 problems?