开发者

How to obtain the scaled size of a WPF Visual element

I am rendering a WPF Visual (UserControl) to a bitmap but the problem is that the rendered image is the size of the UserControl before it is scaled/transformed. So let's say the UserControl was designed at 200x200 pixels. When I render to BMP I'm using the UserControl's ActualWidth and ActualHeightt which r开发者_如何学JAVAeport 200 and 200 respectively. Problem is the UserControl is in a Canvas and is auto sized (set to scale/fill with the Window size) to something closer to 1200 x 1200 (it changes)

I've done some reading and searching and so far can't figure out how to determine the effective size, that is the size the control is being painted on screen.

I came across this question which sounded hopeful but the Transform returned does not contain scaling data. Well it does, but they are both 1. Get element position after transform

Any suggestions on where to look for the render scaling would be great!

[UPDATE] As suggested, I'm including the relevant code:

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

[Update #2] Added the XAML - The Grid element was removed because it was HUGE and from my reading of the XAML the Canvas containing the keyboard UserControl was NOT part of the Grid element.

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


I know lots of time passed since the question was asked but it doesn't hurt to post some more info :)

I use this code to find size (scale) of visuals with applied render transform(s).

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;


Hi i had a similar problem if the visual wasn't displayed before to force this rendering

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

this works at least in my case to fore the UIElement to Render it Size


If there is a RenderTransform you will hardly be able to determine the size unless you you some serious calculations

In most other cases you will be able to get the size of the FrameworkElement by using the ActualHeight and ActualWidth properties. Still, these properties will give you the size of the bounding box which will be sufficient for the scenario you describe.


You say you use the Visual's width and height. First, there are two problems. A Visual, http://msdn.microsoft.com/en-us/library/system.windows.media.visual.aspx does not have any Properties for Height and Width.

Now, assuming you have a FrameworkElement, which does have a Height and Width property, are you using the Height and Width Properties, or the ActualHeight and ActualWidth properties? If you are not, then those are the properties you are looking for.

If you are using them, or you are not using a FrameworkElement, then you should post some code.


If you could use animation to perform the Transform then animation provides Completed event. That should be a good place to extract new transformed size.


Based on @bor's answer, I simplified the code to allow a more common use:

private static Rect GetTransformedVisualBounds(Visual source, Visual target, Rect bounds)
{
    return source.TransformToVisual(target).TransformBounds(bounds);
}
  • source is a Visual which can have transformations applied;
  • target is the Visual to which the coordinates will be converted to;
  • bounds is the non-transformed bounds of the source Visual;

The following is an example of use of the above method:

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

This is a very old question, but it was useful for me and I hope it is still useful for someone else as well.


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

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