проблем с ContainerVisual.Transform

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

Заменям ArrangeOverride и изчислявам правоъгълника, който искам да начертая, въз основа на дадения размер и подложката на контролата.

след това зададох трансформацията на моя обект ContainerVisual в горния ляв ъгъл на правоъгълника, така че методите, които изобразяват чертежа, да не трябва да вземат предвид правоъгълника и да приемат, че началната точка на чертежа е в точка 0,0.

това не работи и чертежът се измества. ако вместо това задам трансформация на обекта DrawingVisual, той работи и правоъгълникът се показва по начина, по който трябва да бъде.

Мислех, че ако задам трансформация на контейнера, тя автоматично ще се приложи към визуалните елементи под него. дали е така?

благодаря за всяка помощ

РЕДАКТИРАНЕ: Актуализира изходния код, за да покаже пълния код.


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>

person akonsu    schedule 09.11.2010    source източник


Отговори (2)


Обяснение на странно поведение при изрязване

Сега, след като публикувахте пълния си изходен код, най-накрая успях да видя какво виждате. Вашият проблем изобщо не е в трансформацията: той е в клипа!

  • Ако коментирате оператора за присвояване 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)

Така че вашият проблем не е, че трансформацията не се случва: а че трансформацията премества и областта за изрязване, така че областта за изрязване вече не съвпада с правоъгълника и можете да видите само две страни на правоъгълника.

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

Всъщност кодът, който сте публикували, никога не използва „контейнер“, така че всичко, което ще видите, е празен екран.

Във вашия действителен код използвате "контейнер" неправилно, предотвратявайки събитията да се появят в правилната последователност, за да накарат неговата трансформация да бъде взета и предадена на MIL слоя.

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

Вашето разбиране е правилно: Ако вашето визуално дърво е изградено по всички правила, няма значение дали вашата трансформация е на вашия „контейнер“ или на вашия „чертеж“.

Тъй като така или иначе използвате Control, любопитен съм защо просто не оставите нормалната базирана на UIElement система за оформление да се справи с вашите нужди за оформление.

Първа актуализация (запазена по същата причина)

Благодаря за корекцията на кода. Това е, както подозирах: изграждате вашето визуално дърво неправилно. Ако използвате AddVisualChild, вие също трябва да замените GetVisualChild и VisuaChildrenCount. Това е така, защото Visual не съхранява списък с деца: зависи от подкласа (вашият клас) да направи това. Това, което се случва е:

  • Когато извикате AddVisualChild, трансформацията на контейнера е null, така че това се предава на MILCore.
  • По-късно, когато промените преобразуването на контейнера, той използва своя родителски указател (който беше зададен в AddVisualChild), за да сигнализира, че неговите данни за преобразуване трябва да бъдат опреснени. Тази актуализация изисква част от визуалното дърво да бъде сканирано с помощта на GetVisualChild и VisualChildrenCount.
  • Тъй като не сте приложили тези методи, тази част от актуализацията е неуспешна.

Казвате, че сте „нов в WPF“. Наясно ли сте, че си играете с някои от най-ниските и езотерични функции на WPF, такива, които никога не биха били използвани в най-обикновени WPF приложения? Това е еквивалентно на това да започнете да учите програмиране с помощта на машинен език. Обикновено бихте използвали шаблони с път, правоъгълник и т.н. за тази цел. Понякога може да отидете на по-ниско ниво и да използвате DrawingBrush с DrawingGroup, съдържаща GeometryDrawings и т.н. Но почти никога няма да стигнете чак до DrawingVisual и RenderOpen! Единственият път, когато бихте направили това, е когато имате огромни чертежи, състоящи се от милиони отделни елементи и затова искате да заобиколите цялото оформление и структура на по-високите слоеве за абсолютна максимална производителност.

Самостоятелното манипулиране на визуалното дърво (AddVisualChild и т.н.) също е разширена функция. Винаги препоръчвам на хората, които са нови за WPF, да се придържат към UIElement и по-горе през първите няколко месеца, като използват Control с шаблони. Препоръчвам им да използват Path и други подкласове на форми за своите рисунки и да използват VisualBrushes, когато са необходими разширени ефекти за рисуване.

Надявам се това да помогне.

person Ray Burns    schedule 09.11.2010
comment
Рей, благодаря за отговора. Редактирах кода си, забравих да добавя реда, който добавя контейнера към контролата. Аз съм нов в WPF, така че не знам как да използвам правилно контейнера. бихте ли изяснили, моля? какви са правилата, които трябва да следвам, за да изградя правилно своето визуално дърво? причината, поради която правя мое собствено оформление, е, че тъй като имам визуални елементи в моя контрол, изпълнението по подразбиране на ArrangeOverride хвърля изключение, защото очаква UIElement (ако си спомням правилно), вместо Visual. това ли питаш? - person akonsu; 10.11.2010
comment
да, това помага. Благодаря много. редактирах въпроса си и включих пълния код там. имах GetVisualChild и т.н., пропуснах ги. проблемът все още се появява. ако задам transform на контейнера, той рисува само долните десни ръбове на правоъгълника. опитвам се да разбера защо. причината, поради която използвам визуални елементи, е, че пиша контрола на диаграма, която ще има множество слоеве с различни графики в решетка и потребителят ще трябва да превърта диаграмата, така че производителността е важна. реших да не използвам пътища и т.н., защото те имат функционалност, която не ми трябва, обвързване на данни и т.н. - person akonsu; 10.11.2010
comment
Добре, сега е възможно да видите какво се случва. Добавих актуализация, обясняваща истинския проблем. - person Ray Burns; 11.11.2010

проблема е с контейнера.Клип. трябва да бъде


container.Clip = new RectangleGeometry(new Rect(0, 0, w, h));
person akonsu    schedule 10.11.2010