Silverlight 4 Twitter Client – Part 6
- by Max
In this post, we are going to look into implementing lists into our twitter application and also about enhancing the data grid to display the status messages in a pleasing way with the profile images. Twitter lists are really cool feature that they recently added, I love them and I’ve quite a few lists setup one for DOTNET gurus, SQL Server gurus and one for a few celebrities. You can follow them here. Now let us move onto our tutorial. 1) Lists can be subscribed to in two ways, one can be user’s own lists, which he has created and another one is the lists that the user is following. Like for example, I’ve created 3 lists myself and I am following 1 other lists created by another user. Both of them cannot be fetched in the same api call, its a two step process. 2) In the TwitterCredentialsSubmit method we’ve in Home.xaml.cs, let us do the first api call to get the lists that the user has created. For this the call has to be made to https://twitter.com/<TwitterUsername>/lists.xml. The API reference is available here. myService1.AllowReadStreamBuffering = true;
myService1.UseDefaultCredentials = false;
myService1.Credentials = new NetworkCredential(GlobalVariable.getUserName(), GlobalVariable.getPassword());
myService1.DownloadStringCompleted += new DownloadStringCompletedEventHandler(ListsRequestCompleted);
myService1.DownloadStringAsync(new Uri("https://twitter.com/" + GlobalVariable.getUserName() + "/lists.xml"));
3) Now let us look at implementing the event handler – ListRequestCompleted for this.
public void ListsRequestCompleted(object sender, System.Net.DownloadStringCompletedEventArgs e)
{
if (e.Error != null)
{
StatusMessage.Text = "This application must be installed first.";
parseXML("");
}
else
{
//MessageBox.Show(e.Result.ToString());
parseXMLLists(e.Result.ToString());
}
}
4) Now let us look at the parseXMLLists in detail
xdoc = XDocument.Parse(text);
var answer = (from status in xdoc.Descendants("list")
select status.Element("name").Value);
foreach (var p in answer)
{
Border bord = new Border();
bord.CornerRadius = new CornerRadius(10, 10, 10, 10);
Button b = new Button();
b.MinWidth = 70;
b.Background = new SolidColorBrush(Colors.Black);
b.Foreground = new SolidColorBrush(Colors.Black);
//b.Width = 70;
b.Height = 25;
b.Click += new RoutedEventHandler(b_Click);
b.Content = p.ToString();
bord.Child = b;
TwitterListStack.Children.Add(bord);
}
So here what am I doing, I am just dynamically creating a button for each of the lists and put them within a StackPanel and for each of these buttons, I am creating a event handler b_Click which will be fired on button click. We will look into this method in detail soon. For now let us get the buttons displayed.
5) Now the user might have some lists to which he has subscribed to. We need to create a button for these as well. In the end of TwitterCredentialsSubmit method, we need to make a call to http://api.twitter.com/1/<TwitterUsername>/lists/subscriptions.xml. Reference is available here. The code will look like this below.
myService2.AllowReadStreamBuffering = true;
myService2.UseDefaultCredentials = false;
myService2.Credentials = new NetworkCredential(GlobalVariable.getUserName(), GlobalVariable.getPassword());
myService2.DownloadStringCompleted += new DownloadStringCompletedEventHandler(ListsSubsRequestCompleted);
myService2.DownloadStringAsync(new Uri("http://api.twitter.com/1/" + GlobalVariable.getUserName() + "/lists/subscriptions.xml"));
6) In the event handler – ListsSubsRequestCompleted, we need to parse through the xml string and create a button for each of the lists subscribed, let us see how. I’ve taken only the “full_name”, you can choose what you want, refer the documentation here. Note the point that the full_name will have @<UserName>/<ListName> format – this will be useful very soon.
xdoc = XDocument.Parse(text);
var answer = (from status in xdoc.Descendants("list")
select status.Element("full_name").Value);
foreach (var p in answer)
{
Border bord = new Border();
bord.CornerRadius = new CornerRadius(10, 10, 10, 10);
Button b = new Button();
b.Background = new SolidColorBrush(Colors.Black);
b.Foreground = new SolidColorBrush(Colors.Black);
//b.Width = 70;
b.MinWidth = 70;
b.Height = 25;
b.Click += new RoutedEventHandler(b_Click);
b.Content = p.ToString();
bord.Child = b;
TwitterListStack.Children.Add(bord);
}
Please note, I am setting the button width to be auto based on the content and also giving it a midwidth value. I wanted to create a rounded corner buttons, but for some reason its not working.
Also add this StackPanel – TwitterListStack of the Home.xaml
<StackPanel HorizontalAlignment="Center" Orientation="Horizontal" Name="TwitterListStack"></StackPanel>
After doing this, you would get a series of buttons in the top of the home page.
7) Now the button click event handler – b_Click, in this method, once the button is clicked, I call another method with the content string of the button which is clicked as the parameter.
Button b = (Button)e.OriginalSource;
getListStatuses(b.Content.ToString());
8) Now the getListsStatuses method:
toggleProgressBar(true);
WebRequest.RegisterPrefix("http://", System.Net.Browser.WebRequestCreator.ClientHttp);
WebClient myService = new WebClient();
myService.AllowReadStreamBuffering = true;
myService.UseDefaultCredentials = false;
myService.DownloadStringCompleted += new DownloadStringCompletedEventHandler(TimelineRequestCompleted);
if (listName.IndexOf("@") > -1 && listName.IndexOf("/") > -1)
{
string[] arrays = null;
arrays = listName.Split('/');
arrays[0] = arrays[0].Replace("@", " ").Trim();
//MessageBox.Show(arrays[0]);
//MessageBox.Show(arrays[1]);
string url = "http://api.twitter.com/1/" + arrays[0] + "/lists/" + arrays[1] + "/statuses.xml";
//MessageBox.Show(url);
myService.DownloadStringAsync(new Uri(url));
}
else
myService.DownloadStringAsync(new Uri("http://api.twitter.com/1/" + GlobalVariable.getUserName() + "/lists/" + listName + "/statuses.xml"));
Please note that the url to look at will be different based on the list clicked – if its user created, the url format will be
http://api.twitter.com/1/<CurentUser>/lists/<ListName>/statuses.xml
But if it is some lists subscribed, it will be
http://api.twitter.com/1/<ListOwnerUserName>/lists/<ListName>/statuses.xml
The first one is pretty straight forward to implement, but if its a list subscribed, we need to split the listName string to get the list owner and list name and user them to form the string. So that is what I’ve done in this method, if the listName has got “@” and “/” I build the url differently.
9) Until now, we’ve been using only a few nodes of the status message xml string, now we will look to fetch a new field - “profile_image_url”. Images in datagrid – COOL. So for that, we need to modify our Status.cs file to include two more fields one string another BitmapImage with get and set.
public string profile_image_url { get; set; }
public BitmapImage profileImage { get; set; }
10) Now let us change the generic parseXML method which is used for binding to the datagrid.
public void parseXML(string text)
{
XDocument xdoc;
xdoc = XDocument.Parse(text);
statusList = new List<Status>();
statusList = (from status in xdoc.Descendants("status")
select new Status
{
ID = status.Element("id").Value,
Text = status.Element("text").Value,
Source = status.Element("source").Value,
UserID = status.Element("user").Element("id").Value,
UserName = status.Element("user").Element("screen_name").Value,
profile_image_url = status.Element("user").Element("profile_image_url").Value,
profileImage = new BitmapImage(new Uri(status.Element("user").Element("profile_image_url").Value))
}).ToList();
DataGridStatus.ItemsSource = statusList;
StatusMessage.Text = "Datagrid refreshed.";
toggleProgressBar(false);
}
We are here creating a new bitmap image from the image url and creating a new Status object for every status and binding them to the data grid. Refer to the Twitter API documentation here. You can choose any column you want.
11) Until now, we’ve been using the auto generate columns for the data grid, but if you want it to be really cool, you need to define the columns with templates, etc…
<data:DataGrid AutoGenerateColumns="False" Name="DataGridStatus" Height="Auto" MinWidth="400">
<data:DataGrid.Columns>
<data:DataGridTemplateColumn Width="50" Header="">
<data:DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Image Source="{Binding profileImage}" Width="50" Height="50" Margin="1"/>
</DataTemplate>
</data:DataGridTemplateColumn.CellTemplate>
</data:DataGridTemplateColumn>
<data:DataGridTextColumn Width="Auto" Header="User Name" Binding="{Binding UserName}" />
<data:DataGridTemplateColumn MinWidth="300" Width="Auto" Header="Status">
<data:DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock TextWrapping="Wrap" Text="{Binding Text}"/>
</DataTemplate>
</data:DataGridTemplateColumn.CellTemplate>
</data:DataGridTemplateColumn>
</data:DataGrid.Columns>
</data:DataGrid>
I’ve used only three columns – Profile image, Username, Status text. Now our Datagrid will look super cool like this.
Coincidentally, Tim Heuer is on the screenshot , who is a Silverlight Guru and works on SL team in Microsoft. His blog is really super.
Here is the zipped file for all the xaml, xaml.cs & class files pages.
Ok let us stop here for now, will look into implementing few more features in the next few posts and then I am going to look into developing a ASP.NET MVC 2 application.
Hope you all liked this post. If you have any queries / suggestions feel free to comment below or contact me.
Cheers!
Technorati Tags: Silverlight,LINQ,Twitter API,Twitter,Silverlight 4