проблема с ContainerVisual.Transform - PullRequest
2 голосов
/ 09 ноября 2010

в моем пользовательском элементе управления у меня есть объект ContainerVisual и DrawingVisual под ним.

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

после этого я установил преобразование моего объекта ContainerVisual в верхний левый угол прямоугольника, чтобы методы, которые отображают чертеж, не должны были учитывать прямоугольник и предполагать, что источник чертежа находится в точке 0,0. 1005 *

это не работает, и рисунок смещен. если вместо этого я устанавливаю преобразование объекта DrawingVisual, он работает, и прямоугольник отображается так, как он должен быть.

Я думал, что если я установлю transform для контейнера, он будет автоматически применен к визуальным элементам под ним. это так?

спасибо за любую помощь

РЕДАКТИРОВАТЬ: Обновлен исходный код, чтобы показать полный код.


class MyControl : Control
{
    private readonly ContainerVisual container = new ContainerVisual();
    private readonly DrawingVisual drawing = new DrawingVisual();
    private Rect rect;

    private void RenderDrawing()
    {
        using (var c = drawing.RenderOpen())
        {
            var p = new Pen(new SolidColorBrush(Colors.Black), 1);

            c.DrawRectangle(null, p, new Rect(0, 0, rect.Width, rect.Height));
        }
    }

    protected override Size ArrangeOverride(Size s)
    {
        var h = Math.Max(0, s.Height - Padding.Top - Padding.Bottom);
        var w = Math.Max(0, s.Width - Padding.Left - Padding.Right);

        var r = new Rect(Padding.Left, Padding.Top, w, h);

        if (rect != r)
        {
            rect = r;

            container.Clip = new RectangleGeometry(rect);
            container.Transform = new TranslateTransform(rect.Left, rect.Top);

            // replace the line above with the following line to make it work
            // drawing.Transform = new TranslateTransform(rect.Left, rect.Top);

            RenderDrawing();
        }
        return s;
    }

    protected override Visual GetVisualChild(int index)
    {
        return container;
    }

    protected override Size MeasureOverride(Size s)
    {
        return new Size();
    }

    protected override int VisualChildrenCount
    {
        get { return 1; }
    }

    public MyControl()
    {
        container.Children.Add(drawing);
        AddVisualChild(container);
    }
}

<Window x:Class="MyApp.MyWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:c="clr-namespace:MyApp"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
  <Grid>
    <c:MyControl Padding="20" />
  </Grid>
</Window>

Ответы [ 2 ]

3 голосов
/ 10 ноября 2010

Объяснение странного поведения отсечения

Теперь, когда вы опубликовали свой полный исходный код, я наконец смог увидеть то, что вы видели.Ваша проблема вовсе не в преобразовании: она в клипе!

  • Если вы закомментируете оператор присваивания container.Clip, вы получите идентичные результаты независимо от того, включили ли вы преобразование container или drawing
  • Если вы раскомментировали оператор присваивания container.Clip, область отсечения идеально центрируется при преобразовании чертежа, но при преобразовании контейнера область отсечения смещается, так что только областьнижняя и правая линии прямоугольника были видны (и не все)

Причина, по которой это происходит, заключается в том, что геометрия, указанная для container.Clip, является частью контейнера, поэтому на нее влияют container.Transform но не drawing.Transform:

Это можно лучше понять, посмотрев на верхние левые углы контейнера, рисунок, прямоугольник и область обрезки относительно верхнего левого угла окна:

Когда вы устанавливаете преобразование на чертеже:

  • Контейнер в (0,0) относительно окна (нулевое преобразование)
  • Область клипа в (20,20) относительно окна (нулевое преобразование + RectangleGeometry)
  • Рисование в (20,20) относительно окна (нулевое преобразование + TranslateTransform)
  • Прямоугольник в (20,20) относительно окна (нулевое преобразование + TranslateTransform + 0,0)

