WPF: как сделать (0,0) в центре холста

WPF Canvas имеет систему координат, начинающуюся с точки (0,0) в верхнем левом углу элемента управления.

Например, если задать следующее, мой элемент управления появится в верхнем левом углу:

<Control Canvas.Left="0" Canvas.Top="0">

Как мне изменить его на стандартные декартовы координаты?

В основном:

  • (0,0) в центре
  • перевернуть Y

Я заметил этот пост похож, но в нем не говорится о переводе системы координат. Я пробовал добавить TranslateTransform, но не могу заставить его работать.


person decasteljau    schedule 09.12.2009    source источник


Ответы (4)


Лучше всего написать собственный Canvas, в котором вы можете написать ArrangeOverride таким образом, чтобы он принимал 0,0 в качестве центра.

Обновление: я дал еще один комментарий в ответе ниже (@decasteljau). Я не рекомендую заимствовать из Canvas. Вы можете получить из Panel и добавить два свойства Attached Dependancy Top и Left и поместить тот же код, который вы вставили выше. Также не нужен конструктор с LayoutTransform в нем. И не используйте какие-либо преобразования в коде панели, используйте правильную меру и упорядочение на основе DesiredSize панели, чтобы вы также могли получить хорошее поведение при изменении размера содержимого. Canvas не позиционирует элементы динамически при изменении размера Canvas.

person Jobi Joy    schedule 09.12.2009
comment
К сожалению, этот ответ неверен :-(. Пользовательский холст - это гораздо больше работы, чем необходимо: вы можете добиться точно такого же эффекта, не работая вообще, установив высоту / ширину холста на ноль, центрируя его в своем контейнере и перевернув на рендере. См. мой ответ ниже. - person Ray Burns; 27.12.2009
comment
Я согласен с вашим ответом @Ray, но если вам нужно масштабируемое решение, я не рекомендую ваш ответ. это означает, что если я установлю для своего дочернего Canvas.Left = 0.5 и Canvas.Top = 0.5, то при изменении размера родительского окна / холста дочерняя позиция будет полностью испорчена. Но если вы напишете правильный настраиваемый элемент управления (всего 10-15 строк кода), он всегда динамически позиционируется в (0.5,0.5). Поэтому я рекомендую написать собственную панель - получившаяся UnitPanel очень полезна, чем обычный Canvas. :) - person Jobi Joy; 27.12.2009
comment
@JobiJoy, можете ли вы предоставить какой-нибудь образец реализации вашего описания? - person RobinAtTech; 08.04.2014

Нет необходимости создавать настраиваемую панель. Canvas подойдет. Просто оберните его внутри другого элемента управления (например, границы), отцентрируйте, установите нулевой размер и переверните с помощью RenderTransform:

<Border>
  <Canvas HorizontalAlignment="Center" VerticalAlignment="Center"
          Width="0" Height="0"
          RenderTransform="1 0 0 -1 0 0">
    ...
  </Canvas>
</Border>

Вы можете сделать это, и все на холсте по-прежнему будет отображаться, за исключением того, что (0,0) будет в центре содержащего элемента управления (в данном случае в центре границы), а + Y будет вверху, а не вниз.

Опять же, для этого нет необходимости создавать настраиваемую панель.

person Ray Burns    schedule 27.12.2009
comment
@Ray, отлично, я искал что-то подобное. Однако я не могу фиксировать щелчки на холсте, есть ли решение? - person Bruno Reis; 10.02.2010
comment
@Bruno: Как и любая панель, Canvas полностью участвует в маршрутизации событий. Вы можете обрабатывать события мыши на уровне холста или в его контейнере. Однако сам он ничего не рисует и поэтому не участвует в проверке попадания. Если вы хотите, чтобы щелчок по пустой области холста распознавался как щелчок по холсту, простой способ сделать это - добавить прямоугольник с заливкой = Прозрачный, который покрывает весь холст. Этот прямоугольник будет получать события щелчка, которые, конечно же, будут перенаправлены на холст и выше, чтобы их можно было обрабатывать на любом уровне. - person Ray Burns; 11.02.2010
comment
@Ray, у меня есть приложение, которому нужно центрировать Canvas (0, 0). Я сделал это так, как вы здесь объяснили. Но размер холста остается равным 0, хотя я рисую на нем линии или увеличиваю окно. Нужно ли мне делать что-то помимо того, что вы упомянули? - person Shankar Raju; 10.08.2011

Это было очень легко сделать. Я посмотрел на исходный код Canvas с помощью .NET Reflector и заметил, что реализация на самом деле очень проста. Единственное, что требовалось, - это переопределить функцию ArrangeOverride(...).

public class CartesianCanvas : Canvas
{
    public CartesianCanvas()
    {
        LayoutTransform = new ScaleTransform() { ScaleX = 1, ScaleY = -1 };
    }
    protected override Size ArrangeOverride( Size arrangeSize )
    {
        Point middle = new Point( arrangeSize.Width / 2, arrangeSize.Height / 2 );

        foreach( UIElement element in base.InternalChildren )
        {
            if( element == null )
            {
                continue;
            }
            double x = 0.0;
            double y = 0.0;
            double left = GetLeft( element );
            if( !double.IsNaN( left ) )
            {
                x = left;
            }

            double top = GetTop( element );
            if( !double.IsNaN( top ) )
            {
                y = top;
            }

            element.Arrange( new Rect( new Point( middle.X + x, middle.Y + y ), element.DesiredSize ) );
        }
        return arrangeSize;
    }
}
person decasteljau    schedule 10.12.2009
comment
хорошо, но я не рекомендую заимствовать из Canvas. Вы можете заимствовать из Panel и добавить два свойства Attached Dependancy Top и Left и поместить тот же код, который вы вставили выше. Также не нужен конструктор с LayoutTransform в нем. - person Jobi Joy; 10.12.2009
comment
@JobiJoy Panel и добавьте два свойства Attached Dependancy Top и Left и поместите тот же код, который вы вставили выше. Можете ли вы его показать? - person RobinAtTech; 10.04.2014
comment
Это пример, который делает то, что вы, вероятно, хотите. Остальные являются хорошими сокращениями, но они довольно легко потерпят неудачу, например, когда вы начнете рисовать линии сетки на своем холсте (поскольку actualSize = 0) и т. Д. - person Spiked3; 29.12.2014

Вы можете просто изменить происхождение с помощью RenderTransformOrigin.

    <Canvas Width="Auto" Height="Auto"
            HorizontalAlignment="Center"
            VerticalAlignment="Center"
            RenderTransformOrigin="0.5,0.5">
        <Canvas.RenderTransform>
            <TransformGroup>
                <ScaleTransform ScaleY="-1" ScaleX="1" />
            </TransformGroup>
        </Canvas.RenderTransform>
    </Canvas>
person persianLife    schedule 06.01.2014