Согласно базе знаний MS, можно указать RenderTransformOrigin для каждой операции преобразования, которая будет применена к объекту. Смотрите эту статью
В нем говорится: «Когда вы применяете преобразование к свойству RenderTransform UIElement, вы можете использовать свойство RenderTransformOrigin, чтобы указать источник для каждого преобразования, которое вы применяете к элементу». Имея эту информацию, я ожидал, что смогу использовать TransformGroup с несколькими поворотами, каждый из которых вокруг своей центральной точки, причем один из них является центром элемента, который задается относительной координатой (0,5, 0,5).
На самом деле я не нашел никакого способа или примера того, как определить RenderTransformOrigin для преобразования, как сказано в статье, что это возможно (хотя и не продемонстрировано), поэтому я попробовал посмотреть, учитывает ли это установка RenderTransformOrigin для объекта. если не указаны CenterX и CenterY. То есть я предполагал, что для любого RotateTransform без явного центра x/y он будет использовать относительную координату в RenderTransformOrigin. Однако мое предположение ошибочно. Посмотрите этот простой тест:
MainWindow.xaml:
<Window x:Class = "RenderTransformOrigin.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:RenderTransformOrigin"
mc:Ignorable = "d"
Title = "MainWindow" Height = "540" Width = "500">
<StackPanel>
<Canvas x:Name = "cv" Height = "500" Width = "500"/>
</StackPanel>
</Window>
MainWindow.xaml.cs:
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
namespace RenderTransformOrigin
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
TextBlock tblock = new TextBlock() { Text = "A", FontSize = 30, FontWeight = FontWeights.Bold };
tblock.Measure(new Size());
tblock.Arrange(new Rect());
cv.Measure(new Size());
cv.Arrange(new Rect());
tblock.RenderTransformOrigin = new Point(0.5, 0.5);
TransformGroup tgroup = new TransformGroup();
tgroup.Children.Add(new TranslateTransform() { X = (cv.ActualWidth - tblock.ActualWidth) / 2, Y = (cv.ActualHeight - tblock.ActualHeight) / 2 - 100 });
tgroup.Children.Add(new RotateTransform() { Angle = 90, CenterX = (cv.ActualWidth - tblock.ActualWidth) / 2 , CenterY = (cv.ActualHeight - tblock.ActualHeight) / 2 });
//tgroup.Children.Add(new RotateTransform() { Angle = -90 });
tblock.RenderTransform = tgroup;
cv.Children.Add(tblock);
}
}
}
Программа помещает TextBlock в Canvas немного смещенным от центра по Y и по центру по X. Сначала она поворачивает текст на 90 градусов, а затем идея состоит в том, чтобы повернуть текст вокруг его центра, чтобы он снова был обращен к читателю. Посмотрите, что я назначил RenderTransformOrigin перед любым преобразованием. Поскольку второе вращение закомментировано, оно не влияет на первое вращение с его явным центром x/y. Если вы раскомментируете строку, текст обязательно повернется вокруг начала координат холста (0, 0), поскольку он выходит за пределы экрана. Добавление { CenterX = 0,5, CenterY = 0,5 } не имеет никакого значения, хотя я думаю, что это избыточно после установки RenderTransformOrigin.
Итак, мой вопрос: как я могу использовать относительные координаты для преобразования, учитывая тот факт, что в группе преобразований есть другие преобразования с разными центрами, которые необходимо сохранить? Вы можете увидеть подобные вопросы, задаваемые в S.O. раньше, без успешных ответов:
C# WPF Как изменить RenderTransformOrigin, но сохранить местоположение?
Несколько RotateTransforms разного происхождения
====== ОБНОВЛЕНИЕ 21/07 ПОСЛЕ КЛЕМЕНСА ПОСТА =======
В части C# я нахожу центр элемента (в данном случае TextBlock, но может быть и что угодно) и назначаю CenterX и CenterY первого поворота, сохраняя при этом угол равным 0. Затем я применяю перемещение и вращение вокруг центра холста. После того, как группа трансформации назначена элементу и добавлена на холст, я меняю первое вращение на нужный угол. Похоже, что здесь используются преимущества CenterX и CenterY предыдущих преобразований, преобразуемых по пути, поэтому мне не нужно отслеживать центр элемента.
public MainWindow()
{
InitializeComponent();
TextBlock tblock = new TextBlock() { Text = "A", FontSize = 30, FontWeight = FontWeights.Bold };
tblock.Measure(new Size());
tblock.Arrange(new Rect());
cv.Measure(new Size());
cv.Arrange(new Rect());
double cx = (cv.ActualWidth - tblock.ActualWidth) / 2,
cy = (cv.ActualHeight - tblock.ActualHeight) / 2;
TransformGroup tgroup = new TransformGroup();
tgroup.Children.Add(new RotateTransform() { Angle = 0, CenterX = tblock.ActualWidth / 2, CenterY = tblock.ActualHeight / 2 });
tgroup.Children.Add(new TranslateTransform() { X = cx, Y = cy - 100 });
tgroup.Children.Add(new RotateTransform() { Angle = 90, CenterX = cx , CenterY = cy });
tblock.RenderTransform = tgroup;
cv.Children.Add(tblock);
((RotateTransform)((TransformGroup)(tblock).RenderTransform).Children[0]).Angle = -90;
}
Тогда текст оказывается лицом к читателю после первого поворота:
Извините, что был неряшлив и не разместил пару фотографий (одна из них была бы просто пустым холстом), чтобы люди, пытающиеся помочь, не оставались в догадках. Я всегда публикую фотографии, поэтому в следующий раз я просто сделаю это, не предполагая, что люди могут читать мои мысли. Спасибо, Джерри.





