I have a WPF application in which I am attempting to take a screen grab of a WindowsFormsHost control. To do this I am doing a Graphics.CopyFromScreen. On most boxes we have tested on this works perfect, however we have one machine that is not grabbing the right size values. The width and height that are given by WPF do not match the actual width and height of the control. When snooping, the incorrect values show for the ActualWidth and ActualHeight of the control as well. Even further, when I snoop the main window (which is maximized), I get an ActualWidth of 1550 and and ActualHeight of 840, but my screen resolution is 1920x1080. I would understand being a few pixels off for borders, margins, etc, but to have WPF tell me my maximized window is 370 pixels shorter in width than the actual screen just doesn't make sense. Can anyone explain this behaviour?
The following is the code being used to capture an image of the control:
public static Bitmap CreateBitmapFromVisual(this FrameworkElement target)
{
Rect bounds = VisualTreeHelper.GetDescendantBounds(target);
if (bounds.Width == 0 && bounds.Height == 0)
return null;
System.Windows.Point p0 = target.PointToScreen(bounds.TopLeft);
System.Drawing.Point p1 = new System.Drawing.Point((int) p0.X, (int) p0.Y);
Bitmap image = new Bitmap((int)bounds.Width, (int)bounds.Height);
Graphics imgGraphics = Graphics.FromImage(image);
imgGraphics.CopyFromScreen(p1.X, p1.Y, 0, 0, new System.Drawing.Size((int) bounds.Width, (int) bounds.Height));
return image;
}
Here is an image to better describe what I'm talking about:
As you can see, snoop is saying that the actualwidth and actualheight of the WindowsFormsHost is 486x336. Any debug information I log says the same thing. However, when I Print Screen and crop to the control in paint, the actual size is 608x423, a sizeable difference.
As a further update, it seems like the width given by wpf is roughly 80% of the actual width. This is true for both the control and the window.
Have you looked at your DPI settings for your display?
To do this go to Control Panel - Display and select Set custom text size (DPI). In the dialog that opens you will see a scale to percentage. If it is not set at 100% then try this. This could be your issue.
Related
I have to crop some control to show only a half of it but the rest should be transparent and clickable so it is not enough to cover the control with something. The result should give a control with only half of the content (for example 50% of top) and the rest should be cropped (not hidden) so some other control below should be visible and not overlapped by cropped part. New control should also scale when window is scaled. How to do this in WPF?
I have finally did the trick using Border around the control and Clip property of this border was set to Multibinging that was generating Rectangle basing on ActualWidth and ActualHeight of my control
Maybe GridSplitter:
http://www.wpf-tutorial.com/panels/gridsplitter/
Can be used to split views horizontally/vertically, and can be responsive.
In Windows Forms, you have a PreferredSize property that will tell you how large a control would like to be.
Where's that property in WPF?
I have a Grid with some content (of unknown size) and would like to create an animation that increases the grid in height from 0 up to its preferred (auto) height. Of course the grid is either at 0 height or collapsed at the beginning, because it's not supposed to pop up in an instant but smoothly "fade in". So I cannot use the ActualHeight property for the animation target because it is always 0. The opposite direction animation should be easier because I can animate from ActualHeight (or just no explicit start value) to 0.
Given a FrameworkElement (element) and I wish to allow it to expand fully and then measure it's size I do the following:
element.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
element.Arrange(new Rect(new Point(0, 0), element.DesiredSize));
element.UpdateLayout();
Size sizeElementWantsToBe = element.DesiredSize;
Caveat: I found this question because I'm having problems with this method of triggering layout with Telerik RadGridViews with templated columns. But I doubt you will have that problem.
Hope this helps.
I derive from shape, this is what is in the DefiningGeometry
protected override Geometry DefiningGeometry
{
get
{
topLeft.X = Math.Min(Start.X, End.X);
topLeft.Y = Math.Min(Start.Y, End.Y);
width.X = Math.Abs(Start.X - End.X);
width.Y = Math.Abs(Start.Y - End.Y);
rectBounds.X = topLeft.X;
rectBounds.Y = topLeft.Y;
rectBounds.Width = width.X;
rectBounds.Height = width.Y;
rectGeo.Rect = rectBounds;
return rectGeo;
}
}
I see the fill, but not the stroke, since the sroke is additional to the width and height I tried to make some room for it by setting:
Width = width.X + StrokeThickness;
//same for height.
But then nothing gets drawn, does anyone know what I am doing wrong? By the way the background and the stroke brush are different color.
Stroke is always on top of Fill. Thicknesses below 1.0 are no problem at all, although very thin strokes naturally tend to become invisible.
Fill exactly fills the Shape's geometry. Stroke renders the geometry's outline, half of the stroke lying inside, half outside the shape.
Never add StrokeThickness to your Shape's width (which would only work as you expect on rectangles anyway). See the MSDN for how the Shape's properties behave.
Do not derive from Shape to create simple geometric objects. Use the predefined Rectangle, Ellipse, Line etc. Use Path for more complex geometries and set Path.Data.
Also consult the Shapes and Basic Drawing in WPF Overview and maybe the Geometry Overview in the MSDN.
StrokeThickness for some reason should be larger than 1 (I am guessing the Fill brush is covering it), or don't set the Fill property, and StrokeThickness 1 works.
I'm adding controls programmatically to a canvas which is all just wonderful...
var newControlPoint = new ControlPoint() { Width = 10, Height = 10 };
newControlPoint.SetResourceReference(Control.TemplateProperty, "ControlPoint");
SetCanvasPosition(newControlPoint, position.X - (newControlPoint.Width / 2), position.Y - (newControlPoint.Height / 2));
canvas.Children.Add(newControlPoint);
newControlPoint.UpdateLayout();
... but I'm coming unstuck when I attempt to remove the hardwired Width and Height settings from the first line...
var newControlPoint = new ControlPoint();
...the canvas positioning doesn't seem to take effect and the newly created control winds up at {0,0}.
Any ideas?
Two problems:
The Width and Height properties won't be set because you haven't explicitly set them. It's ActualWidth and ActualHeight you want, which are set by WPF.
The controls haven't been laid out yet, so ActualWidth and ActualHeight will be zero.
To work around the problem you could:
Use databinding instead of doing a one-off calculation.
Attach the positioning logic to the control's Loaded event so it has been positioned.
Use Dispatcher.BeginInvoke to run the layout logic in a separate message that runs at a lower priority to layout, thus ensuring the ActualWidth and ActualHeight have been calculated and assigned.
Consider the following:
Let's say the Window is 1024x768 and the ViewBox fills the entire window,
this means the TextBox is really large on the screen.
I want to get the size of the TextBox as it is currently on the screen.
If I get DesiredSize or ActualSize or even RenderedSize I always get 100.
Any suggestions?
Update: I could probably get the ActualWidth of the ViewBox and divide it by the ActualWidth of it's child which would give me the current scale factor and expose that as a property somehow but I'm not sure that's the best way to do it.
This is how you get the ScaleTransform the ViewBox exerts on its children:
var child = VisualTreeHelper.GetChild(viewBox, 0) as ContainerVisual;
var scale = child.Transform as ScaleTransform;
Here viewBox is the ViewBox that textbox sits in.
Then you can just multiply scale.ScaleX * textBox.ActualWidth and you get the size in Screen coordinates
But it gets even easier! To get that textbox's size directly in Screen Coordinates you do:
textbox.PointToScreen(new Point(textbox.ActualWidth,textbox.ActualHeight)) - textbox.PointToScreen(new Point(0,0))