Archive
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
HowTo: Type in double-quote in Windows Live Writer
A big nuissance in Windows Live Writer is that when you try to type in a double-quote character you get some special Unicode character (“ or ”, at the start and end of a string respectively), other than the classic ASCII character used in programming.
That way people copy-pasting snippets from your blog can get lots of errors. I remember having that issue sometime ago with people copy-pasting (into a .reg text file) a registry script to add Google search engine into Copernic Agent from my blog and not understanding why it wasn’t working as expected.
However, I found a keyboard shortcut to work-arround this issue. You just press BACKSPACE key right after typing in a double-quote character in Windows Live Writer to restore it a real ASCII double-quote.
HowTo: Bind to a DataContext property named Source in XAML
While refactoring ClipFlair code to use MVVM (Model-View-ViewModel) pattern, I came across the XAML error “Object reference not set to an instance of an object.”, shown in Visual Studio when trying to bind to my ViewModel (accessed implicitly, being set as the DataContext of the XAML control) for a property named Source.
I have settled down to using the following MVVM-style pattern (of my own) at ClipFlair:
Update: Later on, at ClipFlair project, I renamed folder “Views” to “ViewModels” and folder “Components” to “Views” and moved “ViewModels” folder inside the “Views” one to keep view-related stuff in one place.
IImageViewer.cs:
ImageView.cs:
Update: Later on, I refactored this to initialize the fields directly instead of at the constructor:
//can set fields directly here or at the constructor
private Uri source = IImageViewerDefaults.DefaultSource;
ImageViewerWindow.xaml.cs (the so-called codebehind for the XAML control):
Note above how we set the "DataContext" of the control to our ViewModel, so that we can bind to its properties implicitly (without referencing it) in the XAML.
Update: There’s something very important I had forgotten to do at the implementation of the “View” property in the code above, that is to listen for PropertyChangeEvents emitted by the ViewModel. In specific, we need to listen for changes of the ViewModel’s “Source” property to keep the View’s “SourceProperty” (a DependencyProperty that is also accessed via the View’s “Source” property) in sync with the ViewModel’s “Source” property. So the code shown in the above screenshot has to be fixed by changing the “View” property’s implementation like below:
For extra safety in case you want to allow setting a null ViewModel to the View property, you could check for (value != null) before trying to add PropertyChanged event handler to it:
if (value != null)
value.PropertyChanged += new PropertyChangedEventHandler(View_PropertyChanged);
Obviously, if more DependencyProperties are added to the component (e.g. to be able to set them declaratively from XAML when instantiating the component) that have to be in sync to respective ones at the ViewModel (which is held by the View property), then the “View_PropertyChanged” method (event handler) implementation has to be extended with “else if” statements for each of those properties to set the respective dependency properties from the matching ViewModel properties.
BTW, if your ViewModel passes null to the changed property name to mark a single change event for multiple properties, then you have to also take that in mind (else you will get exception when you try to call “Equals” method on null string), by doing something like below:
if (e.PropertyName == null) {
Source = View.Source;OtherProperty = View.OtherProperty;
//…sync all properties since we are not told which ones changed
return;}
else if (e.PropertyName.Equals("Source") {
Source = View.Source;}
else if (e.PropertyName.Equals("OtherProperty")
{OtherProperty = View.OtherProperty;
}
//…
Note that instead of “.Equals” one could have also used “==” operator, since the C# compiler maps the string equality operator to string’s “.Equals” method.
Update: Another retouch I have since done to the code above is to avoid hardcoding property names and their default values and use nstead ithe proprerty name constants defined at the class IImageViewerProperties and the respective default values defined at IImageViewerDefaults. For example, I prefer to use (after adding a “using ClipFlair.Models.Views;” to the top):
public static readonly DependencyProperty SourceProperty =
DependencyProperty.Register(IImageViewerProperties.PropertySource, typeof(Uri), typeof(ImageWindow), new FrameworkPropertyMetadata((Uri)IImageViewerDefaults.DefaultSource,
new PropertyChangedCallback(OnSourceChanged)));
Update: I eventually opted for using a “switch” statement instead of nested if / else if statements, to keep things cleaner when more properties are added:
protected void View_PropertyChanged(object sender,
PropertyChangedEventArgs e)
{
if (e.PropertyName == null)
{
Source = View.Source;
//...
}
else switch (e.PropertyName)
//string equality check in .NET uses ordinal (binary) comparison
//semantics by default
{
case IImageViewerProperties.PropertySource:
Source = View.Source;
break;
//...
}
}
ImageViewerWindow.xaml (the XAML control):
The issue at the last screenshot is that you can’t write Source="{Binding Source}" as you’d write Source="{Binding SomeDataContextProperty}", but need to write Source="{Binding Path=Source}" instead, since Source is a keyword at Binding expression syntax.
BTW, note that at the “edImageURL” TextBox I use Mode=TwoWay (two-way binding, the default is one way binding from source [the DataContext in our case] to target), whereas at the Image control I don’t. Both get an image URI from the ViewModel’s “Source” property, but we also want to be able to edit the URI at the textbox and update the view’s Source property (which will in turn update the Image control with the new image).
Also, note that apart from passing a URI to the Source property of an Image control there’s also a more verbose syntax one could use above, which can be handy to know of:
<Image>
<Image.Source><BitmapImage UriSource="{Binding Path=Source}" />
</Image.Source>
</Image>
The <Image.Source> tag here is using the so-called property element syntax pattern. In the snippet above it sets a value to the Source property of the Image tag. XAML can use both XML attributes and child elements to set values to properties of controls, but whereas the attribute name would be “Source”, the child element tag has to be “Image.Source”, not just “Source”, since the XML schema needs to have unique definitions for the XML tags, whereas multiple objects could define different Source properties causing naming collisions.
According to http://10rem.net/blog/2012/03/27/tip-binding-an-image-elements-source-property-to-a-uri-in-winrt-xaml this more verbose syntax is needed at Windows 8 Metro-style apps (that is for the WinRT API) for binding an Image to a URI (but since that article is back from March 2012, probably this is not an issue any more).
Single statement ScaleTransform initialization in both WPF and SL
With Silverlight using a cut-down .NET API, some decisions have been at least ackward, causing Silverlight code to be sometimes unnecesserily more complex than its WPF counterpart and WPF code needing several changes to get ported for Silverlight. In ClipFlair I’ve implemented a WPFCompatibility layer to ease compiling WPF code for Silverlight (and vice-versa too) without source code changes.
One of the missing things from Silverlight is at ScaleTransform class, where only a parameterless constructor is available. That way, whereas in WPF you can construct a ScaleTransform and initialize it in one statement, in Silverlight you need to first construct the transform and then set its X scale and Y scale in two separate (property setter) statements. Obviously this also has a small cost in performance since you end up with 3 method calls instead of a single one.
That is the WPF statement
ScaleTransform t = new ScaleTransform(someXscale, someYscale);
has to be translated in Silverlight to:
ScaleTransform t = new ScaleTransform();
t.ScaleX = someXscale;
t.ScaleY = someYscale;
In WPF_ScaleTransform.cs file at WPF_Compatibility project, I provide two different approaches to aid in more portable code that uses ScaleTransform:
1) The first approach is to create a method named “new_ScaleTransform” that takes X & Y scale parameters and returns a new ScaleTransform object and use it instead of calls to ScaleTransform’s constructor that takes X and Y scale.
public static ScaleTransform new_ScaleTransform(double scaleX, double scaleY) //unfortunately there are is no extension method mechanism for contructors in C# yet (and the ScaleTransform class is sealed so we can’t create descendent class)
{
ScaleTransform result = new ScaleTransform();
result.ScaleX = scaleX;
result.ScaleY = scaleY;
return result;
}
Since WPF_Compatibility uses two separate projects WPF_Compatibility.WPF and WPF_Compatibility.Silverlight that share the same source files (added as file links from a common Source subfolder instead of copied into the projects), there is an extra optimization that I decided to do, using a conditional compilation statement so that in WPF we use more optimal code:
public static ScaleTransform new_ScaleTransform(double scaleX, double scaleY) //unfortunately there are is no extension method mechanism for contructors in C# yet (and the ScaleTransform class is sealed so we can’t create descendent class)
{
#if SILVERLIGHT
ScaleTransform result = new ScaleTransform();
result.ScaleX = scaleX;
result.ScaleY = scaleY;
#else
ScaleTransform result = new ScaleTransform(scaleX, scaleY);
#endif
return result;
}
So, with the above approach one will be able to write in both WPF and Silverlight (assuming they add a “Using WPF_Compatibility;” to the top of their C# source file):
ScaleTransform t = new_ScaleTransform(someXscale, someYscale);
2) The second approach is to create a (static) extension method (called SetScale) for the ScaleTransform class and have that method accept X and Y scale params (apart from the implicit ScaleTransform object), which, after setting X and Y scale, should return the ScaleTransform object so that one can construct a ScaleTransform with the parameterless constructor and daisy chain a call to the SetScale method in the same statement. Since that method returns the ScaleTransform object that was passed to it (after setting X and Y scale to it), the combination behaves like a parametric constructor.
public static ScaleTransform SetScale(this ScaleTransform transform, double scaleX, double scaleY)
//can use this in both WPF and Silveright (the last one misses a parametric
//constructor) to initialize on the same statement on which we construct the
//ScaleTransform
{
transform.ScaleX = scaleX;
transform.ScaleY = scaleY;
return transform; //return the transform so that it can be used in the form
ScaleTransform t = new ScaleTransform().SetScale(scaleX, scaleY)
}
After you import the namespace WPFCompatibility (via the using statement mentioned above), where the WPF_ScaleTransform static class belongs, you can make use of this static extension method like below:
ScaleTransform t = new ScaleTransform().SetValue(someXscale, someYscale);
As an added bonus, an extra extension method is provided to set both the X and Y scale from a single parameter:
public static ScaleTransform SetScale(this ScaleTransform transform, double scale)
{
return transform.SetScale(scale, scale);
}
Update:
In recent versions of C# (not sure if VB.net has such a feature) you can initialize multiple properties for a new instance in a single statement at the time of construction, so I have updated the “new_ScaleTransform” method like below. It still works at both WPF and Silverlight, while both get a single-line statement (the WPF version should still be the fastest, even though both are now one-liners).
public static ScaleTransform new_ScaleTransform(double scaleX, double scaleY)
//unfortunately there are is no extension method mechanism for contructors
//in C# yet (and the ScaleTransform class is sealed so we can't create
//descendent class) { #if SSILVERLIGHT
ScaleTransform result =
new ScaleTransform() { ScaleX = scaleX, ScaleY = scaleY }; #else ScaleTransform result = new ScaleTransform(scaleX, scaleY); #endif return result; }
HowTo: Compress speech audio using CSpeex codec at Silverlight apps
Following up on my post on how to fix CSpeex (http://cspeex.codeplex.com) to compile at Silverlight 5 (see http://zoomicon.wordpress.com/2012/06/09/fix-compile-cspeex-audio-codec-at-silverlight-4-rc-and-silverlight-5/).
Initially I thought it was not working correctly, but then I noticed I was running it on Terminal Server and didn’t have an audio device available. Running SLAudioDemo included in the CSpeex distribution (after doing the fix mentioned at my post above) seems to work fine on my local PC.
The SLAudioDemo demonstrates capturing audio into memory as a compressed CSpeex stream which it can decompress and save into a .WAV file that one can play say with Windows Media Player. The WAV is uncompressed, since SLAudioDemo compresses in memory (via StreamAudioSink) and decompresses to normal PCM WAV when you save (else it would be unplayable).
Obviously the Speex codec is useful for uploading (e.g. via HSS Interlink) compressed audio to a server (or to the other end at some voice chat app) and decompress there before playback (similarly the other way around).
At the server side if you want to do audio storage too, you could also keep an extra decoded copy of the compressed Speex audio data, recoded into WMA using automation of Microsoft Expression Encoder for example (see Microsoft Transform Manager, can do this in the background if you uncompress into a .WAV file at some incoming folder). That way you could serve that audio to your Silverlight client as Speex-encoded data file, but also to other users that use Windows Media Player to get the audio file from some URL etc. Could also encode to MP4/AAC or MP3 but there are licensing issues with those from what I know.
For the CSpeex encoder see StreamAudioSink.cs. Only 2 lines are needed in MainPage.xaml.cs of SLAudioDemo:
streamAudioSink = new StreamAudioSink();
streamAudioSink.CaptureSource = _captureSource;
after you 1st declare at the top level (must have this as a top-level field, else the garbage collector can get it)
private StreamAudioSink streamAudioSink;
Here’s the decoding code that saves the WAV (from MainPage.xaml.cs):
if (sfd.ShowDialog() == true)
{
// User selected item. Only property we can get to is.
using (Stream stream = sfd.OpenFile())
{
JSpeexDec decoder = new JSpeexDec();
decoder.setDestFormat(JSpeexDec.FILE_FORMAT_WAVE);
decoder.setStereo(true);
Stream memStream = streamAudioSink.MemFile.InnerStream;
memStream.Position = 0;
decoder.decode(new RandomInputStream(memStream),
new RandomOutputStream(stream));
stream.Close();
}
}
Don’t get puzzled by the use of RandomInputStream / RandomOutputStream. These are from Java’s I/O package (java.io.*) that CSpeex has ported to .NET, since it’s in fact a port of JSpeex, a Java implementation of the Speex codec.
Vertical Centering with CSS
According to http://www.w3.org/Style/Examples/007/center.en.html#vertical
CSS level 2 doesn’t have a property for centering things vertically. There will probably be one in CSS level 3. But even in CSS2 you can center blocks vertically, by combining a few properties. The trick is to specify that the outer block is to be formatted as a table cell, because the contents of a table cell can be centered vertically.
That page points to a horizontal and vertical centering example (which seems to be a common need): http://www.w3.org/Style/Examples/007/center-example
To cater for various (mainly older) web browsers and peculiarities in CSS implementations, many vertical centering techniques have been developed. Here are some related links:
- http://blog.themeforest.net/tutorials/vertical-centering-with-css
(test page: http://douglasheriot.com/tutorials/css_vertical_centre/demo4.html) - http://www.vanseodesign.com/css/vertical-centering/
- http://hicksdesign.co.uk/journal/how-to-vertical-centering-with-css
- http://www.jakpsatweb.cz/css/css-vertical-center-solution.html
ASP.net WebPartZone skins and CSS
I was just examining a MonoX portal skin that our designer is authoring for the ClipFlair platform and noticed that in the Default.skin file, there’s a note pointing to http://support.microsoft.com/kb/911717 that speaks of an issue with Microsoft’s implementation of WebPartZone skins:
There it says:
The CssClass property does not affect the following style objects:
•The MenuLabelHoverStyle property
•The MenuPopupStyle property
•The MenuVerbHoverStyle property
•The MenuVerbStyle property
•The MenuCheckImageStyle property
Do not use the following line of code:
<MenuPopupStyle CssClass="wp_menupopup" />
…
while having a Stylesheet.css file with the following code:
.wp_menupopup
{
background-color:Red
}
Instead, use the following line of code:
<MenuPopupStyle backcolor="red" />
In short, at the Default.skin file one can give a CssClass and then define the styles at Stylesheet.css, but for MenuPopupStyle, MenuVerbHoverStyle, MenuVerbStyle and MenuCheckImageStyle this doesn’t work and you have to enter the object properties that correspond to the CSS attributes you want (e.g. background property of ASP.net for the background-color CSS attribute) directly into the Default.skin file for the respective objects.
BTW, the aformentioned Microsoft article speaks of Stylesheet.css, while MonoX template uses Default.css. This is because the ASP.net theming engine applies all .css files it finds in the theme folder (independent of filename), as explained at http://msdn.microsoft.com/en-us/library/ykzx33wh.aspx. That last Microsoft article has lots of useful information regarding Themes and how you can (optionally) combine them with CSS and on issues like scoping, precedence, security etc.
HowTo: Remove Babylon search from Mozilla Firefox address bar
Babylon Search is one of the most abusive addons for Mozilla Firefox and other web browsers. It installs with other free software (most probably profiting from tracking your searching habits) and then it is quite hard even for experienced users to completely uninstall it.
While maintaining other computers in the past I’ve used a combination of Add-Remove programs dialog, searching programs folder for Babylon text string and deleting related files, entering “about:config” at Firefox address bar and searching for Babylon in Firefox settings, right clicking each one of them to restore etc.
For the separate search bar it is easier to remove Babylon, since Mozilla has a dropdown menu there with search engine settings, however it’s sad that the aforementioned menu doesn’t control the address bar search.
However, in a recent incident I found an easier way to get rid of the crappy Babylon search which hijacks the search action when one types at Mozilla address bar some search query.
There’s a (quite hidden) action to reset Mozilla settings. You go to Mozilla’s menu (either from the old menu bar if visible, or from the newer yellowish button at the top-left of the Firefox window) and select Help/Troubleshoot problems and it takes you to an (internal) webpage that hosts a button you can use to reset all Mozilla settings to defaults (as if it was installed for the first time).
This is quite useful since even updating Mozilla (another hidden action, placed at Help/About dialog), won’t fix it if settings have been tampered with by malicious installers like Babylon’s. In fact some malicious addons for Mozilla even tamper its version setting so that it won’t update. Resetting settings as mentioned above can help with that, if not you may have to download and install the latest Firefox manually from http://www.mozilla.com (preferably download it in such a case from another browser like Internet Explorer if that seems to work OK).