... как я могу использовать относительные координаты для преобразования, учитывая тот факт, что в группе преобразований есть другие преобразования с разными центрами, которые необходимо сохранить?
Вы должны добавить обратное вращение (или любое другое преобразование, которое должно действовать первым, не подвергаясь влиянию других преобразований), добавив его в качестве первого дочернего элемента TransformGroup. Вращение происходит вокруг центральной точки TextBlock, потому что его RenderTransformOrigin равен 0.5,0.5.
var tblock = new TextBlock
{
Text = "A",
FontSize = 30,
FontWeight = FontWeights.Bold,
RenderTransformOrigin = new Point(0.5, 0.5)
};
cv.Children.Add(tblock);
cv.Measure(new Size());
cv.Arrange(new Rect());
var cx = (cv.ActualWidth - tblock.ActualWidth) / 2;
var cy = (cv.ActualHeight - tblock.ActualHeight) / 2;
var tgroup = new TransformGroup();
tgroup.Children.Add(new RotateTransform());
tgroup.Children.Add(new TranslateTransform(cx, cy - 100));
tgroup.Children.Add(new RotateTransform(90, cx, cy));
tblock.RenderTransform = tgroup;
Теперь (или в любое время позже) вы можете изменить угол первого RotateTransform следующим образом:
((RotateTransform)((TransformGroup)tblock.RenderTransform).Children[0]).Angle = -90;
Вместо TransformGroup вы также можете использовать MatrixTransform, как показано ниже. Чтобы добавить вращение вокруг центра TextBlock, используйте метод RotatePrepend.
var matrix = new Matrix();
matrix.Translate(cx, cy - 100);
matrix.RotateAt(90, cx, cy);
// prepend here
matrix.RotatePrepend(-90);
tblock.RenderTransform = new MatrixTransform(matrix);
При необходимости вы можете сохранить исходную TransformGroup и по-прежнему добавлять поворот с помощью манипуляций с матрицей. Получите текущую матрицу преобразования из свойства Value TransformGroup.
var matrix = tgroup.Value;
matrix.RotatePrepend(-90);
tblock.RenderTransform = new MatrixTransform(matrix);
Правильно ли я понимаю, что вы предлагаете мне не тратить время на RenderTransformOrigin и что в приведенной выше статье, возможно, содержится неверная информация о том, что ее можно применять к отдельным преобразованиям, а не только к элементу? Текущее решение, которое у меня есть, заключается в том, что вместо использования матричного подхода, который вы продемонстрировали, я использую точку (не нанесенную на холст), которая расположена в центре объекта, и когда объект вращается, я применяю то же вращение к нему, чтобы отслеживать центр объекта. Но я хотел избавиться от этого и упростить, см. обновленный вопрос.
Формулировка в этой статье, безусловно, вводит в заблуждение. RenderTransformOrigin не будет волшебным образом работать в качестве центра RotateTransform, который вы добавляете к произвольной TransformGroup. Однако это будет источник преобразования для первого преобразования в группе, как показано в ответе.
Это означает, что установка RenderTransform = new Point(0.5, 0.5) (как это сделано в ответе) эквивалентна использованию центральной точки TextBlock для первого поворота, как вы это делаете в new RotateTransform() { Angle = 0, CenterX = tblock.ActualWidth / 2, CenterY = tblock.ActualHeight / 2
Я ценю ваши дальнейшие разъяснения. Позвольте мне отметить ваш пост как ответ, потому что, хотя эту проблему можно решить разными способами, ваш, безусловно, один из них, и, возможно, люди, повторно посещающие эту тему, смогут найти этот пост и найти рабочий пример, которого я не нашел в передано 2 С.О. дискуссии.
Из вашего первоначального вопроса было неясно, как именно вы будете строить первоначальную TransformGroup и как именно должно было применяться «обратное вращение». Конечно, было бы несложно добавить его в качестве первого элемента коллекции Children TransformGroup. Я добавил ответ соответственно.
Вы должны показать фотографию «до» и «после»; тогда можно было бы подсказать, как туда добраться. Вы можете делать только 4 вещи (вращать, перемещать, масштабировать и наклонять); и это всего лишь вопрос корректировки «относительных» факторов: медленнее; Быстрее; более; или менее. Человек выбирает координаты, которые можно обойти; для меня все зависит от центральной точки объекта. Вращение означает отсутствие перемещения. Вращение означает некоторое перемещение и поворот на 90 градусов. «Расширение» и сжатие означает перемещение (перемещение) и масштабирование ширины. И так далее.