如何获取 WPF 可视元素的缩放大小

7
我正在将一个WPF Visual(UserControl)呈现为位图,但问题在于呈现的图像是缩放/变换之前的UserControl大小。因此,假设UserControl设计为200x200像素。当我渲染到BMP时,我使用的是UserControl的ActualWidth和ActualHeightt,它们分别报告为200和200。问题在于UserControl位于Canvas中,并且自动调整大小(设置为随窗口大小缩放/填充)以接近1200 x 1200(它会改变)
我已经阅读并搜索了一些内容,到目前为止还无法确定有效大小,即控件在屏幕上被绘制的大小。
我遇到了这个问题,听起来很有希望,但返回的Transform不包含缩放数据。嗯,它确实包含,但它们都是1。 Get element position after transform 有什么建议可以查找渲染缩放比例的地方!
[更新]如建议所示,我包括了相关代码:
public static Bitmap PngBitmap(this Visual visual)
{
    // Get height and width
    int width = (int)(double)visual.GetValue(
        FrameworkElement.ActualWidthProperty);
    int height = (int)(double)visual.GetValue(
        FrameworkElement.ActualHeightProperty);

    // Render
    RenderTargetBitmap rtb =
        new RenderTargetBitmap(
            width,
            height,
            96,
            96,
            PixelFormats.Default);
    rtb.Render(visual);

    // Encode
    PngBitmapEncoder encoder = new PngBitmapEncoder();
    encoder.Frames.Add(BitmapFrame.Create(rtb));
    System.IO.MemoryStream stream = new System.IO.MemoryStream();
    encoder.Save(stream);

    // Create Bitmap
    Bitmap bmp = new Bitmap(stream);
    stream.Close();

    return bmp;
}

public static BitmapSource BitmapSource(this Visual visual)
{
    Bitmap bmp = visual.PngBitmap();
    IntPtr hBitmap = bmp.GetHbitmap();
    BitmapSizeOptions sizeOptions = BitmapSizeOptions.FromEmptyOptions();
    return Imaging.CreateBitmapSourceFromHBitmap(
                        hBitmap,
                        IntPtr.Zero,
                        Int32Rect.Empty,
                        sizeOptions);
}
[更新 #2] 添加了 XAML - Grid 元素被移除,因为它太大了,根据我阅读的 XAML,包含键盘 UserControl 的 Canvas 不是 Grid 元素的一部分。
<UserControl xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
    xmlns:local="clr-namespace:PMD.HECAT.DashboardModule" 
    xmlns:PMD_HECAT_DashboardModule_VirtualKeyboard="clr-namespace:PMD.HECAT.DashboardModule.VirtualKeyboard" 
    xmlns:System_Windows_Controls="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Input.Toolkit" 
    xmlns:PMD_HECAT_DashboardModule_Input="clr-namespace:PMD.HECAT.DashboardModule.Input"
    xmlns:control="clr-namespace:PMD.HECAT.DashboardModule.Controls"    
    x:Class="PMD.HECAT.DashboardModule.CandidateElectrodeView"             
    x:Name="UserControl"
    mc:Ignorable="d"
    d:DesignWidth="1024" d:DesignHeight="768" Width="640" Height="360">
    <UserControl.Resources>
         <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>
                <ResourceDictionary Source="../Themes/DashboardStyles.xaml" />
                <ResourceDictionary Source="../Themes/ImageButtons.xaml" />
                <ResourceDictionary Source="CandidateViewResources.xaml" />
            </ResourceDictionary.MergedDictionaries>
        </ResourceDictionary>        
    </UserControl.Resources>
    <UserControl.Triggers>
        <EventTrigger RoutedEvent="PMD_HECAT_DashboardModule_VirtualKeyboard:VirtualKeyboardView.KeyboardClose" SourceName="virtualKeyboardView">
            <BeginStoryboard Storyboard="{StaticResource OnKeyboardClose1}"/>
        </EventTrigger>
    </UserControl.Triggers>
    <Canvas Width="100" HorizontalAlignment="Left">

        <PMD_HECAT_DashboardModule_VirtualKeyboard:VirtualKeyboardView x:Name="virtualKeyboardView" Height="222" Width="550" RenderTransformOrigin="0.5,0.5" Opacity="0" Active="False">
            <PMD_HECAT_DashboardModule_VirtualKeyboard:VirtualKeyboardView.RenderTransform>
                <TransformGroup>
                    <ScaleTransform/>
                    <SkewTransform/>
                    <RotateTransform/>
                    <TranslateTransform X="40" Y="400"/>
                </TransformGroup>
            </PMD_HECAT_DashboardModule_VirtualKeyboard:VirtualKeyboardView.RenderTransform>
        </PMD_HECAT_DashboardModule_VirtualKeyboard:VirtualKeyboardView>
        <Rectangle Stroke="White" Opacity="0.7" Fill="White" Height="370" Width="654.851" Canvas.Left="687" Canvas.Top="0" />
    </Canvas>
</UserControl>

控件应用了哪种转换?LayoutTransform还是RenderTransform?“自动大小”是什么意思? - Emond
@Erno - 老实说,我不是很确定。我是新手,对于WPF来说比新手还要新,还没有掌握布局工具。看起来UserControl在一个Canvas内,该Canvas被配置为在宽度和高度上进行拉伸。 - scubasteve
请将代码发送给我们以便帮助您解决问题。 - Emond
@Erno - 我添加了XAML,希望这对你有用。 - scubasteve
@stevek,您能否添加使用此UserControl的Xaml代码呢? - Emond
7个回答

8

我知道这个问题被问了很久,但发表更多信息是没有害处的 :)

