Найти примененный ScaleTransform для элемента управления или UIElement?
У меня есть контроль, сидящий где-то в моем окне. В корне этого окна находится сетка с именем MainGrid. у меня есть ScaleTransform
применяется к LayoutTransform MainGrid, который я использую, чтобы увеличить все содержимое моего окна, когда размер окна увеличивается. Тем не менее, один из моих элементов управления используетBitmapCache
на их полотнах, чтобы оптимизировать производительность рисования. BitmapCache не принимает во внимание ScaleTransform
s, которые могут быть применены к элементу управления, поэтому, если я увеличу масштаб, элемент управления выглядит размытым.
BitmapCache имеет свойство RenderAtScale, которое я могу использовать для увеличения масштаба кэшированного изображения. Однако у меня проблема в том, что я не знаю элегантного способа узнать, каким должно быть значение Scale. На данный момент у меня есть свойство в моем элементе управления, чтобы мое Окно могло передать свое значение масштаба элементу управления. Однако мне бы хотелось, чтобы мне не нужно было полагаться на какой-либо внешний источник, передающий значение масштаба.
Есть ли способ, которым элемент управления может получить сводку всех примененных к нему ScaleTransforms?
5 ответов
Вы всегда можете рекурсивно пройтись по всем родителям Контроля и суммировать их ScaleTransform
,
Я не думаю, что вы можете сделать это по-другому.
Вы можете использовать PresentationSource
"s 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);
Как сказал Элад Кац, я не думаю, что есть какой-либо прямой способ выяснить, ScaleTransform
применяется к любому родителю, не проходя их.
Однако, если вы знаете, что ScaleTransform
применяется к MainGrid, то вы можете связать RenderAtScale
к ScaleX
или же ScaleY
MainGrid's 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)}"/>
Основываясь на рекомендациях Элада Каца, я создал некоторый код для сканирования VisualTree, чтобы попытаться получить любую униформу ScaleTransform
s. Если кто-то имеет некоторые оптимизации для этих алгоритмов или может подумать о нескольких вещах, которые я, возможно, не рассматриваю, дайте мне знать. Опять же, моя цель состоит в том, чтобы получить 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;
}
Я знаю, что это старый вопрос, но у меня была похожая проблема: мне нужно было сделать один тип контроля, чтобы игнорировать ScaleTransform
на верхнем уровне.
UIElement
имеет RenderTransform
свойство, которое в основном ScaleTransform
осуществляя это.