Archive

Posts Tagged ‘Scaling’

HowTo: Scale control arround its center using a render transform

In the context of ClipFlair development, I was recently adding independent scaling (zooming) functionality to its ZUI container’s floating windows (apart from the container zooming functionality) and came across some strange behavior, where the windows seemed to also move apart from getting scaled.

After banging my head a bit I decided to take a closer look at Microsoft’s documentation and noticed the following text at http://msdn.microsoft.com/en-us/library/system.windows.uielement.rendertransformorigin.aspx

"RenderTransformOrigin has a somewhat nonstandard use of the Point structure value, in that the Point does not represent an absolute location in a coordinate system. Instead, values between 0 and 1 are interpreted as a factor for the range of the current element in each x,y axis. For example, (0.5,0.5) will cause the render transform to be centered on the element, or (1,1) would place the render transform at the bottom right corner of the element. NaN is not an accepted value. Values beyond 0 and 1 are also accepted, and will result in more unconventional transform effects. For instance, if you set RenderTransformOrigin to be (5,5), and then apply a RotateTransform, the rotation point will be well outside the bounds of the element itself. The transform will spin your element around in a big circle that originates beyond bottom right. The origin might be somewhere inside its parent element and could possibly be possibly out of frame or view. Negative point values are similar, these will go beyond the top left bounds. Render transforms do not affect layout, and are typically used to animate or apply a temporary effect to an element."

Since ClipFlair’s FloatingWindowHostZUI template uses a Canvas to host its FloatingWindows, I obviously didn’t care about the phrase “Render transforms do not affect layout”, but the phrase “values between 0 and 1 are interpreted as a factor for the range of the current element in each x,y axis” rang a bell immediately.

Misguided by the poor Intellisense info for the RenderTransformOrigin property, I had thought that point was in the control’s coordinate system, and since I wanted to scale the control arround its center, I had used the following erroneous statement:

window.RenderTransformOrigin = new Point(window.ActualWidth/2, window.ActualHeight/2);
window.RenderTransform = new ScaleTransform().SetScale((double)e.NewValue);

instead of the correct one:

window.RenderTransformOrigin = new Point(0.5, 0.5);
                                                        //scale arround the window center
window.RenderTransform = new ScaleTransform().SetScale((double)e.NewValue);

That is the range 0 to 1 for x & y coordinates of RenderTransformOrigin refers to the UIElement region, whereas less or greater values are (proportionally) outside of it, useful for example if you want to rotate an object arround an external point with a RotateTransform.

Don’t get puzzled by the expression new ScaleTransform().SetScale(…), it’s a syntax I use for authoring portable source code between WPF and Silverlight (since Silverlight only has a parameter-less constructor for ScaleTransform and anyway WPF doesn’t have a constructor that takes a single parameter for both X and Y scale values).

To sum up, here’s the “Scale” property I added to the FloatingWindow class:

#region public double Scale   
/// <summary> /// Gets or sets current window scale. /// </summary> /// <value>Current scale.</value>
public double Scale {  
get { return (double)GetValue(ScaleProperty); }
  set { SetValue(ScaleProperty, value); } } /// <summary>
/// Identifies the <see cref="FloatingWindow.Scale" /> dependency property.
/// </summary>
/// <value>
/// The identifier for the <see cref="FloatingWindow.Scale" /> dependency property.
/// </value>
public static readonly DependencyProperty ScaleProperty =
    DependencyProperty.Register("Scale", typeof(double), typeof(FloatingWindow),
      new PropertyMetadata(1d, OnScalePropertyChanged));
//Must use 1d here, not 1 (else will get XAMLParseException at runtime)
/// <summary>
/// ScaleProperty PropertyChangedCallback call back static function.
/// </summary>
/// <param name="d">FloatingWindow object whose Scale property is changed.</param>
/// <param name="e">DependencyPropertyChangedEventArgs contains old and new values.</param>
private static void OnScalePropertyChanged(DependencyObject d,
DependencyPropertyChangedEventArgs e) {  
FloatingWindow window = (FloatingWindow)d;
  if (window != null)
  { 
    window.RenderTransformOrigin = new Point(0.5, 0.5); //scale arround the window center
    window.RenderTransform = new ScaleTransform().SetScale((double)e.NewValue);
}
} #endregion
%d bloggers like this: