Rants Tagged with “Silverlight 3”
1 2 > >> (Total Pages: 2/Total Results: 19)
I've recently been looking at the PagedCollectionView class. For those who are not familiar with this class, it allows you to automatically show sections of a collection in a paged way (especially when paired with the PagerControl). There is a good example on MSDN here:
http://msdn.microsoft.com/en-us/library/system.windows.data.pagedcollectionview(VS.95).aspx
The PagedCollectionView class supports the ICollectionView interface that supports a number of features that are really useful for dealing with collections. These include:
- Sorting
- Filtering
- Grouping
To show this, I decided to start with a simple DataGrid. I created the PagedCollectionView and wrapped my data collection in it like so (GameList is a simple collection of a Game class to show some data):
PagedCollectionView view =
new PagedCollectionView(new GameList());
Then when data binding, I simple do the binding directly to the view instead of the collection:
theGrid.ItemsSource = view;
Using the PagedCollectionView as a wrapper for your collection provides the functionality of sorting, grouping and filtering to your collection. Let's start with sorting.
The ICollectionView interface has a SortDescriptions collection that is used to set the sort for the view. For example, to sort our collection by ReleaseDate then Name, we can add two SortDescription objects to the collection:
view.SortDescriptions.Clear();
view.SortDescriptions.Add(
new SortDescription("ReleaseDate",
ListSortDirection.Descending));
view.SortDescriptions.Add(
new SortDescription("ProductName",
ListSortDirection.Ascending));
In our DataGrid, both columns will mark themselves with the sort markers to indicate the search. You can reverse this if you need to detect the changes in sorting that happens when the user clicks on the columns to sort. You can do this by registering for the CollectionChanged event and looking at the SortDescription collection:
view.CollectionChanged += view_CollectionChanged;
...
void view_CollectionChanged(object sender,
NotifyCollectionChangedEventArgs e)
{
foreach (var s in view.SortDescriptions)
{
MessageBox.Show(
string.Concat("Sorting is: ",
s.PropertyName,
" - ",
s.Direction));
}
}
The ICollectionView also exposes the ability to group items in the DataGrid. To add grouping, you simply need to add new GroupDescription objects to the view's GroupDescription list. The only type of GroupDescription that is currently implemented is the PropertyGroupDescription which allows you to group by a property. For example, to group by the ReleaseDate I added a new PropertyGroupDescription like so:
view.GroupDescriptions.Add(
new PropertyGroupDescription("ReleaseDate"));
In the DataGrid, these groups look like so:

The last feature I love about the ICollectionView interface is the ability to do arbitrary filtering. The interface has a Filter property that takes a Predicate<object> object. This way we can use a simple lambda to perform filtering. For example, to only show the games that have Microsoft in the name, I just create a simple lambda like so:
view.Filter = g =>
((Game)g).Publisher.Contains("Microsoft");
Cool, huh?

I was trading tweets today with @pauliom about whether RIA Services would solve some Auth problems he was having out of the browser. While RIA does do some interesting things with roles/users, I mentioned that typical Forms Auth out of the box should just work.
To that end I have created a simple example of how to protected WCF Services with Forms Auth (works with ADO.NET Data Services as well BTW). Because I wanted to support it out of the browser as well, I used the new Forms Auth service. To do so, just add a new .svc file to your project and put this in the body:
<%@ ServiceHost Language="C#"
Service="System.Web.ApplicationServices.AuthenticationService" %>
The web.config also needs to know about the service. So first, add a web extensions like so:
<system.web.extensions>
<scripting>
<webServices>
<authenticationService enabled="true"/>
</webServices>
</scripting>
</system.web.extensions>
Finally, the WCF configuration bits:
<system.serviceModel>
<behaviors>
<serviceBehaviors>
<behavior
name="OutOfBrowserFormsAuth.Web.AuthServiceBehavior">
<serviceMetadata httpGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="false" />
</behavior>
</serviceBehaviors>
</behaviors>
<services>
<service
behaviorConfiguration="OutOfBrowserFormsAuth.Web.AuthServiceBehavior"
name="System.Web.ApplicationServices.AuthenticationService">
<endpoint address=""
binding="basicHttpBinding"
contract="System.Web.ApplicationServices.AuthenticationService" />
<endpoint address="mex"
binding="mexHttpBinding"
contract="IMetadataExchange" />
</service>
</services>
</system.serviceModel>
Once you have all those pieces you can create the proxy like any other service (with "Add Service Reference..."). Then you can login from the Silverlight application:
var authSvc = new AuthenticationServiceClient();
authSvc.LoginCompleted += (s, a) =>
{
if (a.Error != null)
{
result.Items.Add(string.Concat("Error logging in: ", a.Error.Message));
}
else
{
result.Items.Add(string.Concat("Login: ", a.Result));
}
};
authSvc.LoginAsync("swildermuth", "P@ssw0rd", null, false);
So securing the web service becomes pretty simple. I created a new "Silverlight WCF Service" inside a secure folder:

I created the web service and then the Service Reference before enabling security so adding a Service Reference would work. This is the one pain point in that you must disable the folder security to add the reference then re-enable it afterwards:
<?xml version="1.0"?>
<configuration>
<system.web>
<authorization>
<deny users="?"/>
<allow users="*"/>
</authorization>
</system.web>
</configuration>
Now the web service will only work once the login happens. You can test this in or out of the browser and with both stacks with the source code. Here's a quick screenshot showing it working after login and not working after logout:

You can download the source code here:
http://wildermuth.com/downloads/ProtectedWCF.zip
UPDATE: Here are some links to the actual docs for this:
Configuring the Authentication Service:

In my earlier blog entry, I mentioned the Blend SDK that is shipping with Blend 3 RC. For those of you who won't have access to Blend, the SDK is supposed to be available to you. But not quite yet.
I've been told that the SDK will be available as a separate download once Blend 3 ships (no date yet). That means if you want it you need to install the Blend 3 RC that shipped with Silverlight 3. You can find it on the Silverlight Getting Started Page:
http://silverlight.net/GetStarted/

While Scott Guthrie (et al.) are going to give you the big list of what is new in Silverlight 3, let me point you at a number of hidden gems in the Silverlight 3 release (some of which are new to the release, and some of which have been there since the beta):
Where is the Silverlight ASP.NET Control?
The Silverlight 3 SDK/Tools no longer ships with the ASP.NET controls (Silverlight and Media). There are a number of reasons for this (and I still think that Silverlight.js + Object tag is the way to go), but if you need it, you can find it on Microsoft's Code Site:
http://code.msdn.microsoft.com/aspnetprojects
Improved Text Rendering
A common complaint with Silverlight has been its poor font rendering, especially at small size. In the release, you can see this new rendering using ClearType.
Blend SDK?
Blend now offers an SDK that is installed with Blend 3 (or can be downloaded separately). The primary reason for this is that some of the features are really Blend features (like Behaviors). So using the Blend SDK will allow you to build your own Behaviors without the need to have Blend installed.
Cached Assemblies
In the beta you could tell your Silverlight 3 project that you wanted to cache the Microsoft assemblies which created links to Microsoft.com for downloading the SDK assemblies (the ones not in the plugin). While this was great, in the release these assemblies are now stored on your own servers. This will cause a bit less caching but it opens up the opportunity to cache your own assemblies as well. I'll have a demo of this in an upcoming blog post.
Client HTTP Stack
In the release, they've supplied a new Client HTTP stack. Normally all network calls go through the browser's HTTP stack. But this stack has some limitations like the inability to do PUT and DELETE verbs and limited access to headers. The new client-based stack allows you to opt-in to using the new stack. Unless you need specific functionality in the client stack, its recommended you continue to use the browser stack as that way you will still use the cookies/session id from the browser session (so that things like forms auth "just work"). I'll expand on this topic in a blog post soon too.
Lots more!
There are more interesting nuggets to find in the bits, but hopefully this has given you a taste...

I had a chance to sit down (metaphorically) with Dmitry Lyalin and Peter Laudati and talk about Silvelright, MVVM and Prism 2.0. Let me know if you agree, disagree or think that I am a little obsessed with IoC containers.
The Connected Show is a podcast on New Microsoft Technology for the developer community, produced independently by Dmitry Lyalin and Peter Laudati.