Когда вы устанавливаете преобразование для контейнера:

  • Контейнер находится в (20,20) относительно окна (TranslateTransform)
  • Область клипа находится в (40,40) относительно окна (TranslateTransform + RectangleGeometry)
  • Рисование в (20,20) относительно окна (TranslateTransform + нулевое преобразование)
  • Прямоугольник в (20,20) относительно окна (TranslateTransform + нулевое преобразование +0,0)

Так что ваша проблема не в том, что преобразование не происходит: оно заключается в том, что преобразование тоже перемещает область клипа, поэтому область клипа больше не совпадает с прямоугольником иВы можете видеть только две стороны прямоугольника.

Ответ дан для оригинального кода (сохранен, потому что он имеет некоторое полезное объяснение)

На самом деле, отправленный вами код никогда не использует «контейнер», поэтому все, что вы увидите, это пустой экран.

В вашем фактическом коде вы используете «контейнер» неправильно, не позволяя событиям происходить в правильной последовательности, что приводит к тому, что их Transform выбирается и передается в слой MIL.

Помните, что когда Visual имеетНабор преобразований, это не сам визуал, а тот визуальный родительский элемент , который фактически обрабатывает это преобразование.Например, если вы визуализируете страницу в XPS с помощью ReachFramework или выполняете тестирование попаданий, преобразование на внешнем визуальном объекте игнорируется.

Ваше понимание верно: если ваше визуальное дерево построено в соответствии со всеми правилами, оно неНеважно, находится ли ваше преобразование в вашем "контейнере" или в вашем "чертеже".

Поскольку вы все равно используете Control, мне любопытно, почему вы не просто позволяете обычной системе макетов на основе UIElement обрабатыватьВаш макет нуждается.

Первое обновление (сохраняется по той же причине)

Спасибо за исправление кода.Как я и подозревал, вы неправильно строите свое визуальное дерево.Если вы используете AddVisualChild, вы также должны переопределить GetVisualChild и VisuaChildrenCount.Это потому, что Visual не хранит список дочерних элементов: это зависит от подкласса (вашего класса).Происходит следующее:

  • Когда вы вызываете AddVisualChild, преобразование контейнера становится нулевым, поэтому оно передается в MILCore.
  • Позже, когда вы изменяете преобразование контейнера, он использует родительский указатель (который был установлен в AddVisualChild), чтобы сигнализировать, что его данные преобразования должны быть обновлены. Это обновление требует сканирования части визуального дерева с использованием GetVisualChild и VisualChildrenCount.
  • Так как вы не реализовали эти методы, эта часть обновления завершается ошибкой.

Вы говорите, что вы "новичок в WPF". Знаете ли вы, что вы играете с некоторыми из самых низкоуровневых и эзотерических функций WPF, которые никогда не будут использоваться в самых обычных приложениях WPF? Это равносильно тому, чтобы начать изучать программирование с использованием машинного языка. Обычно для этой цели вы используете шаблоны с Path, Rectangle и т. Д. Иногда вы можете перейти на более низкий уровень и использовать DrawingBrush с DrawingGroup, содержащей GeometryDrawings и т. Д. Но вы почти никогда не пройдете весь путь до DrawingVisual и RenderOpen! Единственный раз, когда вы это сделаете, это когда у вас есть огромные чертежи, состоящие из миллионов отдельных элементов, и вы хотите обойти все накладные расходы на макеты и структуру верхних слоев для достижения абсолютной максимальной производительности.

Управление визуальным деревом самостоятельно (AddVisualChild и т. Д.) Также является расширенной функцией. Я всегда рекомендую новичкам в WPF придерживаться UIElement и выше в течение первых нескольких месяцев, используя Control с шаблонами. Я рекомендую использовать для своих чертежей Path и другие подклассы фигур, а также использовать VisualBrushes, когда требуются дополнительные эффекты рисования.

Надеюсь, это поможет.

0 голосов
/ 11 ноября 2010

проблема с контейнером. Клип. должно быть


container.Clip = new RectangleGeometry(new Rect(0, 0, w, h));
...