Friday, October 02, 2009
When using binding and textboxes you can find a nasty surprise… binding entities get only updated when your textbox looses the focus… this can work for you in a lot of scenarios, but in some other not, e.g. What happens if you want to enable a Save button based on some fields being informed ?, or you want to implement an autosave functionality?… if you want to make it based on the standard behavior the user would have to tab over the textbox and things will get updated…
Is there any workaround available? Yes, on this thread (
http://forums.silverlight.net/forums/p/103695/294102.aspx#294102) found and interesting one (update the binding on the TextBoxchange event), but implementing this in every page could be a pain in the neck, so I have decided to create an extended version of TextBox control that includes this behavior by default:
public class TextBoxChangeExt : System.Windows.Controls.TextBox
{
public TextBoxChangeExt()
{
this.TextChanged += new TextChangedEventHandler(TextBoxChangeExt_TextChanged);
}
void TextBoxChangeExt_TextChanged(object sender, TextChangedEventArgs e)
{
// based on
// http://betaforums.silverlight.net/forums/t/103695.aspx
TextBox txCtl = (TextBox)sender;
if (txCtl != null)
{
var be = txCtl.GetBindingExpression(TextBox.TextProperty);
if (be != null)
{
be.UpdateSource();
}
}
}
}
Saturday, September 26, 2009
Sometimes you need to enable / disable all the controls that are inside a StackPanel or a Grid depending on some conditions… well it seems something pretty straight forward until you realize that these elements does not have available the IsEnabled property … ouch! What to do? Well… going control by control setting the IsEnabled property is a pain in the neck, encapsulating that elements in an user control can be an elegant solution but doesn’t fit to all scenarios (maybe is just a simple layout, or you don’t have the time to make all that refactoring). Is there any easier solution?...
Let’s check how it works with a simple sample: We have the following layout / XAML:
<StackPanel Orientation="Vertical" HorizontalAlignment="Center" Margin="5">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="20"/>
<RowDefinition Height="20"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="70"/>
<ColumnDefinition Width="90"/>
</Grid.ColumnDefinitions>
<TextBlock Text="Fullname" Grid.Row="0" Grid.Column="0" />
<TextBox Grid.Row="0" Grid.Column="1"/>
<TextBlock Text="Address" Grid.Row="1" Grid.Column="0"/>
<TextBox Grid.Row="1" Grid.Column="1"/>
</Grid>
<Button Content="Update" Margin="5" Width="100"/>
</StackPanel>
Let’s Wrap the stack panel with the content template and set it’s IsEnabled property to false, gotcha !! we get the desired behavior with just two lines of code :).
<ContentControl IsEnabled="false">
<StackPanel Orientation="Vertical" HorizontalAlignment="Center" Margin="5">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="20"/>
<RowDefinition Height="20"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="70"/>
<ColumnDefinition Width="90"/>
</Grid.ColumnDefinitions>
<TextBlock Text="Fullname" Grid.Row="0" Grid.Column="0" />
<TextBox Grid.Row="0" Grid.Column="1"/>
<TextBlock Text="Address" Grid.Row="1" Grid.Column="0"/>
<TextBox Grid.Row="1" Grid.Column="1"/>
</Grid>
<Button Content="Update" Margin="5" Width="100"/>
</StackPanel>
</ContentControl>
Monday, August 24, 2009
When you create a new Silverlight project, VStudio asks you to add web site project to host that Silverlight application, that’s great and a time saver: Visual Studio creates for you a new web project that contains the SL XAP application inside a folder called ClientBin, each time you make changes in your SL project and compile that XAP on the web project will get updated.

So far so good… but what happens when you want to add more Silverlight projects and linked them to the current existing web project? (e.g. you have several widget projects, or you want to chop the app in several XAP’s and use asynchronous XAP load), one option is to do that on the wizard dialog that appears when creating the XAP… but if you are in the case of integrating an existing project or you have just skipped that dialog…
If you google a bit you will find a bit confusing approach: a context menu option called “add linked server”, that doesn’t appear in your visual studio menu’s, What’s that? Well it’s an old CTP or Alpha approach to link a SL project to an existing web project, this options has been removed in the further releases of SL. What’s the right approach then? … Quite easy, just only need to know where’s now the option :):
- Just click to show the properties of your web project.
- Choose the Silverlight Applications Tab.
- And click on Add to add the new Silverlight project.

