Archive
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; }
How to compile code that uses WPF Decorator type in Silverlight
At ClipFlair I’m using a modified version of FloatingWindow control, where I try to make the WPF and Silverlight flavours of the original control share as much code as possible (this is work in progress currently, hoping to eventually have the WPF and Silverlight projects both link to the same source files [this is currently true for most of those files]).
With the ControlExtensions.cs file there (which has some useful extension methods for WPF and Silverlight controls), I had an issue, since they were very similar, apart from the fact that the WPF version had 2 methods that were using Decorator type in their implementation, whereas the Silverlight ones were identical apart from using the Border type.
WPF’s Border class extends WPF Decorator, whereas Silverlight’s Border doesn’t since Silerlight doesn’t have a Decorator class at all (to keep the size small for quick web installation they cut-off needless parts of WPF, that’s why they call Silverlight a small sibling of WPF [used to all call WPF/E if I remember well]).
Seems there’s a quick trick one can pull out to overcome this, based on C# support for “Type Aliases” with the using directive. You can add to the problematic source code file (either outside or inside any namespace declaration you have there), the following:
#if SILVERLIGHT using Decorator = System.Windows.Controls.Border; #endif
Then in that source code file you just use Decorator instead of Border for both WPF and Silverlight. Of course this works in method bodies, if you want to have that type in method parameters else well, you have to put the above type alias definition (called Using Alias Directive) in every source code file that uses this class. I’d prefer to put this in my WPF_Compatibility layer at ClipFlair codebase, but C# support for type aliases is very limited unfortunately, so I put a dummy WPF_Delegate.cs file there with the above usage info.
Isn’t it sad that you can place extension methods in existing namespaces, but can’t place type aliases similarly? In fact you can’t place say some of them in a file and refer to them alltogether, you have each user of your types replicate those using definitions to all code units where they use them. If you change somethine in the future in those definitions, things will break since some files will have outdated definitions (apart from having consumers of your types see unnecessary implementation details). I guess we need Alias Types, not Type Aliases (sic).
To see this in action, you can try opening in Visual Studio the FloatingWindow solution under Client folder of http://clipflair.codeplex.com source code base. You’ll notice the file Extensions/ControlExtensions.cs has a small arrow icon on it at both the Silverlight and the WPF projects, since it’s a link (added to both the projects using Add Existing File and clicking the dropdown menu next to the Add button to select “Add as Link”).
Speaking of file links in Visual Studio 2010, there seems to be a bug there. If you remove (not delete) a file from a project, then move that source file to another location (say one folder higher and into a Common subfolder), you can’t add the file to the project anymore till you Close the project (or right-click and Unload it) and Open it again (or reload it into the current Solution).
Zoom and Pan control for WPF and Silverlight (via WPF compatibility layer)
I’ve managed to combine
http://www.codeproject.com/Articles/85603/A-WPF-custom-control-for-zooming-and-panning
and a cut-down version of it for Silverlight that was out there:
http://www.codeproject.com/Articles/167453/A-Silverlight-custom-control-for-zooming-and-panni
I did it in a way that the Silverlight version is source-code compatible with the WPF version (via a WPF compatibility layer that even implements value coercion), so that both the WPF and the Silverlight projects share the same source code files.
The resulting libraries for WPF and Silverlight work with the existing samples (from the two articles mentioned above) without any changes to them.
Can checkout the latest version (plan to add the samples there too for convenience) under the "Client" subfolder of ClipFlair source-code base at http://clipflair.codeplex.com
IsolatedStorageSettings for WPF
System.IO.IsolatedStorageSettings doesn’t exist in WPF (only in Silverlight) but can easily be ported from Mono project’s Moonlight implementation (the Moonlight project home is at http://www.mono-project.com/Moonlight), as suggested at: http://groups.google.com/group/wpf-disciples/browse_thread/thread/4ed6c009f3fb7d69
I found that implementation at the following URL:
Did some modifications to IsolatedStorageSettings.cs to make it work with WPF (whether the application is deployed via ClickOnce or not):
// per application, per-computer, per-user
public static IsolatedStorageSettings ApplicationSettings {
get {
if (application_settings == null) {
application_settings = new IsolatedStorageSettings (
(System.Threading.Thread.GetDomain().ActivationContext!=null)?
IsolatedStorageFile.GetUserStoreForApplication() :
//for WPF, apps deployed via ClickOnce will have a non-null ActivationContext
IsolatedStorageFile.GetUserStoreForAssembly());
}
return application_settings;
}
}
// per domain, per-computer, per-user
public static IsolatedStorageSettings SiteSettings {
get {
if (site_settings == null) {
site_settings = new IsolatedStorageSettings (
(System.Threading.Thread.GetDomain().ActivationContext!=null)?
IsolatedStorageFile.GetUserStoreForApplication() :
//for WPF, apps deployed via ClickOnce will have a non-null ActivationContext
IsolatedStorageFile.GetUserStoreForAssembly());
//IsolatedStorageFile.GetUserStoreForSite() works only for Silverlight
}
return site_settings;
}
}
Note that you should also change the #if block at the top of that code to write
#if !SILVERLIGHT
Also can take a look at the following for custom settings storage:
http://f10andf11.blogspot.gr/2012/03/wpf-implement-isolatedstoragesettings.html
The resulting IsolatedStorageSettings.cs file for WPF (thanks to if !SILVERLIGHT block, the C# compiler will ignore it in Silverlight which has such class already) is included in the WPF_Compatibility layer at the ClipFlair source-code in http://clipflair.codeplex.com (checkout the “Client” subfolder in the source).
The WPF compatibility layer contains other goodies too like value coercion for Silverlight DependencyProperties using WPF-compatible syntax, so that your source code can stay source-level compatible with both WPF and Silverlight and be shared among respective projects.
Running E-Slate (Αβάκιο) on Windows 7 and Vista
For those who wonder what E-Slate is, it’s a componentized authoring environment for the creation of (interactive) educational microworlds, see my previous post for more info.
As I mentioned at that post, the latest official version (ignoring any unofficial / experimental versions that you might find around at some educational research labs’ websites) doesn’t work out of the box on Windows Vista and Windows 7, so below I provide some easy steps to make it work there too.
First of all, you navigate to http://e-slate.cti.gr with your web browser.
The you follow the download link on the left and locate the English and Greek installation files for Windows.
Note that the official version was never released for the Mac (although we had working prototypes running on both MacOS and MacOS-X), cause at the time Apple’s own JVM (MRJ) had serious issues (if I remember well they’ve moved on by now to use the official Java code under the hood). Some RA.CTI team has even recently made it to run one of the latest experimental E-Slate versions on Linux too (full source-code is now available with these latest versions).
Note that the installer also installs documentation, source code for the code parts and components that were OpenSource at that time (e.g. the Logo scripting engine) and several demo microworlds. However you can download more microworlds from the “microworlds” link on the left.
Then you download and run the appropriate installer (English or Greek, so that you get localized content in the demo microworlds). Do accept/allow at any security prompts for the installer to function properly.
Follow all the steps at the installer (just press Next etc.) and just press OK at the following dialog to ignore it (the URL provided is broken by now with Sun Microsystems having been acquired by Oracle and anyway Java 1.3 is quite old [there’s 1.6+ now]).
You should instead install the latest Java Virtual Machine (JVM) from http://www.java.com
After these steps, you have to fix E-Slate launcher to work with administrator rights if UAC (User Access Control) is enabled on Windows Vista or Windows 7 (which is the default setting), or if the current user doesn’t have administrator rights (you’ll be asked for the administrator password in that case when you do that setting change). So right click the E-Slate shortcut (“Αβάκιο” if you installed the Greek version) on the desktop and select “Properties”.
At the properties dialog displayed, click the “Compatibility” tab and then select “Run this program as an administrator” and press “OK”.
From then on you’ll be able to either click an E-Slate shortcut to launch the E-Slate Container (the editor) or double-click a .MWD file (an E-Slate Microworld storage file) to open that microworld in the E-Slate Container. Make sure you reply “Yes” to the
The 1st time you launch E-Slate you’ll see a dialog like the following popup, to select the Java Virtual Machine (JVM) to use with E-Slate (assuming you did install Java from http://www.java.com). Make sure you select the greatest version (there are some duplicate entries there due to recent changes on how JVMs register themselves with the system – those that have the same “in …” part at the end are equivalent).
The E-Slate launcher will remember this selection in the future, unless it can’t find the JVM you had selected anymore. To select another JVM make sure you hold down CTRL key when launching E-Slate (e.g. can press CTRL+OK at the security prompt Windows UAC shows when you run E-Slate with administrator privileges or hold CTRL when double-clicking a .MWD file or an E-Slate shortcut if you are running it using an administrator account and UAC is disabled).
The above instructions also apply to software based on the E-Slate environment, like the fine education software GAIA II I had built together with several educational and research partners from Greece. If you have any problems, just drop a question at the comments section. Will be glad to help…