It is great to see that Tim Heuer and I are thinking about the same thing. In his recent article on DataForm helpers, he explained how to use the Validation Attributes to hint the DataForm with additional information. These attributes are used extensively in the validation model in RIA Services but I'll be covering that later.
Instead of that, I've been digging into how you can customize the look the DataForm while still getting the benefit of paging functionality. As Tim mentioned you can specify the rows of the DataForm in a similar way to who the DataGrid works;
<df:DataForm x:Name="theForm"
AutoGenerateFields="False">
<df:DataForm.Fields>
<df:DataFormTextField Binding="{Binding Name}"
FieldLabelPosition="Top" />
</df:DataForm.Fields>
</df:DataForm>
While specifying the fields to use is interesting, I wanted more control. That's where DataForm templates come in. In the DataForm control, there are three templates:
- DisplayTemplate: The DataTemplate for showing the 'read-only' view of a bound item.
- EditTemplate: The DataTemplate for editing an item.
- InsertTemplate: The DataTemplate for a new item.
Specifying these Templates is as simple as using a DataTemplate like so:
<df:DataForm x:Name="theForm"
AutoGenerateFields="True">
<df:DataForm.DisplayTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding Name}" />
<TextBlock Text="{Binding Title}" />
</StackPanel>
</DataTemplate>
</df:DataForm.DisplayTemplate>
<df:DataForm.InsertTemplate>
<DataTemplate>
<StackPanel>
<TextBox Text="{Binding Name, Mode=TwoWay}" />
<TextBox Text="{Binding Title, Mode=TwoWay}" />
</StackPanel>
</DataTemplate>
</df:DataForm.InsertTemplate>
<df:DataForm.EditTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding Name, Mode=TwoWay}" />
<TextBox Text="{Binding Title, Mode=TwoWay}" />
</StackPanel>
</DataTemplate>
</df:DataForm.EditTemplate>
</df:DataForm>
This gives you the full power of controlling the look of the individual items in the DataForm. Of course, to get the full power, you will often want to use the other new controls as well (like the FieldLabel):
<df:DataForm.EditTemplate>
<DataTemplate>
<StackPanel>
<df:FieldLabel PropertyPath="Name" />
<TextBox Text="{Binding Name, Mode=TwoWay,
NotifyOnValidationError=true,
ValidatesOnExceptions=true}"
HorizontalContentAlignment="Stretch" />
<df:FieldLabel PropertyPath="Title" />
<TextBox Text="{Binding Title, Mode=TwoWay,
NotifyOnValidationError=true,
ValidatesOnExceptions=true}"
HorizontalContentAlignment="Stretch" />
<df:ErrorSummary />
</StackPanel>
</DataTemplate>
</df:DataForm.EditTemplate>
In this way the DataForm gives you full control over how the individual items are displayed. This allows you to use all the services of the DataForm without having to deal with the look of the DataForm.
What do you think?

