找到在 Control 或 UIElement 上应用的 ScaleTransform?

发布于 2024-10-19 13:06:07 字数 432 浏览 6 评论 0原文

我的窗口某处有一个控件。该窗口的根部是一个名为 MainGrid 的网格。我在 MainGrid 的 LayoutTransform 上应用了 ScaleTransform,当窗口大小增大时,我用它来放大窗口的所有内容。然而,我的一个控件在其画布上使用BitmapCache来优化绘图性能。 BitmapCache 不考虑任何可能应用于控件的 ScaleTransform,因此如果我放大,控件会显得模糊。

BitmapCache 确实有一个 RenderAtScale 属性,我可以用它来增加缓存图像的比例。然而,我遇到的问题是我不知道有一种优雅的方法来找出 Scale 值需要的值。现在,我的控件上有一个属性,以便我的窗口可以将其比例值传递给控件。但是,如果我不需要依赖某些外部源传递比例值,我会喜欢它。

有什么方法可以让控件获得应用到它的所有 ScaleTransform 的摘要吗?

I have a control sitting somewhere in my Window. At the root of this Window is a grid named MainGrid. I have a ScaleTransform applied on MainGrid's LayoutTransform that I use to zoom in on all the contents of my Window when the window's size grows. However, one of my controls usesBitmapCache on their canvases to optimize drawing performance. BitmapCache does not take into consideration any ScaleTransforms that might be applied to the control, so if I'm zoomed in, the control appears blurry.

BitmapCache does have a RenderAtScale property I can use to increase the scale of the cached image. However, the problem I have is that I don't know of an elegant way to find out what that Scale value needs to be. For now, I have a property on my control so that my Window can pass its scale value to the control. However, I would like it if I didn't need to rely on some external source passing in a scale value.

Is there any way a Control can get a summary of all the ScaleTransforms applied to it?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

微暖i 2024-10-26 13:06:07

您可以使用 PresentationSourceRootVisual 来计算元素的总变换。查找父母和孩子之间应用的比例的一般模式是

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);

You can use the PresentationSource's RootVisual to calculate the total transform of an element. The general pattern to find the scale applied between a parent and a child is

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);
心安伴我暖 2024-10-26 13:06:07

正如 Elad Katz 所说,我认为没有任何直接方法可以在不经过父级的情况下找出 ScaleTransform 是否应用于任何父级。

但是,如果您知道 ScaleTransform 应用于 MainGrid,则可以将 RenderAtScale 绑定到 ScaleXScaleY。也许这就是您已经在做的事情,但无论如何我都会将其作为提示添加

到绑定中引用 ScaleTransform 的最简单方法可能是通过命名它

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

绑定应该看起来与此类似

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

否则, 在路径中获取 ScaleTransform

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

您可以使用 TransformGroup或不使用 TransformGroup

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

As Elad Katz said, I don't think there's any direct way to find out if a ScaleTransform is applied to any parent without going through them.

However, if you know that the ScaleTransform is applied to the MainGrid, then you can bind RenderAtScale to the ScaleX or ScaleY of the MainGrid's ScaleTransform. Maybe this is what you're already doing but I throw it in as a tip anyway

Probably the easiest way to reference the ScaleTransform in the Binding is by naming it

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

The Binding should then look similar to this

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

Otherwise, you can get the ScaleTransform in the Path with TransformGroup

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

Or without TransformGroup

<BitmapCache RenderAtScale="{Binding ElementName=MainGrid,
                              Path=(LayoutTransform).(ScaleTransform.ScaleX)}"/>
煮酒 2024-10-26 13:06:07

根据 Elad Katz 的建议,我创建了一些代码来扫描 VisualTree,以尝试汇总所有统一的 ScaleTransform。如果有人对此算法有一些优化,或者可以想到一些我可能没有考虑的事情,请告诉我。同样,我的目标是获得一个在当前应用的 ScaleTransform 的情况下实际上合适的 RenderAtScale。 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;
}

Based on Elad Katz's recommendations, I have created some code to scan up the VisualTree to attempt to total any uniform ScaleTransforms. If any one has some optimizations for this algorithms or can think of a few things I may not be considering, let me know. Again, my goal is to get a RenderAtScale that is actually apropriate given the currently applied ScaleTransforms. OnRenderSizeChanged seems to be an OK place to do this, as it happens AFTER all the layout stuff has run. But, perhaps there is a better place to trigger 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;
}
一紙繁鸢 2024-10-26 13:06:07

您始终可以递归地遍历所有 Control 的父级并对它们的 ScaleTransform 求和。

我不认为你可以用其他方式做到这一点。

You can always recursively go through all of the Control's parents and sum their ScaleTransform.

I don't think you can do it any other way.

欢烬 2024-10-26 13:06:07

我知道这是一个老问题,但我自己也遇到了类似的问题:我需要制作一种类型的控件来忽略顶层的 ScaleTransform 。

UIElement 有一个 RenderTransform 属性,它基本上是影响它的 ScaleTransform

I know this is an old question, but I had a similar problem myself: I needed to make one type of control to ignore the ScaleTransform on the top level.

UIElement has a RenderTransform property which is basically the ScaleTransform effecting it.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文