Найти примененный ScaleTransform для элемента управления или UIElement? - PullRequest
3 голосов
/ 27 февраля 2011

У меня есть контроль, сидящий где-то в моем Окне. В корне этого окна находится сетка с именем MainGrid. У меня есть ScaleTransform, примененный к LayoutTransform MainGrid, который я использую, чтобы увеличить все содержимое моего окна, когда размер окна увеличивается. Тем не менее, один из моих элементов управления использует BitmapCache на своих холстах для оптимизации производительности рисования. BitmapCache не учитывает любые значения ScaleTransform, которые могут быть применены к элементу управления, поэтому при увеличении масштаба элемент управления выглядит размытым.

BitmapCache имеет свойство RenderAtScale, которое я могу использовать для увеличения масштаба кэшированного изображения. Однако у меня проблема в том, что я не знаю элегантного способа узнать, каким должно быть значение Scale. На данный момент у меня есть свойство в моем элементе управления, чтобы мое Окно могло передать свое значение масштаба элементу управления. Однако мне бы хотелось, чтобы мне не нужно было полагаться на какой-либо внешний источник, передающий значение масштаба.

Есть ли способ, которым элемент управления может получить сводку всех примененных к нему преобразований ScaleT

Ответы [ 5 ]

3 голосов
/ 05 октября 2013

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

Visual child = /* some visual */ this;
Visual parent = PresentationSource.FromVisual(child).RootVisual;

// calculate the transform needed to go from the parent to the child coordinate spaces
var childTransform = parent.TransformToDescendant(child);

// use the transform to scale a 1x1 rectangle
// use Inverse as the transform is from Parent coordinates to child, we want 
// child to parent
var unitRect = new Rectangle(0, 0, 1, 1);
var transformedUnit = childTransform.Inverse.TransformBounds(unitRect);

// transformedUnit.Width and transformedUnit.Height are now the scale factors (X and Y)
Debug.Assert(transformedUnit.Width == 0.25 && transformedUnit.Height == 0.25);
2 голосов
/ 27 февраля 2011

Как сказал Элад Кац , я не думаю, что есть какой-либо прямой способ узнать, применяется ли ScaleTransform к какому-либо из родителей, не проходя их.

Однако, есливы знаете, что ScaleTransform применяется к MainGrid, тогда вы можете связать RenderAtScale с ScaleX или ScaleY из MainGrid ScaleTransform.Может быть, это то, что вы уже делаете, но я все равно добавлю это в качестве подсказки

Вероятно, самый простой способ сослаться на ScaleTransform в привязке - это назвать его

<Grid Name="MainGrid">
    <Grid.LayoutTransform>
        <TransformGroup>
            <ScaleTransform x:Name="MainGridScaleTransform" .../>
        </TransformGroup>            
    </Grid.LayoutTransform>

Привязка должна выглядеть примерно так:

<BitmapCache RenderAtScale="{Binding ElementName=MainGridScaleTransform,
                                     Path=ScaleX}"/>

В противном случае вы можете получить ScaleTransform на Пути с TransformGroup

<BitmapCache RenderAtScale="{Binding ElementName=MainGrid,
 Path=(LayoutTransform).(TransformGroup.Children)[0].(ScaleTransform.ScaleX)}"/>

или без TransformGroup

<BitmapCache RenderAtScale="{Binding ElementName=MainGrid,
                              Path=(LayoutTransform).(ScaleTransform.ScaleX)}"/>
2 голосов
/ 01 марта 2011

Основываясь на рекомендациях Элада Каца, я создал некоторый код для сканирования VisualTree, чтобы попытаться подсчитать любые единообразные ScaleTransform с. Если кто-то имеет некоторые оптимизации для этих алгоритмов или может подумать о нескольких вещах, которые я, возможно, не рассматриваю, дайте мне знать. Опять же, моя цель состоит в том, чтобы получить RenderAtScale, который на самом деле является подходящим, учитывая применяемые в настоящее время ScaleTransform s. OnRenderSizeChanged, кажется, подходит для этого, поскольку это происходит ПОСЛЕ того, как все элементы макета были запущены. Но, может быть, есть лучшее место для запуска GetTotalTransformScale ()?

protected override void OnRenderSizeChanged(SizeChangedInfo sizeInfo)
{
    base.OnRenderSizeChanged(sizeInfo);
    double totalTransformScale = GetTotalTransformScale();
    BitmapCache bitmapCache = (BitmapCache)MyCachedCanvas.CacheMode;
    if (bitmapCache.RenderAtScale != totalTransformScale)
        bitmapCache.RenderAtScale = totalTransformScale;
}

private double GetTotalTransformScale()
{
    double totalTransform = 1.0d;    
    DependencyObject currentVisualTreeElement = this;            
    do
    {
        Visual visual = currentVisualTreeElement as Visual;
        if (visual != null)
        {
            Transform transform = VisualTreeHelper.GetTransform(visual);

            // This condition is a way of determining if it
            // was a uniform scale transform. Is there some better way?
            if ((transform != null) &&
                (transform.Value.M12 == 0) &&
                (transform.Value.M21 == 0) &&
                (transform.Value.OffsetX == 0) &&
                (transform.Value.OffsetY == 0) &&
                (transform.Value.M11 == transform.Value.M22))
            {                        
                totalTransform *= transform.Value.M11;
            }
        }
        currentVisualTreeElement = VisualTreeHelper.GetParent(currentVisualTreeElement);
    }
    while (currentVisualTreeElement != null);

    return totalTransform;
}
1 голос
/ 27 февраля 2011

Вы всегда можете рекурсивно просмотреть всех родителей Контроля и сложить их ScaleTransform.

Я не думаю, что вы можете сделать это другим способом.

0 голосов
/ 14 марта 2012

Я знаю, что это старый вопрос, но у меня была похожая проблема: мне нужно было сделать один тип управления, чтобы игнорировать ScaleTransform на верхнем уровне.

UIElement имеет RenderTransform свойство, которое в основном ScaleTransform влияет на него.

...