Scaling multiple objects relative to a bounding rectangle - wpf

I'm trying to figure out the algorithm for scaling multiple selected objects on a canvas (similar to Visio's behavior). Say for instance I have the following selected objects in my application:
I then drag the lower-right handle of the bounding box to increase the size of the selected objects and thus produce the following results:
My questions are as follows:
How do I get the amount of scaling to be applied to each object?
How do I get the amount of translation to be applied to each object?
I hope this question makes sense. And I hope you could help.

Hi I dont think there is any Translation , There is only Scaleing . One easy way to do that is preserve the Width and Height of your object like (TextBoxes above) and then when you want to get Scaleing values of that object
ScaleTransform scale = new ScaleTransform();
//_text is the scaled object
scale.ScaleX = text.ActualWidth - _width; //_width is width of the textbox at beginning.
scale.ScaleY = text.ActualHeight - _height; //_height is the height of textbox at the beginning.
This will give you the amount by which object is scaled corressponding to the Width and Height of TextBox at the beginning (i.e When window initialized) . I hyope this will give you an idea.

Related

Fabric.js auto-size group controls when contained objects change size

When I have a group of fabric IText objects and I change all their font sizes together, the grouped text objects all shrink or grow according to the font size, but the group controls and border stay in place rather than also changing in size. My question is, how do I get the group controls to auto-size after changing the objects inside the group?
Looking through the code for Fabric, I can't figure out how the boundaries get set in the first place on the group. I have tried calling _setCoords() and setCoords() on the group, but that moves the group to 0, 0 on the canvas and keeps the height and width the same. Calling _setCoords(true) has no effect.
I have also tried the following code in which I attempt to replace the old group with a new one containing the same objects, which would theoretically draw new boundaries, but it seems like a clunky solution. Also, it doesn't work: the new group boundaries and controls do not appear.
var selected = [];
var all = scope.canvas.getObjects();
for(var x in all) {
if(all.active) {
selected[selected.length] = x;
}
}
scope.canvas._discardActiveGroup();
var objects = [];
for(var x in selected) {
objects[x] = scope.canvas.getObjects()[x];
}
var group = new fabric.Group(objects,{
originX: 'center',
originY: 'center',
canvas: scope.canvas
});
//Using $timeout so that this will wait until current $scope digest is finished
$timeout(function () {
scope.canvas.setActiveGroup(group);
scope.canvas.renderAll();
}, 0);
scope.canvas.renderAll();
EDIT:
I think this is related to another problem where after the text object changes size due to font size change, its selection area remains where it was before the size change. That is, it selects the object if you click within the pre-resize bounding rectangle, regardless of the post-resize bounding rectangle. Only after selecting some other object or deselecting all objects does selection work as expected for the object. I call setCoords() for the objects, so their boundaries are drawn correctly.
There were two parts to this problem, as I said in my edit to the question. Solving my problem required both of the solutions below.
Bug #1: IText object's selection area remains as it was before size change even if you use setCoords(). This is because setCoords() "sets corner position coordinates based on current angle, width and height" -- but it does not update width and height based on changes to the text. That only occurs during the method _renderViaNative(ctx):
//inside _renderViaNative:
this.width = this._getTextWidth(ctx, textLines);
this.height = this._getTextHeight(ctx, textLines);
Solution: Because I am actually using a custom descending class of IText, I used an override of the setCoords function to fix it. Something like this override should really be part of the IText class:
/** #Override */
setCoords : function () {
var ctx = this.canvas.getContext();
var textLines = this.text.split(this._reNewline);
this._setTextStyles(ctx);
this.width = this._getTextWidth(ctx, textLines);
this.height = this._getTextHeight(ctx, textLines);
this.callSuper('setCoords');
},
Bug #2: The group's selection area does not refresh after the IText object(s) that it contains change size, even if you call setCoords. If you call _calcBounds with a false parameter, it sets the group's center point by calculating the objects' center point -- but because the objects have been reset to have coordinates relative to the group center as (0,0) for them, the recalculation comes up with (0,0) as the center, and therefore, the whole group moves so that its center is at (0,0) on the canvas. If you call _calcBounds with a true parameter, it resizes around its original center, rather than around the new center, and you can't manually reset the center because that will make the objects move, too.
Solution: Recalculate the boundaries from scratch by resetting the objects' coordinates. I did this mainly by cannibalizing the function addWithUpdate(object).
// set the objects to their not-in-group coordinates
activeGroup.destroy();
// since _restoreObjectsState (inside destroy) set objects inactive, set them active
activeGroup.forEachObject(activeGroup._setObjectActive, activeGroup);
// now we can call _calcBounds without bouncing the group to 0,0
activeGroup._calcBounds();
// set the objects to their in-group coordinates
activeGroup._updateObjectsCoords();

