Archive
Nice DeepZoom image samples via ClipFlair Studio’s Image component
Here are some DeepZoom image samples to enjoy via ClipFlair‘s Image component, in our ClipFlair Studio app (Silverlight-based):
-
http://studio.clipflair.net/?image=http://static.seadragon.com/content/misc/milwaukee.dzi (hand-drawn pano, 1898)
-
http://studio.clipflair.net/?image=http://static.seadragon.com/content/misc/yosemite-panorama.dzi (try zooming onto the waterfall)
- http://studio.clipflair.net/?image=http://static.seadragon.com/content/misc/color-flower.dzi
Another impressive one is this Mandelbrot fractal, a dynamically generated DeepZoom image from Google AppEngine SDK demos:
http://studio.clipflair.net/?image=http://mattjmandelbrot.appspot.com/mandelbrot256.dzi
You can point the mouse and scroll the mouse wheel or double click to zoom in where you want.
One can make DeepZoom tiled images and DeepZoom image collections (can read more at http://msdn.microsoft.com/en-us/library/cc645050%28VS.95%29.aspx) with various authoring tools, including DeepZoom Composer (can read more about that tool at http://msdn.microsoft.com/en-us/library/dd409068%28loband%29.aspx)
However, the easiest way is to use ZoomIt service (http://zoom.it/pages/create/) to make a DeepZoom image wrapper (hosted at that site) for any big image on the web (giving it the original image URL). e.g. http://zoom.it/wR4i is a ClipFlair Poster (the respective DZI image is at http://cache.zoom.it/content/wR4i.dzi – internally ClipFlair’s image component converts zoom.it URLs to get the real .DZI image and show it)
Gotcha: Image component not loading remote URLs during debugging
At ClipFlair’s Image component I use the following XAML to make it show an image from a URL that its ViewModel holds at a property named “Source”, of type Uri (URI = Uniform or Universal Resource Identifier in W3C parlance, something like a superset of the old classic URLs).
<Image Name="imgContent"
HorizontalAlignment="Stretch" VerticalAlignment="Stretch"
Source="{Binding Source, Mode=OneWay}"
Stretch="{Binding Stretch, Mode=OneWay}"
>
I’ve had issues in the past with that component not loading an image, a tricky issue was when I had used Mode=TwoWay when data-binding to Source property – that was disastrous, since the Source property expects an ImageSource and just “plays it clever” internally, also accepting a conversion from a Uri. So when doing reverse binding too, you’d end up getting a null value at respective the ViewModel property.
So when it recently started not showing the test image (from a remote URL) that I had been using, I started wondering if it was some regression of that older bug, but couldn’t find some change in the respective code, plus in the Visual Studio XAML designer the component would load and display the remote image fine.
It turned out to be an issue with Silverlight’s security policy regarding cross-site access. The Image control is supposed to be able to load images from any remote URL (without the remote web server needing to have a ClientAccessPolicy.xml file for example to allow it, as is the case with the WebClient class), however I had recently found out that if at your Silverlight project you have selected at the “Debug” tab the “Dynamically generate a test page” option, the Image control wouldn’t load remote images.
What I didn’t know was that even the “Out-of-browser application option there won’t let the Image control load remote images if you don’t select the web project that goes with your Silverlight project (supposing you have them in the same Visual Studio solution), but you happen to select your Silverlight project instead from the dropdown list.
I had changed that option without thinking it might cause an issue while doing other changes in the project. That’s why one should try to do a minimal set of related changes only and test again thoroughly each time (if only they had the time available to do it), so that they can spot such issues early and be able to relate newly introduced bugs to the recent small set of changes, helping to track down the exact change that caused the unwanted behaviour.
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; //... } }
In an even newer iteration of that code, I removed the if/else and added the null check in the switch statement:
protected void View_PropertyChanged(object sender, PropertyChangedEventArgs e) { switch (e.PropertyName) { case null: Source = View.Source; //... break; 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).