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.
Related
I have a class that extends a Canvas. On creation I explicitly set its width to a value:
Public Sub New(w As Integer, h As Integer)
Me.Width = w
Me.Height = h
End Sub
Now I add instances of this to a canvas to another canvas using cnvPreview.Children.Add(e) where e is the instance of my extended canvas.
However when I run the application, the ActualWidth and ActualHeight of these canvases are 0.
How can this be fixed?
Don't Canvas's shrink to the smallest size possible, yet still display fully their own children? Might this be causing the ActualHeight and ActualWidth values to be zero. Try adding say, a TextBlock and reading the values then.
I believe that this is explained better on this other post: Why are ActualWidth and ActualHeight 0.0 in this case?
Note that ActualHeight and ActualWidth will give you the values after rendering the control. Suppose if the Canvas Height and Width is 50 and after rendering the Canvas is shrinked then it will return a lower ActualHeight and ActualWidth value or maybe zero if not visible.
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.
How to calculate increased width and height of the scale transform applied control in WPF?
public static Rect GetAbsolutePlacement(this FrameworkElement visual)
{
Point topLeft = visual.PointToScreen(new Point(0, 0));
var bottomRight = visual.PointToScreen(new Point(visual.ActualWidth, visual.ActualHeight));
var bounds = Rect.Empty;
bounds.Union(topLeft);
bounds.Union(bottomRight);
return bounds;
}
You can use the ActualHeight and ActualWidth properties. These return the true values of the controls, not the values you have requested. Though this is the value after the control has been rendered.
If you want to know what the height and width will be then you could apply your transformation to the request sizes, but these might not match what the actual values will be.
The MSDN has more information.
There is a difference between the properties of Height and Width and ActualHeight and ActualWidth. For example, the ActualHeight property is a calculated value based on other height inputs and the layout system. The value is set by the layout system itself, based on an actual rendering pass, and may therefore lag slightly behind the set value of properties, such as Height, that are the basis of the input change.
Because ActualHeight is a calculated value, you should be aware that there could be multiple or incremental reported changes to it as a result of various operations by the layout system. The layout system may be calculating required measure space for child elements, constraints by the parent element, and so on.
I'm trying to use the OpacityMask property combined with a VisualBrush so that when you drag an image over another control (such as another image, rectangle, or any other control), the part of the image that is over the second control has a different opacity. That is, the image has some non-zero base opacity, and any part of the image that is over another control has a different (again, non-zero) opacity.
Is this possible simply using VisualBrush and OpacityMask? Or is a more complex approach required?
Thanks!
Edit: I'm trying to make the image have some lower opacity (such as 0.5), and the part being dragged over the control have a higher opacity (such as 1.0). I originally left out this detail, which is important to the approach taken.
In addition to ima's answer, I have figured this out using an opacity mask. I use the following code hooked into the LayoutUpdated event for the image.
// Make a visual brush out of the masking control.
VisualBrush brush = new VisualBrush(maskingControl);
// Set desired opacity.
brush.Opacity = 1.0;
// Get the offset between the two controls.
Point offset = controlBeingMasked.TranslatePoint(new Point(0, 0), maskingControl);
// Determine the difference in scaling.
Point scale = new Point(maskingControl.ActualWidth / controlBeingMasked.ActualWidth,
maskingControl.ActualHeight / controlBeingMasked.ActualHeight);
TransformGroup group = new TransformGroup();
// Set the scale of the mask.
group.Children.Add(new ScaleTransform(scale.X, scale.Y, 0, 0));
// Translate the mask so that it always stays in place.
group.Children.Add(new TranslateTransform(-offset.X, -offset.Y));
// Rotate it by the reverse of the control, to keep it oriented correctly.
// (I am using a ScatterViewItem, which exposes an ActualOrientation property)
group.Children.Add(new RotateTransform(-controlBeingMasked.ActualOrientation, 0, 0));
brush.Transform = group;
controlBeingMasked.OpacityMask = brush;
If you want a desired base opacity, use two images; one that's always at the base opacity, and another that uses the opacity mask that sits on top of it. If you want the base opacity to be higher than the masked opacity, then it might be easier to use ima's approach.
One advantage of this solution as opposed to the maskless approach is that if the masking control moves, changes size, etc., this will automatically pick up the change without having to keep another control in sync with it.
Here's how it looks:
(source: yfrog.com)
No masks
Define visual brush for the control
Paint shape right on top of the control with that brush
Drag image between the shape and the control
Set opacity of the brush to achieve desired effect
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))