Tuesday, August 11, 2009
When we develop our silverlight widget the sample page that we use normally is a nice servier side control called asp:Silverlight, that’s quite nice if you use ASP .net 3.5 SP1, but… what if your widget has to run on ASP .net 2.0, or PHP, or a plain HTML page…
Well… the first thing to note down is just that asp:Silverlight control is a nice server side control that is translated into HTML, no more no less. You can as well define directly an OBJECT tag and instantiate the control.
What are the main differences between the asp:Silverlight approach and the OBJECT approach?
- The Object approach has a little bit more of work to declare it.
- Both asp:Silverlight and Object have an OnLoad event but the signature of the event have different parameters.
- The way in which we get the instance of the Silverlight control running is different
So let’s see how this work on a real sample: sometime ago I developed an open source widget called DB Schema Viewer (http://dbschemaviewer.codeplex.com/), I just need code to instantiate the control on a simple HTML page, and interact with the widget using javascript (e.g. loading a DB Schema or zooming), for this sample we are going to see how to expose the zoom operation to javascript (javascript calling a SL method), and how to instantiate the control in HTML, grab the instance running and call the scriptable SL method from js.
First part exposing SL zoom operation to js
Silverlight code.
We have to set our page class to scriptable:
[ScriptableType]
public partial class Page : UserControl, IViewMainPage
On the page load or app startup we have to register our plugin instance and give it a name
private void UserControl_Loaded(object sender, RoutedEventArgs e)
{
HtmlPage.RegisterScriptableObject("plDBSchemaViewer", this);
Then we have to define the zooming method as script table
[ScriptableMember]
public void ExecuteZoomOperation(bool zoomIn)
HTML / Javascript Code
We start from the sample HTML page that a Silverlight project create for us and add a new parameter (OnLoad):
<div id="silverlightControlHost">
<object id="plugin" data="data:application/x-silverlight-2," type="application/x-silverlight-2" width="100%" height="100%">
<param name="source" value="./DBSchemaViewer.FrontEnd.Main.xap"/>
<param name="onError" value="onSilverlightError" />
<param name="background" value="white" />
<param name="minRuntimeVersion" value="3.0.40624.0" />
<param name="autoUpgrade" value="true" />
<param name="onLoad" value="pluginLoaded" />
<a href="http://go.microsoft.com/fwlink/?LinkID=149156&v=3.0.40624.0" style="text-decoration:none">
<img src="http://go.microsoft.com/fwlink/?LinkId=108181" alt="Get Microsoft Silverlight" style="border-style:none"/>
</a>
</object><iframe id="_sl_historyFrame" style="visibility:hidden;height:0px;width:0px;border:0px"></iframe>
</div>
This event will be triggered once the Silverlight plugin has been instantiated and the widget is up and running.
In the pluginLoaded method we can get from the parameters an instance to the SL plugin (good to hold it in a member variable) and execute any initial interaction with the plugin:
// basic initialization plus instantiation
// this flag will indicate us whether the viewer is ready (downloaded and running)
var viewer_Loaded = false;
var slCtl = null;
// -- This method is called by the SL application to notify the HTML page that the application
// has been download and is ready to run and receive commands
/// Just when the SL ctl is loaded this event is fired
/// For the ASP .net version
///
// function pluginLoaded(sender) { // ASPX version
// slCtl = sender.get_element();
// alert(slCtl.Content.mySLapp.MyToUpper("Test String"));
// }
function pluginLoaded(sender, args) { // HTML version
viewer_Loaded = true;
slCtl = sender.getHost();
}
Now if we want to call from javascript the Silverlight zooming operation we only need to get from SlCtl the plugin instance the we have defined as scriptable in Sl, and call the zooming method:
function GetPluginInstance() {
var pluginInstance = null;
if (viewer_Loaded == true) {
pluginInstance = slCtl.Content.plDBSchemaViewer;
}
return pluginInstance
}
function ZoomIn() {
var pluginInstance = GetPluginInstance()
if (pluginInstance)
pluginInstance.ExecuteZoomOperation(true);
}
One last thing, you will need to include the autogenerated Silverlight.js file.
If you need more detailed info:
- Working sample: http://dbschemaviewer.codeplex.com
- Detailed info: http://msdn.microsoft.com/en-us/library/cc221414(VS.95).aspx
- More about javascript and SL interaction: http://www.mostlydevelopers.com/mostlydevelopers/blog/post/2008/08/11/Call-Javascript-Method-from-Silverlight-and-Vice-Versa.aspx
Now you are prepared to intregrate your SL widget on any web technology (at the end everything is ported to plain HTML and Javascript J).
Sunday, July 12, 2009
** Just a last minute update, from Arnoud:
And about... saving a canvas as PNG compressed:
Hope to get some extra time to create a new post with all this new updates :-).
**
One of the missing functionality of SL 2.0 was the ability to export a canvas content as an image and let the user save it to his local file storage. We had to use nasty tricks to implement that: rountrip to server side, draw again the content using GDI+ (server side) and dumping that in a bitmap…
In SL 3.0 they have incorporated:
- A class called WriteableBitmap, that allows us dumping a canvas content into an image.
- A Save File Dialog that allows us to save the given image in the client’s local file system.
But… it still has a limitation:
- SL 3.0 does not implement code to save as PNG or JPEG.
Is there any solution for this? Well the thing is you can implement in SL an algorithm to save in such formats, pain in the… J well we are lucky somebody has already done that for us.
I have packed in a library the excellent work from:
- Andy Beaulieu has implemented a class that get the writableBitmap content and encode in PNG format.
- Tom Giam, code to grab that stream and save it to local.
- Pete Brown: good concept explanation plus excellent support (interesting to read the comment section)
The library is called ImageExportLib, and how you can use it:
CanvasToPNG canvasToPNG = new CanvasToPNG();
// It will export to PNG the canvas content
// parameter canvas ID
canvasToPNG.ShowSaveDialog(canvasToExport);
About the sample: Draws some basic shapes and let’s you save it as PNG on your local file system:
You can download the whole solution:
Next Steps To do:
- Check for FJCore to allow JPEG export.
- Check why is limited to 2047x2047 width and height
- Define error handling (simple doesn’t capture exceptions).
- Properly comment the code, based on the excellent info published by Andy and Tom.
Saturday, July 11, 2009
When you add a WCF service referencein your Silverlight project you get for free a powerful autogenerated proxy: collections are ObservableCollection based, even the entities have already implemented the FireNotification on the set method ( it’s a good exercise to make a “go to definition” and explore the autogenerated classes code).
All this is great but… what happens if you need to extend an autogenerated entity? Let’s say I have the following scenario:
- A service provides me with a list of expected sales per quarter by product.
- I display this result on a grid.
- Now as a requirement I need to:
o Add an extra column to the grid showing the totals by product.
o The expected sales entries are editable so if a given user introduces a new figure the totals by product associated cell should be automatically recalculated.
o Add a validation to the expected sales fields, negatives values are not allowed.
To do this we could try to modify the autogenerated clasess but each time we need to refresh the service reference we would lose our changes, what can we do?
- Create your own set of entity classes on the client side: I don’t like that approach, I’m to lazy to write all that plumbing, maybe for some scenarios could be a good idea.
- Create a new class that inherits from the entity class: still some work to do, castings…
- Create a partial class that extends the current entity: that’s my favorite, I only need to add a partial class definition and add the new properties and validations that I need.
Let’s solve the challenges that we are facing using the third approach.
First, let’s say that our autogenerated entity class looks something like:
public partial class EntitySalesForecast : object, System.ComponentModel.INotifyPropertyChanged
{
private double SalesQ1Field;
[System.Runtime.Serialization.DataMemberAttribute()]
public double SalesQ1 {
get {
return this. SalesQ1Field;
}
set {
if ((this. SalesQ1Field.Equals(value) != true)) {
this. SalesQ1Field = value;
this.RaisePropertyChanged("SalesQ1");
}
}
}
// (…) Sample implemetation for Q2, Q3, Q4…
}
For the “Add extra totals columns” requirement let’s create the partial class (same name as the autogenerated proxy class) and add a new property that just performs the sum calculations (this will extend our proxy generated class without losing changes when refreshing the service reference):
public partial class EntitySalesForecast
{
public double TotalSales
{
get
{
return (SalesQ1 + SalesQ2 + SalesQ3 +` SalesQ4);
}
}
}
We have the totalSales available to be binded and displayed by the grid.
Now comes a trickier part, we need to refresh the totals field whenever a Quarter entry changes, we cannot directly add a call to RaisePropertyChanges on the Totals sales for each quarter property because each quarter property been already implemented by the proxy, what we are going to do is to keep on extending the newly created partial class, and create a new property per quarter (for instance if we have Quarter1 let’s call it Quarter1Bindable) that would wrap the Quarter1 property and implement the additional functionality that we need… then on the presentation layer we will replace the old QuarterX binding with the new QuarterXBindable.
public partial class EntitySalesForecast : object, System.ComponentModel.INotifyPropertyChanged
{
// (…) Add to the previouls created partial class a property per quarter
public double SalesQ1 _ToBind
{
get
{
return SalesQ1;
}
set
{
if ((SalesQ1.Equals(value) != true))
{
SalesQ1Field = value;
// DataGrid cell binding to this field will get a notification to refresh the value displayed
this.RaisePropertyChanged("SalesQ1_ToBind");
// Changes in a month entry... TotalSales calc field must be refreshed as well
this.RaisePropertyChanged("TotalSales");
}
}
}
// (…)
}
By doing this we kepp all the logic of the original property and expose a new property to the grid that will make the refresh totals calls whenever the user changes an entry.
Now that we have implemented this trick, implementing the “sales cannot be below zero” it’s easy just add the functionality to the QuarterXBindable:
public double SalesQ1 _ToBind
{
get
{
return SalesQ1;
}
set
{
if ((SalesQ1.Equals(value) != true))
{
// Validations, exception will be handled by SL Error handling Arch (validation)
if (value < 0)
{
throw new Exception("Sales amount cannot be naegative.");
}
SalesQ1Field = value;
// DataGrid cell binding to this field will get a notification to refresh the value displayed
this.RaisePropertyChanged("SalesQ1_ToBind");
// Changes in a month entry... TotalSales calc field must be refreshed as well
this.RaisePropertyChanged("TotalSales");
}
}
}
Again… keep the original quarter property as it is and extend functionality wrapping it up (of course in the grid you have to bind this new property).
Friday, July 10, 2009
SL Calling same domain WCF located service zero configuration
Nice stuff… in my app’s I use to have a config.xml file associated to my Silverlight application, it included an entry to specify the root url to access the WCF services that I need to call in my app.
It was a pain on the neck swapping between localhost and server mode (remember always to set the right config. Blah blah…), googling a bit I have found nice solution… why do you need to host that root url in a file? You already can grab it from the navigator that is browsing your page !!! this function works quite well:
public string GetServiceProxyURL()
{
string serviceBase = "";
if (Application.Current.Host.Source.Host.ToUpper() == "LOCALHOST")
{
serviceBase = string.Format("{0}://{1}:{2}",
Application.Current.Host.Source.Scheme,
Application.Current.Host.Source.Host,
Application.Current.Host.Source.Port);
}
else
{
// include the appname in the path
string url = System.Windows.Browser.HtmlPage.Document.DocumentUri.AbsolutePath;
string[] path = url.Split("/".ToCharArray());
string appname = path[1];
serviceBase = string.Format("{0}://{1}",
Application.Current.Host.Source.Scheme,
Application.Current.Host.Source.Host, appname);
}
//Here you can add any subfolder if needede
//serviceBase += "/WsCommunicator/";
return serviceBase;
}
One of the SL app’s that I have developed (http://www.dbschemaeditor.com) calls services that are located on the sames domain… so far so good.
Some weeks ago a user was complaining that in some occasions the application thrown a “Connection failed” message whenever he tried to login, … researching a bit on the issue I found that the user was typing in the url address dbschemaeditor.com (no WWW), what happens in this case? It’s treated as a cross domain call !! (request for clientaccesspolicy.xml and ding dong … error !).
Maybe to solve this you can configure something at IIS level (redirect or whatever), in my case I’m running the app in a shared hosting… I found a quick (and dirty?) solution… add a clientaccesspolicy file (site root) enabling explicitly access to the url dbschemaeditor.com (no WWW):
<?xml version="1.0" encoding="utf-8" ?>
<access-policy>
<cross-domain-access>
<policy>
<allow-from http-request-headers="SOAPAction" >
<!-- Just include the same domain without wwww-->
<domain uri="http://dbschemaeditor.com/"/>
</allow-from>
<grant-to>
<resource include-subpaths="true" path="/"/>
</grant-to>
</policy>
</cross-domain-access>
</access-policy>
That worked out for the SL service calls.
Monday, July 06, 2009
I got a MVP award from Microsoft on Silverlight. Altough I already manage a generic .net site + blog (tipsdotnet.com), I have decided to reopen my blog here at Geek with blogs just to talk about Silverlight.
Hope to start posting soon interesting stuff :-).