我使用这段代码来查找应用了渲染变换的视觉元素的大小(比例)。

GeneralTransform t = transformedVisual.TransformToVisual(parentVisual);

Vector topLeft = (Vector) t.Transform(new Point(0,0));
Vector topRight = (Vector) t.Transform(new Point(originalWidthOfTransformedVisual, 0));

double renderedWidth = (topRight-topLeft).Length;
double widthScale = renderedWidth / originalWidthOfTransformedVisual;

4
请注意,更一般而言,可以简单地计算相对于父元素的可视范围的边界:Rect bounds = t.TransformBounds(new Rect(new Size(transformedVisual.ActualWidth, transformedVisual.ActualHeight));。当然,为了使此方法生效,transformedVisual需要是一个FrameworkElement,而不仅仅是一个简单的Visual(后者本身没有任何尺寸信息)。 - Peter Duniho

2

你好,我曾经遇到过类似的问题。如果之前没有显示视图,请强制进行渲染。

        uiElement.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
        uiElement.Arrange(new Rect(new Point(0, 0), uiElement.DesiredSize));
        Size _size = uiElement.DesiredSize;

在我这种情况下,这至少可以强制UIElement呈现其大小


1

0

基于@bor的回答,我简化了代码以便更常见的使用:

private static Rect GetTransformedVisualBounds(Visual source, Visual target, Rect bounds)
{
    return source.TransformToVisual(target).TransformBounds(bounds);
}
  • source 是一个可应用变换的可视化对象;
  • target 是将坐标转换为其所在的可视化对象;
  • bounds 是源可视化对象未经变换的边界;

以下是上述方法的使用示例:

private void ExampleOfUse()
{
    Border border = new Border()
    {
        Width = 100,
        Height = 100
    };
    ContainerVisual container = new ContainerVisual();
    container.Children.Add(border);
    container.Transform = new ScaleTransform(2d, 2d);

    Rect transformedBounds = GetTransformedVisualBounds(container, this, VisualTreeHelper.GetDescendantBounds(container));

    // This should print a rectangle with a size of 200x200;
    Debug.Print($"Transformed bounds: {transformedBounds}");
}

这是一个非常古老的问题,但它对我很有用,我希望它对其他人仍然有用。


0
public static Rect GetRelativePlacement(this UIElement element, UIElement relativeTo)
{
    var absolutePos = element.PointToScreen(new Point(0, 0));
    var posRelativeTo = relativeTo.PointToScreen(new Point(0, 0));

    var topLeft = new Point(absolutePos.X - posRelativeTo.X, absolutePos.Y - posRelativeTo.Y);
    var bottomRight = element.PointToScreen(new Point(element.RenderSize.Width, element.RenderSize.Height));

    var bounds = Rect.Empty;
    bounds.Union(topLeft);
    bounds.Union(bottomRight);

    return bounds;
}

0

你说你使用了Visual的宽度和高度。 首先,有两个问题。 Visual http://msdn.microsoft.com/en-us/library/system.windows.media.visual.aspx 没有任何高度和宽度的属性。

现在,假设你有一个FrameworkElement,它确实有一个高度和宽度的属性,那么你是使用Height和Width属性还是ActualHeight和ActualWidth属性? 如果不是,那么你要找的就是这些属性。

如果你正在使用它们,或者你没有使用FrameworkElement,那么你应该发布一些代码。


抱歉造成困惑。实际上这是一个用户控件,我正在使用ActualWidth和ActualHeight属性(我已经更新了原始消息以澄清这一点)。 - scubasteve

0
如果您可以使用动画来执行 Transform,那么动画提供了 Completed 事件。这应该是提取新转换大小的好地方。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接