Skip to content

Commit 68590ad

Browse files
committed
Added 60th post.
1 parent f3c58de commit 68590ad

15 files changed

+552
-0
lines changed
8.24 KB
Loading

60-SLCollectionViewSource/README.md

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
# Silverlight's CollectionViewSource
2+
3+
4+
In my <a href="http://www.zagstudio.com/blog/387">last post</a>, I explained the reasoning behind adding CollectionViewSource to WPF. In this post I will talk about CollectionViewSource in Silverlight (introduced in Silverlight 3) and compare it with its WPF counterpart.
5+
6+
If you recall from my last post, CollectionViewSource was added to WPF mainly to permit view-related scenarios to be expressed in XAML (so they could be tooled), and also to aid in remembering the current item of previously displayed collections. The reasons for adding CollectionViewSource to Silverlight were very different. Before the introduction of CollectionViewSource, Silverlight collections were not automatically wrapped by views - in fact the concept of "views" didn't even exist in Silverlight 2. There was no way to sort or filter a collection (other than modifying the collection itself), or to build a master-detail scenario based on currency (although you could create a master-detail scenario based on selection). The introduction of CollectionViewSource enabled all of those scenarios in Silverlight, while improving compatibility with WPF.
7+
8+
Just like in WPF, a class is considered a "view" if it implements ICollectionView. All the views that we're used to interacting with derive from CollectionView, which in turn implements ICollectionView. Silverlight provides implementations only for EnumerableCollectionView and ListCollectionView, which means that it is able to generate views only for collections that implement IEnumerable or IList. These are by far the two most common scenarios. Unlike WPF, Silverlight's CollectionView, EnumerableCollectionView and ListCollectionView classes are all internal.
9+
10+
In addition to these, Silverlight also contains a PagedCollectionView class (this is unique to Silverlight). You can manually wrap your collection with this view to add filtering, sorting, grouping, currency tracking and paging to your collection. <a href="http://timheuer.com/blog/archive/2009/11/04/updated-silverlight-3-datagrid-grouping-data-pagedcollectionview.aspx">Tim Heuer</a> shows an example of its usage. Silverlight's CollectionViewSource, on the other hand, provides the ability to filter, sort, and track currency, but it does not offer the ability to group data.
11+
12+
Unlike WPF, Silverlight only wraps collections with a view when CollectionViewSource is used as an intermediary. If you simply bind an ItemsControl to a collection, no "default view" will be created internally for you. Also, since the ItemCollection class doesn't implement ICollectionView, it's not possible to sort or filter non-data bound items that have been added to an ItemsControl.
13+
14+
Just like in WPF, Silverlight's CollectionViewSource creates a view to wrap a collection when its Source property points to a new collection. In both platforms, it is not necessary to specify "Path=View" when binding to the CollectionViewSource - the binding does that automatically. Here's the syntax you use to bind to a CollectionViewSource:
15+
16+
<UserControl.Resources>
17+
<CollectionViewSource x:Key="cvs" />
18+
</UserControl.Resources>
19+
20+
<ListBox ItemsSource="{Binding Source={StaticResource cvs}}" />
21+
22+
Notice that I didn’t have to specify "{Binding Source={StaticResource cvs}, Path=View}" to bind to the view exposed by the CollectionViewSource. Both syntaxes are equivalent, but the second is unnecessary - the binding engine knows to drill into the View property when given a CollectionViewSource.
23+
24+
In WPF, when the CollectionViewSource points to several collections throughout its life, it creates a view for each of them and remembers those views. My last post explains how this feature enables a common scenario that would be a bit of work to implement without CollectionViewSource. I'm very glad to say that this feature has also been implemented in Silverlight, so the selection behavior of my previous post's WPF sample works the same way in Silverlight.
25+
26+
In fact, porting that sample to Silverlight was straightforward. The only difference was that in WPF, Bindings within the resources inherit the Window's DataContext. This enabled me to write the following code/XAML to bind the CollectionViewSource's Source to the Window's DataContext:
27+
28+
this.DataContext = new Mountains();
29+
30+
<CollectionViewSource Source="{Binding}" x:Key="cvs1"/>
31+
32+
This same feature is not present in Silverlight 3. Here's one equivalent way of implementing that behavior in Silverlight:
33+
34+
<local:Mountains x:Key="mountains" />
35+
<CollectionViewSource Source="{Binding Source={StaticResource mountains}}" x:Key="cvs1"/>
36+
Binary file not shown.
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
2+
Microsoft Visual Studio Solution File, Format Version 10.00
3+
# Visual Studio 2008
4+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SLCollectionViewSource", "SLCollectionViewSource\SLCollectionViewSource.csproj", "{73D84A6F-D1C9-4782-A895-BB5DFC604CC4}"
5+
EndProject
6+
Global
7+
GlobalSection(SolutionConfigurationPlatforms) = preSolution
8+
Debug|Any CPU = Debug|Any CPU
9+
Release|Any CPU = Release|Any CPU
10+
EndGlobalSection
11+
GlobalSection(ProjectConfigurationPlatforms) = postSolution
12+
{73D84A6F-D1C9-4782-A895-BB5DFC604CC4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
13+
{73D84A6F-D1C9-4782-A895-BB5DFC604CC4}.Debug|Any CPU.Build.0 = Debug|Any CPU
14+
{73D84A6F-D1C9-4782-A895-BB5DFC604CC4}.Release|Any CPU.ActiveCfg = Release|Any CPU
15+
{73D84A6F-D1C9-4782-A895-BB5DFC604CC4}.Release|Any CPU.Build.0 = Release|Any CPU
16+
EndGlobalSection
17+
GlobalSection(SolutionProperties) = preSolution
18+
HideSolutionNode = FALSE
19+
EndGlobalSection
20+
EndGlobal
Binary file not shown.
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
<Application xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
2+
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
3+
x:Class="SLCollectionViewSource.App"
4+
>
5+
<Application.Resources>
6+
7+
</Application.Resources>
8+
</Application>
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Net;
5+
using System.Windows;
6+
using System.Windows.Controls;
7+
using System.Windows.Documents;
8+
using System.Windows.Input;
9+
using System.Windows.Media;
10+
using System.Windows.Media.Animation;
11+
using System.Windows.Shapes;
12+
13+
namespace SLCollectionViewSource
14+
{
15+
public partial class App : Application
16+
{
17+
18+
public App()
19+
{
20+
this.Startup += this.Application_Startup;
21+
this.Exit += this.Application_Exit;
22+
this.UnhandledException += this.Application_UnhandledException;
23+
24+
InitializeComponent();
25+
}
26+
27+
private void Application_Startup(object sender, StartupEventArgs e)
28+
{
29+
this.RootVisual = new MainPage();
30+
}
31+
32+
private void Application_Exit(object sender, EventArgs e)
33+
{
34+
35+
}
36+
private void Application_UnhandledException(object sender, ApplicationUnhandledExceptionEventArgs e)
37+
{
38+
// If the app is running outside of the debugger then report the exception using
39+
// the browser's exception mechanism. On IE this will display it a yellow alert
40+
// icon in the status bar and Firefox will display a script error.
41+
if (!System.Diagnostics.Debugger.IsAttached)
42+
{
43+
44+
// NOTE: This will allow the application to continue running after an exception has been thrown
45+
// but not handled.
46+
// For production applications this error handling should be replaced with something that will
47+
// report the error to the website and stop the application.
48+
e.Handled = true;
49+
Deployment.Current.Dispatcher.BeginInvoke(delegate { ReportErrorToDOM(e); });
50+
}
51+
}
52+
private void ReportErrorToDOM(ApplicationUnhandledExceptionEventArgs e)
53+
{
54+
try
55+
{
56+
string errorMsg = e.ExceptionObject.Message + e.ExceptionObject.StackTrace;
57+
errorMsg = errorMsg.Replace('"', '\'').Replace("\r\n", @"\n");
58+
59+
System.Windows.Browser.HtmlPage.Window.Eval("throw new Error(\"Unhandled Error in Silverlight Application " + errorMsg + "\");");
60+
}
61+
catch (Exception)
62+
{
63+
}
64+
}
65+
}
66+
}
Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
using System;
2+
using System.Net;
3+
using System.Windows;
4+
using System.Windows.Controls;
5+
using System.Windows.Documents;
6+
using System.Windows.Ink;
7+
using System.Windows.Input;
8+
using System.Windows.Media;
9+
using System.Windows.Media.Animation;
10+
using System.Windows.Shapes;
11+
using System.Collections.ObjectModel;
12+
13+
namespace SLCollectionViewSource
14+
{
15+
public class Mountains : ObservableCollection<Mountain>
16+
{
17+
public Mountains()
18+
{
19+
// Whistler
20+
Mountain whistler = new Mountain("Whistler");
21+
Lift bigRedExpress = new Lift("Big Red Express");
22+
bigRedExpress.Runs.Add("Headwall");
23+
bigRedExpress.Runs.Add("Fisheye");
24+
bigRedExpress.Runs.Add("Jimmy's Joker");
25+
Lift garbanzoExpress = new Lift("Garbanzo Express");
26+
garbanzoExpress.Runs.Add("Raven");
27+
Lift orangeChair = new Lift("Orange chair");
28+
orangeChair.Runs.Add("Orange peel");
29+
orangeChair.Runs.Add("Banana peel");
30+
orangeChair.Runs.Add("Upper Dave Murray Downhill");
31+
whistler.Lifts.Add(bigRedExpress);
32+
whistler.Lifts.Add(garbanzoExpress);
33+
whistler.Lifts.Add(orangeChair);
34+
35+
// Stevens Pass
36+
Mountain stevensPass = new Mountain("Stevens Pass");
37+
Lift tyeMill = new Lift("Tye Mill");
38+
tyeMill.Runs.Add("Roller coaster");
39+
tyeMill.Runs.Add("Skid road");
40+
tyeMill.Runs.Add("Crest trail");
41+
Lift jupiterChair = new Lift("Jupiter chair");
42+
jupiterChair.Runs.Add("Corona bowl");
43+
jupiterChair.Runs.Add("Lower gemini");
44+
Lift southernCrossChair = new Lift("Southern cross chair");
45+
southernCrossChair.Runs.Add("Orion");
46+
southernCrossChair.Runs.Add("Aquarius face");
47+
stevensPass.Lifts.Add(tyeMill);
48+
stevensPass.Lifts.Add(jupiterChair);
49+
stevensPass.Lifts.Add(southernCrossChair);
50+
51+
// Crystal Mountain
52+
Mountain crystal = new Mountain("Crystal Mountain");
53+
Lift rainierExpress = new Lift("Rainier Express");
54+
rainierExpress.Runs.Add("Iceberg ridge");
55+
rainierExpress.Runs.Add("Pro course");
56+
rainierExpress.Runs.Add("Lucky shot");
57+
Lift greenValley = new Lift("Green Valley");
58+
greenValley.Runs.Add("Green back");
59+
greenValley.Runs.Add("Northway ridge");
60+
crystal.Lifts.Add(rainierExpress);
61+
crystal.Lifts.Add(greenValley);
62+
63+
this.Add(whistler);
64+
this.Add(stevensPass);
65+
this.Add(crystal);
66+
}
67+
}
68+
69+
public class Mountain
70+
{
71+
private ObservableCollection<Lift> lifts;
72+
73+
public ObservableCollection<Lift> Lifts
74+
{
75+
get { return lifts; }
76+
set { lifts = value; }
77+
}
78+
79+
private string name;
80+
81+
public string Name
82+
{
83+
get { return name; }
84+
set { name = value; }
85+
}
86+
87+
public Mountain(string name)
88+
{
89+
this.name = name;
90+
lifts = new ObservableCollection<Lift>();
91+
}
92+
}
93+
94+
public class Lift
95+
{
96+
private ObservableCollection<string> runs;
97+
98+
public ObservableCollection<string> Runs
99+
{
100+
get { return runs; }
101+
set { runs = value; }
102+
}
103+
104+
private string name;
105+
106+
public string Name
107+
{
108+
get { return name; }
109+
set { name = value; }
110+
}
111+
112+
public Lift(string name)
113+
{
114+
this.name = name;
115+
runs = new ObservableCollection<string>();
116+
}
117+
}
118+
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
<UserControl x:Class="SLCollectionViewSource.MainPage"
2+
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
3+
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
4+
xmlns:local="clr-namespace:SLCollectionViewSource"
5+
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
6+
mc:Ignorable="d" d:DesignWidth="640" d:DesignHeight="480">
7+
<UserControl.Resources>
8+
<Style TargetType="TextBlock" x:Key="TitleStyle">
9+
<Setter Property="FontWeight" Value="Bold" />
10+
<Setter Property="FontSize" Value="14" />
11+
<Setter Property="Margin" Value="0,0,0,5" />
12+
</Style>
13+
14+
<local:Mountains x:Key="mountains" />
15+
16+
<CollectionViewSource Source="{Binding Source={StaticResource mountains}}" x:Key="cvs1"/>
17+
<CollectionViewSource Source="{Binding Source={StaticResource cvs1}, Path=Lifts}" x:Key="cvs2"/>
18+
<CollectionViewSource Source="{Binding Source={StaticResource cvs2}, Path=Runs}" x:Key="cvs3"/>
19+
</UserControl.Resources>
20+
<Grid Margin="30" Width="500" Height="130">
21+
<Grid.ColumnDefinitions>
22+
<ColumnDefinition />
23+
<ColumnDefinition />
24+
<ColumnDefinition />
25+
</Grid.ColumnDefinitions>
26+
<Grid.RowDefinitions>
27+
<RowDefinition Height="Auto"/>
28+
<RowDefinition />
29+
</Grid.RowDefinitions>
30+
31+
<TextBlock Text="Mountains" Style="{StaticResource TitleStyle}"/>
32+
<TextBlock Text="Lifts" Style="{StaticResource TitleStyle}" Grid.Column="1"/>
33+
<TextBlock Text="Runs" Style="{StaticResource TitleStyle}" Grid.Column="2"/>
34+
35+
<ListBox ItemsSource="{Binding Source={StaticResource cvs1}}" DisplayMemberPath="Name" Grid.Row="1"/>
36+
<ListBox ItemsSource="{Binding Source={StaticResource cvs2}}" DisplayMemberPath="Name" Grid.Column="1" Grid.Row="1"/>
37+
<ListBox ItemsSource="{Binding Source={StaticResource cvs3}}" Grid.Column="2" Grid.Row="1"/>
38+
</Grid>
39+
</UserControl>
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Net;
5+
using System.Windows;
6+
using System.Windows.Controls;
7+
using System.Windows.Documents;
8+
using System.Windows.Input;
9+
using System.Windows.Media;
10+
using System.Windows.Media.Animation;
11+
using System.Windows.Shapes;
12+
13+
namespace SLCollectionViewSource
14+
{
15+
public partial class MainPage : UserControl
16+
{
17+
public MainPage()
18+
{
19+
this.InitializeComponent();
20+
}
21+
}
22+
}

0 commit comments

Comments
 (0)