Silverlight horizontal stretch and get position issue

I have a Grid (container) wich in turn has several grids(subContainers) arranged by rows. Each one of those "subContainers" has diferent columns and controls. And each of those "subContainers" has the horizontal alignment set to stretch, and it has to stay that way, since the layout this viewer depends on it.
I use the "container" to set each control on it's adequate position. So far so good. Now comes my headache... I want to remove the control from the grid and put it in a canvas, at the same exact position, only, the position it returns is as if the control is set to the beggining of the grid and not it's true position.
For testing purposes, I've set the "subContainters" horizontal alignment to center and (despite the layout is totally wrong) every control is in it's right position when sent to a canvas, wich it doesn't happen when HA = stretch.
Here's the code I'm using to get position:
GeneralTransform gt = nc.TransformToVisual(gridZoom);
Point offset = gt.Transform(new Point());
So you can understand, for example, my first control should be somewhere like (80, 1090), but the point that I get is (3,3).
Can anyone help me? Thanks
You should try using the top level container (which contains all other subcontainers) to get the offset point.
Also you can try:
var transform = mySubSubSubElement.TransformToVisual(Application.Current.RootVisual);
var position = transform.Transform(new Point(0, 0));

What's the simplest way to calculate a controls' inner display rectangle (with Padding subtracted)?

A control reports its display rectangle in .DisplayRectangle -- what is the simplest way of finding the area available for the control if .Padding is set?
For example, a Label has .DisplayRectangle always equal to .ClientRectangle, even if .Padding is not (0,0,0,0). I want to obtain the size of the rectangle in .ClientRectangle after paying attention to the .Padding setting (and whatever additional properties might limit the "net" display area, for that matter).
Isn't there some method, or other framework-supported way to do it? Do I have to re-code all those rectangle calculations that probably are already in the framework code?
Padding isn't simply a straight modification to Display Rectangle. Some built-in controls factor it in, some do not.
Best is to read the documentation for Padding and see which parts apply to you.
The hopefully on-target simple answer is "No, there's no framework method. Build your own like below"
private Rectangle GetPaddedRectangle(Control control)
{
var rect = control.ClientRectangle;
var pad = control.Padding;
return new Rectangle( rect.X + pad.Left,
rect.Y + pad.Top,
rect.Width - (pad.Left+pad.Right),
rect.Height - (pad.Top+pad.Bottom));
}

How to calculate the size of the scale transform applied control

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.

How to determine width of a windows forms controls whose width was increased because it is Anchored?

I currently have a Textbox on a Windows Forms, and I need to dynamically add a PictureBox box control at the right of the Textbox.
I create the PictureBox programmatically and I when setting the location of the PictureBox, i'm setting like this:
pBox.Location = new Point(tbControl.Location.X + ctrl.Width, ctrl.Location.Y);
So i'm setting the picture box to be located at the X location of the textbox PLUS the width of the textbox. However, since the textbox has an anchor property set to right, its width increases to fill the space between itself and the form border.
Problem is, that even though the textbox's width is visually bigger than the actual value of Textbox.Width. the Width property is not taking into account the extra width of being anchored.
I already tried properties like Textbox.Bounds.Width, Textbox.ClientSize.Width, Textbox.DisplayRectangle.Width, etc. with no luck. All of those properties return the original Width of the control without taking into account the resized width due to the Anchor property.
Does anyone know how I can determine the real size of the textbox? Thank you
The Width property always tracks the current width of a control, whether it is anchored or not. However, the TextBox is going to grow when you make the container larger and that will make it overlap the PictureBox. You have to anchor the PB to the right as well.
These should be returning the adjusted size. Either you are referring to the wrong textbox, or you are doing the query before the size has actually changed.

Resources