In the first part of this article, I showed you how to build a very simple behavior using the Behavior<T> class. That class is useful for creating simple behaviors that just require that the behavior know about attaching and detaching of the item.
In this 2nd part of the series, I'll show off the TriggerAction<T> class which expands on this by supporting an Invoke method which is called once an event trigger happens. This is a much more common case than the simple behavior.
For example, let's create a behavior that shivers an object when an event is fired. I start out with a behavior that derives from TriggerAction<T> like so:
public class ShiverBehavior : TriggerAction<UIElement>
{
}
Note that I use UIElement again as the generic parameter. This is because I should be able to make any UIElement shiver with my behavior. This parameter determines what types of objects I can attach the behavior to.
I decided to implement the behavior by using a DoubleAnimation with a ElasticEase (easing function). To make the shiver happen, I am going to animate a TranslateTransform so I can move it around vertically or horizontally. So my behavior is going to need to reference and create the storyboard (and related objects) to do the actual shivering:
TranslateTransform shiverTransform = new TranslateTransform();
Storyboard shiverStory = new Storyboard();
ElasticEase ease = new ElasticEase();
DoubleAnimation shiverAnimation = new DoubleAnimation();
public ShiverBehavior()
: base()
{
// Set up ease
ease.Springiness = 2;
// Create the Animation
shiverAnimation.EasingFunction = ease;
Storyboard.SetTarget(shiverAnimation, shiverTransform);
// Setup Variable Properties
SetupProperties();
// Attach it to the story
shiverStory.Children.Add(shiverAnimation);
}
The trick to making this work is to use the OnAttached to insert our TranslateTransform into the object itself. (We won't work on objects that already have a TranslateTransform but that's a small price for getting the behavior):
protected override void OnAttached()
{
base.OnAttached();
// Attach the transform
if (this.AssociatedObject.RenderTransform == null)
{
this.AssociatedObject.RenderTransform = shiverTransform;
}
else if (this.AssociatedObject.RenderTransform is TransformGroup)
{
TransformGroup group = (TransformGroup)AssociatedObject.RenderTransform);
group.Children.Add(shiverTransform);
}
else
{
var tx = AssociatedObject.RenderTransform;
var group = new TransformGroup();
group.Children.Add(tx);
group.Children.Add(shiverTransform);
AssociatedObject.RenderTransform = group;
}
}
The OnAttached adds the transformation to the object (or inside a TransformGroup if it exists). In the OnDetaching we need to reverse this as well (you can see that code by downloading the sample since its not especially relevant to our explanation).
At this point we have a Storyboard that contains our animation of the TranslateTransform. The TranslateTransform is now added to the object. We're all ready for the last part of the code. When the Invoke method is called, we simply need to start the animation:
protected override void Invoke(object parameter)
{
shiverStory.Begin();
}
This Invoke method is the only required override that you need in your behavior, but in practice (as you've seen here), you will override several other parts of the class.
In order to make our shivering configurable, we can just add dependency properties and they show up in Blend automatically. For example, the Oscillations property was created like so:
public int Oscillations
{
get { return (int)GetValue(OscillationsProperty); }
set { SetValue(OscillationsProperty, value); }
}
public static readonly DependencyProperty OscillationsProperty =
DependencyProperty.Register("Oscillations",
typeof(int),
typeof(ShiverBehavior),
new PropertyMetadata(25,
new PropertyChangedCallback(OnOscillationsChanged)));
private static void OnOscillationsChanged(DependencyObject d,
DependencyPropertyChangedEventArgs e)
{
((ShiverBehavior)d).OnOscillationsChanged(e);
}
protected virtual void OnOscillationsChanged(DependencyPropertyChangedEventArgs e)
{
SetupProperties();
}
Creating the dependency properties is fairly simple (especially since I am using the dependency property snippets from the Silverlight Contrib project).
Using a TriggerAction based behavior is similar to using a simple Behavior. You can see the behaviors in the Asset Library as shown below:

Once added, the behavior becomes nested inside the object in the "Objects and Timeline":

In the Property pane, you can see the event that will trigger this behavior and change it (as well as behavior specific properties in the Miscellaneous section):

If you are using behaviors for your own projects, you just need to reference the assembly in your Silverlight project and they'll show up in Blend 3's Asset Library. But if you want to build behaviors that are listed in all Blend 3's projects (and make it add the assembly automatically), you will need to register your assembly in the registry:
HKEY_CURRENT_USER\Software\Microsoft\Expression\Blend\v3.0\Toolbox\Silverlight\v3.0\{Friendly Name
With the default key pointing to the directory with behaviors.
Grab the source code and take a look at the behaviors:
http://wildermuth.com/downloads/BehaviorDemo.zip

One of my favorite features of Silverlight 3 (and by extension Blend 3) is the new support for Behaviors. Behaviors are a way to allow designers to add functionality to XAML elements without code. In Blend 3, a couple of built-in behaviors are included (ShowMessageBox and ExecuteMethod). But of course I wanted to write my own behavior.
To start out you need to have a reference to a Blend assembly (for some reason its not part of the Silverlight SDK, at least at this point). The assembly is called Microsoft.Expression.Interactivity.dll and its located in
{ProgramFiles}\Microsoft Expression\Blend 3 Preview\Libraries\Silverlight\
Once you have a reference to the Interactivity assembly, you can use one of the base classes to create a behavior. There are several base classes of interest, but I will focus on three that are the most interesting:
- Behavior<T>: Simple behaviors
- TriggerAction<T>: Behavior tied to an event
- TargettedTriggerAction<T>: TriggerAction that affects a separate object (e.g. the Target)
The class diagram looks like this:

For simple Behaviors, the Behavior<T> class is perfect. This class simply has overridable methods for notifying the Behavior when an object is attached or detached from the behavior. For my first behavior, I decided to create a behavior that blurs an element when the mouse hovers. To do this I started by creating a simply deriving from the Behavior<T> class:
public class BlurHoverBehavior : Behavior<UIElement>
{
}
I choose UIElement as the template parameter since Bluring had to be applied to elements that were part of the UI.
The Behavior class supplies an OnAttached and OnDetaching virtual methods that you can use to wire up your behavior. In my case, I wanted to handle the MouseEnter and MouseLeave event to enable and disable the blurring. First I needed the behavior to have a BlurEffect then on the OnAttached, I registered for the two events then OnDetaching I removed the events (so I wouldn't leak the handlers):
BlurEffect effect = null;
public BlurHoverBehavior()
: base()
{
effect = new BlurEffect() { Radius = 20 };
}
protected override void OnAttached()
{
base.OnAttached();
this.AssociatedObject.MouseEnter +=
new MouseEventHandler(AssociatedObject_MouseEnter);
this.AssociatedObject.MouseLeave +=
new MouseEventHandler(AssociatedObject_MouseLeave);
}
protected override void OnDetaching()
{
base.OnDetaching();
this.AssociatedObject.MouseEnter -=
new MouseEventHandler(AssociatedObject_MouseEnter);
this.AssociatedObject.MouseLeave -=
new MouseEventHandler(AssociatedObject_MouseLeave);
}
In the mouse event handlers, I simply added and removed the blur event as necessary:
void AssociatedObject_MouseLeave(object sender, MouseEventArgs e)
{
this.AssociatedObject.Effect = null;
}
void AssociatedObject_MouseEnter(object sender, MouseEventArgs e)
{
this.AssociatedObject.Effect = effect;
}
Once this class is written, we can add it via Blend 3 or directly in XAML:
<Ellipse Fill="Green"
Width="200"
Height="200"
VerticalAlignment="Top">
<i:Interaction.Behaviors>
<beh:BlurHoverBehavior BlurRadius="25" />
</i:Interaction.Behaviors>
</Ellipse>
The Interaction.Behaviors is an attached behavior that is part of the Interaction assembly that you added from the Blend assemblies. The beh namespace is from your new class (whatever namespace it belongs in).
You can play with this behavior by downloading the source code here:
http://wildermuth.com/downloads/BehaviorDemo.zip
In the next part of this article, I will create a Trigger-based behavior...look for it this weekend!

Last night I attended and spoke to the GGMUG (Gwinett Georgia Microsoft User Group) meeting to talk about everything new Silverlight 3. It was a good event and the people were great. There were good questions and the facility is great. If you attended the event, thanks for coming! You had some great questions.
You can get the slides and sample code on my site here:
http://wildermuth.com/talks#Speaking_at_Gwinnett,_Georgia_Microsoft_User_Group_

I was reading Jesse Liberty's Validation post today and it reminded me of a discussion that I had with my students last week about doing validation. While performing the validation is interesting, for them they needed to worry about localization. In the face of this I suggested the new validation attributes. These attributes live in the System.ComponentModel.DataAnnotations namespace. These attributes include:
- Required
- Range
- RegularExpression
- StringLength
- CustomValidation
Each of these can be applied to properties like so:
public class Game
{
[Required]
[StringLength(255)]
public string ProductName { get; set; }
[Required]
[Range(0m,250m)]
public decimal ListPrice { get; set; }
[CustomValidation(typeof(MyValidator), "CheckReleaseDate")]
public DateTime ReleaseDate { get; set; }
}
In Jesse's example, instead of using attributes he used exceptions to notify the validation UI in Silverlight 3. This works fine but my students were concerned about localizing the string in the exception. While they could have done it with a string table, I suggested that what they might want instead were the validation attributes. In each attribute you can specify the error message like so:
[Required(ErrorMessage="ProductName is required.")]
public string ProductName { get; set; }
But to localize it the trick is to actually specify the name message in the attribute like so:
[Required(ErrorMessageResourceName="PRODUCTNAMEREQUIRED")]
public string ProductName { get; set; }
But after asserting that this was a good solution, I didn't have a good example of how to force validation with these attributes. Of course, in Silverlight 3 you could use the DataForm to do this but I wanted to be able to point my students at a way of using it anywhere. (The DataForm is great, I just like to have options.)
After a couple of hours with Reflector, I had a workable solution:
public class Game
{
string _name;
decimal _listPrice;
DateTime _releaseDate;
[Required(ErrorMessageResourceName="PRODUCTNAMEREQUIRED")]
public string ProductName
{
get { return _name; }
set
{
if (value != _name)
{
_name = value;
Validate();
}
}
}
[Required]
[Range(0m,250m)]
public decimal ListPrice
{
get { return _listPrice; }
set
{
if (value != _listPrice)
{
_listPrice = value;
Validate();
}
}
}
[CustomValidation(typeof(MyValidator), "CheckReleaseDate")]
public DateTime ReleaseDate
{
get { return _releaseDate; }
set
{
if (value != _releaseDate)
{
_releaseDate = value;
Validate();
}
}
}
void Validate()
{
var ctx = new ValidationContext(this, null, null);
Validator.ValidateObject(this, ctx);
}
}
The trick was to create a validation routine directly in the data class that I was binding to. The Validate method creates a ValidationContext object (which contains the metadata about the attributes) and use it to call ValidateObject on the Validator class. This reads the attributes and builds the data (as an exception) that the validation UI requires. The trick is to them call the Validate method in the setters of the object.
The problem I have is that this doesn't quite play well with Web Service or Data Service data (it would require that we modify the generated code). I am still looking for a better solution (like the MetaDataType solution that Dynamic Data uses). When I find the right person at MS who can clue me in, i'll blog about it here.