So I was writing a blog on how to bind data to our Silverlight DataGrid from a WebService call and got bored. AgDataGrid.DataSource supports any collection that implements an IList or IEnumerable, and since all the collection objects that are generated by the Visual Studio's Web Service wizards already implement those, there was nothing really interesting to write about. I was searching for some cool WebService to use and remembered that there is this thing called Facebook (a tiny social networking website) and there are these friends there and how cool would it be if I can group them by say a City?
Prerequisites
Silverlight 2 Beta 2 Tools for Visual Studio 2008
AgDataGrid Beta 1
Facebook Developer Toolkit
Facebook Account
Join the Developer Express on Facebook :)
If you are just starting out with AgDataGrid and Silverlight development, or need a quick refresher, watch these quick-start videos:
The Setup
First things first. You will need a special Facebook Developer Application added to your profile. This is where you create applications to be run inside Facebook. Once you add that, click on Setup New Application and go through the setup process. You will need to set a couple of options:
a) Set the Callback URL to http://localhost:9999. This is the URL that the Facebook will redirect to.

b) Select "Use iframe"
c) Select "Yes" for Can your application be added on Facebook?
Once you finish the setup part there will be two unique keys assigned to your app. The Developer Key and the Secret Key. We will need those when making a Facebook API calls.
Next, create a new Silverlight application and choose an option to generate a Web Application project as well.
Creating a proxy Web Service
The anatomy of the Facebook platform and API is beyond the scope for this article. Instead, we will use a Facebook Developer Toolkit developed and maintained by the community. To be able to make a Facebook API call, a user must be logged in to his or her account. Once the user logs in and navigates to a specific app, Facebook will setup an iframe and pull the content from the Callback URL. It will also pass in an authToken parameter, that will be used to get the user id, session key etc... We will need those to make API calls. In your Web Project, add a reference to the Facebook.dll assembly (or a Facebook.csproj), the one that you downloaded from the toolkit.
A typical page load scenario would look something like this: Redirect to the facebook login screen if this is the first time. Once redirected back, request a facebook_session_key and a facebook_userid using the authToken that is passed in and store them inside the Session for later use.
protected void Page_Load(object sender, EventArgs e) {
string sessionKey = Session["facebook_session_key"] as string;
string userId = Session["facebook_userId"] as string;
string authToken = Request.QueryString["auth_token"];
if (!string.IsNullOrEmpty(sessionKey)) {
// already have a session
FacebookAPI.SessionKey = sessionKey;
FacebookAPI.UserId = userId;
} else if (!string.IsNullOrEmpty(authToken)) {
// on redirect
FacebookAPI.CreateSession(authToken);
Session["facebook_session_key"] = FacebookAPI.SessionKey;
Session["facebook_userId"] = FacebookAPI.UserId;
Session["facebook_session_expires"] = FacebookAPI.SessionExpires;
Session["facebook_api"] = FacebookAPI;
} else {
Response.Redirect(@"http://www.facebook.com/login.php?api_key=" +
FacebookAPI.ApplicationKey + @"&v=1.0");
}
}
Next, we will add a Silverlight-enabled WCF Service to the web project and create a method to return a user profile filled with friends.
public class UserInfo {
public string Name { get; set; }
public string Status { get; set; }
public string UserId { get; set; }
public byte [ ] Picture { get; set; }
// etc..
}
public class UserProfile : UserInfo {
public List<UserInfo> Friends { get; set; }
}
[OperationContract]
public UserProfile GetUserProfile(string userId) {
Facebook.API.FacebookAPI facebookAPI = HttpContext.Current.Session["facebook_api"]
as Facebook.API.FacebookAPI;
...
var list = facebookAPI.GetUserInfo(userId);
var profile = list[0];
var proxy_profile = new UserProfile(profile) {
Friends = new List<UserInfo>(),
Picture = ImageHelper.GetBytesFromWeb(profile.PictureBigUrl),
};
var friends = facebookAPI.GetFriends();
foreach (User friend in friends) {
proxy_profile.Friends.Add(new UserInfo(friend) {
Picture = ImageHelper.GetBytesFromWeb(friend.PictureSquareUrl),
});
}
return proxy_profile;
}
One last thing on the server side. Make sure to drop a Silverlight object on your Default.aspx page and link it to your .xap file. Also in your project Web options, set the port to 9999.
Client Side Binding
Add a reference to DevExpress.AgDataGrid.v8.2 in your Silverlight app, and drop the AgDataGrid on the page.
xmlns:DevExpress="clr-namespace:DevExpress.Windows.Controls;assembly=DevExpress.AgDataGrid.v8.2"
..
<DevExpress:AgDataGrid ShowGroupPanel="Visible" AllowEditing="False"
x:Name="FriendList"
PreviewVisibility="ForAllRows"
ColumnsAutoWidth="True">
<DevExpress:AgDataGrid.Columns>
<DevExpress:AgDataGridTextColumn FieldName="Name"/>
<DevExpress:AgDataGridTextColumn FieldName="City"/>
<DevExpress:AgDataGridTextColumn FieldName="State"/>
<DevExpress:AgDataGridTextColumn FieldName="Country"/>
<DevExpress:AgDataGridImageColumn FieldName="Picture"/>
</DevExpress:AgDataGrid.Columns>
</DevExpress:AgDataGrid>
Notice I use the AgDataGridImageColumn column type to bind to the Picture field. Internally, it will use an AgDataGridImageColumnContentConverter (an IValueConverter) to correctly convert our byte[].
AgDataGridImageColumnContentConverter comes in handy when binding to an Image control as well.
For example:
<UserControl.Resources>
<DevExpress:AgDataGridImageColumnContentConverter x:Key="BytesToImageConverter"/>
</UserControl.Resources>
<Image Source="{Binding Picture, Converter={StaticResource BytesToImageConverter}}"/ >
After adding a reference to the above service, we can bind the result to our UI.
uses FacebookServiceReference;
public Page() {
InitializeComponent();
ShowUserProfile(null);
}
void ShowUserProfile(string userId) {
FacebookServiceClient client = new FacebookServiceClient();
client.GetUserProfileCompleted += new EventHandler<GetUserProfileCompletedEventArgs>(FacebookServiceClient_GetUserProfileCompleted);
client.GetUserProfileAsync(userId);
}
void FacebookServiceClient_GetUserProfileCompleted(object sender, GetUserProfileCompletedEventArgs e) {
if (e.Error == null) {
this.FriendList.DataSource = e.Result.Friends;
}
}
Tweaking the UI - Changing how a data row looks
To change how a data row looks inside AgDataGrid we will reuse the row preview area to define a custom layout and hide the default data row.
Hide the default data row:
<DevExpress:AgDataGrid.DataRowTemplate>
<ControlTemplate TargetType="DevExpress:AgDataGridRow">
<Grid Name="RootElement">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<DevExpressInternal:AgLineStackPanel x:Name="CellsPresenterElement" Opacity="0" Height="1" />
<ContentControl x:Name="PreviewPresenterElement" Height="0"/>
</Grid>
</ControlTemplate>
</DevExpress:AgDataGrid.DataRowTemplate>
Define a custom row preview:
<DevExpress:AgDataGrid
ShowGroupPanel="Visible" AllowEditing="False"
x:Name="FriendList"
PreviewVisibility="ForAllRows"
ColumnsAutoWidth="True">
<DevExpress:AgDataGrid.PreviewTemplate>
<DataTemplate>
<Grid Margin="8">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="50"></ColumnDefinition>
<ColumnDefinition Width="*"></ColumnDefinition>
<ColumnDefinition Width="50"></ColumnDefinition>
</Grid.ColumnDefinitions>
<Image Width="50" Height="50" Stretch="Fill" Grid.Column="0"
Source="{Binding Picture, Converter={StaticResource BytesToImageConverter}}"
VerticalAlignment="Top"
HorizontalAlignment="Left">
</Image>
<StackPanel Orientation="Vertical" Grid.Column="1" HorizontalAlignment="Left" Margin="8,0,0,0">
<TextBlock Text="{Binding Name}" FontFamily="lucida grande,tahoma,verdana,arial,sans-serif"
FontSize="14" Foreground="#3B5998" Cursor="Hand"
FontWeight="Bold" Tag="{Binding UserId}"
MouseLeftButtonUp="UserBlock_MouseLeftButtonUp"></TextBlock>
<TextBlock Text="{Binding Status}" TextWrapping="Wrap"
Margin="2,0,0,0"
Width="300"
FontFamily="lucida grande,tahoma,verdana,arial,sans-serif"
FontSize="11"></TextBlock>
</StackPanel>
</Grid>
</DataTemplate>
</DevExpress:AgDataGrid.PreviewTemplate>
The end result:
Download Source Code
Previous: Silverlight DataGrid - Creating custom column types for the grid control
Next: Silverlight DataGrid - How to perform custom summary calculations within the grid control
Cheers
Azret