#1,058 – Translation Makes No Sense within Layout Transforms

layout transform is a transformation applied before a container’s child elements are laid out.

Because the container will position child elements after doing the layout transform, it doesn’t make sense to include a translation as part of a layout transform.  The container positions its child elements based on its layout rules, deciding on the X and Y position of each element after you’ve done the layout transform.

If you want to translate an element relative to where the container wants to put it, you can do that by using the Margin property of the element or by applying a render transform.

Below, the second label uses a translate transform that is ignored.  The third label does the translation in the render transform.

    <StackPanel>
        <Label Content="Flavius" Background="AliceBlue"
               HorizontalAlignment="Left"/>
        <Label Content="Lucius" Background="Bisque"
               HorizontalAlignment="Left">
            <Label.LayoutTransform>
                <TranslateTransform X="100"/>
            </Label.LayoutTransform>
        </Label>
        <Label Content="Constantine" Background="Gainsboro"
               HorizontalAlignment="Left">
            <Label.RenderTransform>
                <TranslateTransform X="100"/>
            </Label.RenderTransform>
        </Label>
    </StackPanel>

1058-001

Advertisement

#774 – Translate Transforms

You can use a translation transform to translate, or move, a user interface element in the X or Y dimensions.  You specify translation values separately in both X and Y dimensions, expressed in device independent units.

You specify translation using a TranslateTransform element, setting values for the X and Y properties.  Both properties default to a value of 0.

LayoutTransform will ignore translation transforms (since the element will be moved around during the layout process anyway).  So you only use translation transforms with a RenderTransform.

Here’s an example that uses a couple of Sliders to dynamically change the X and Y values of a TranslateTransform.

    <StackPanel Orientation="Vertical">
        <TextBlock Text="Jack Kerouac" FontWeight="Bold" HorizontalAlignment="Center"
                   Padding="10,5" Background="PaleGoldenrod" Margin="5">
            <TextBlock.RenderTransform>
                <TranslateTransform  X="{Binding ElementName=sliX, Path=Value}" Y="{Binding ElementName=sliY, Path=Value}"/>
            </TextBlock.RenderTransform>
        </TextBlock>
        <TextBlock Text="Born 12 Mar, 1922" HorizontalAlignment="Center"
                   Padding="10,5" Background="Thistle"/>

        <Slider Name="sliX" Minimum="0" Maximum="300" Margin="10"/>
        <Slider Name="sliY" Minimum="0" Maximum="300" Margin="10"/>
    </StackPanel>

774-001
774-002
774-003

#742 – Using Touch Manipulation Events to Rotate an Element

In addition to using the touch manipulation events to handle translation of an element, we can use the same mechanisms to allow a user to rotate an element using touch.

We can do both translation and rotation in the same event handler.  The ManipulationDelta object gives us both a translation (vector) and a rotation (angle).  Both are automatically incorporated into the ManipulationDelta object, based on how the user is touching the screen.  The user can translate by sliding one finger around and can rotate by placing two fingers on the object and rotating it.

We transform the element by calling two different functions of the underlying Matrix, for both translation and rotation.

Here’s the XAML, containing a single Image control that we’ll interact with.

    <Canvas Name="canvMain" Background="Transparent">
        <Image Source="JamesII.jpg" Width="100"
               IsManipulationEnabled="True"
               RenderTransform="{Binding ImageTransform}"
               ManipulationStarting="Image_ManipulationStarting" ManipulationDelta="Image_ManipulationDelta"/>
    </Canvas>

Here is the source code, with the updated ManipulationDelta event handler.

    public partial class MainWindow : Window, INotifyPropertyChanged
    {
        public MainWindow()
        {
            InitializeComponent();
            this.DataContext = this;

            ImageTransform = new MatrixTransform();
        }

        private MatrixTransform imageTransform;
        public MatrixTransform ImageTransform
        {
            get { return imageTransform; }
            set
            {
                if (value != imageTransform)
                {
                    imageTransform = value;
                    RaisePropertyChanged("ImageTransform");
                }
            }
        }

        private void Image_ManipulationStarting(object sender, ManipulationStartingEventArgs e)
        {
            // Ask for manipulations to be reported relative to the canvas
            e.ManipulationContainer = canvMain;
        }

        private void Image_ManipulationDelta(object sender, ManipulationDeltaEventArgs e)
        {
            ManipulationDelta md = e.DeltaManipulation;
            Vector trans = md.Translation;
            double rotate = md.Rotation;

            Matrix m = imageTransform.Matrix;

            // Find center of element and then transform to get current location of center
            FrameworkElement fe = e.Source as FrameworkElement;
            Point center = new Point(fe.ActualWidth / 2, fe.ActualHeight / 2);
            center = m.Transform(center);

            // Update matrix to reflect translation/rotation
            m.Translate(trans.X, trans.Y);
            m.RotateAt(rotate, center.X, center.Y);

            imageTransform.Matrix = m;
            RaisePropertyChanged("ImageTransform");

            e.Handled = true;
        }

        public event PropertyChangedEventHandler PropertyChanged;

        private void RaisePropertyChanged(string prop)
        {
            if (PropertyChanged != null)
                PropertyChanged(this, new PropertyChangedEventArgs(prop));
        }
    }

742-001