Sunday, June 05, 2011

NGTweet Part 4 : Control Templates for better UI

 

This is the 4th part of the NGTweet series. In the previous post I demonstrated how to use Themes to enhance the user experience by building consistent UI. In this post I’ll take another step towards making the UI better from what we have currently for the NGTweet application. So far we have been displaying the tweets in a list box with just text. In most of the social media applications we would get to see the profile image of the person who is associated with a Tweet, a scrap, a poke or whatever jazzy name is given for a user action.

So in this post I’ll make two enhancements to the UI. I’ll add the profile picture of the person who originally twitted and also the date on which it was twitted. In future post I want to highlight the feature called ValueConvertor in Silverlight to add some context to the date to make it more relevant for the user. Also I’ll demonstrate the ability to display additional options with the selected tweet. Another features I have in my to do list are the ability to Tweet, ReTweet, send direct messages(replies), mark tweets as favourites, filter tweets and so on. Lets get started with the profile image and the created date features for this post.

Add ProfileImage and CreatedDate to Service layer

If we go back to the first post of this series, we created a very elementary domain model in our service layer. This had the screen name and the Tweet properties associated with it. It was sufficient for the first getting started example. But with changing requirements we need some changes to the domain model. I have based my model on the similar data structures available in TweetSharp library. So I refactored the NGTweeterStatus class and added NGTweeterUser class. Because of the problem with serialization which I mentioned earlier I had to take these steps. I added the required adapter  for TweeterUser. After these minor changes, the NGTweeterStatus class look like

    [DataContract]

    public class NGTweeterStatus

    {

        [DataMember]

        public string Tweet { get; set; }

 

        [DataMember]

        public NGTweeterUser User { get; set; }

 

        [DataMember]

        public DateTime CreatedDate { get; set; }

    }

I removed the ScreenName property and replaced it with a full fledged User property. This encapsulates the user relate properties into a single object which is represented by NGTweeterUser class. I added the CreatedDate property to store the date of the tweet. And the NGTweeterUser class is as follows

    [DataContract]

    public class NGTweeterUser

    {

        [DataMember]

        public int Id { get; set; }

 

        [DataMember]

        public string ScreenName { get; set; }

 

        [DataMember]

        public string Name { get; set; }

 

        [DataMember]

        public string ProfileImageUrl { get; set; }

    }

I will not display the code for the two adapters here as they are basically mapping properties from one class to another. You can check it out in the downloaded code.

Modify ListBox Control Template

Since the service implementation has changed I had to update the service reference. Because the internal representation of the data was also modified, I need to change the bindings as well. I rectified one of my mistake from the previous posts. I was using TwoWay binding. Actually we don’t need two way binding because I am not going to modify the tweets or any other details. In essence these are readonly properties. So I changed those bindings to OneWay to optimize the performance a little bit. Instead of showing the changes one by one, I’ll show the final markup itself

                    <ListBox.ItemTemplate>
                        <DataTemplate>

                                <Border Margin="2"
                                       Padding="3"
                                       BorderBrush="Black"
                                       CornerRadius="5"
                                       BorderThickness="1">
                                    <Grid>
                                        <Grid.ColumnDefinitions>
                                            <ColumnDefinition Width="Auto" />
                                            <ColumnDefinition />
                                            <ColumnDefinition />
                                        </Grid.ColumnDefinitions>
                                        <Grid.RowDefinitions>
                                            <RowDefinition />
                                            <RowDefinition />
                                            <RowDefinition />
                                        </Grid.RowDefinitions>

                                        <Image Source="{Binding User.ProfileImageUrl}"
                                              Height="40"
                                              Width="40"
                                              Margin="5"
                                              Grid.RowSpan="3"
                                              Grid.Row="0"
                                              Grid.Column="0"
                                              HorizontalAlignment="Center"
                                              VerticalAlignment="Top" />

                                        <TextBlock Text="{Binding User.ScreenName, Mode=OneTime}"
                                                  FontWeight="Bold"
                                                  Grid.Row="0"
                                                  Grid.Column="1" />

                                        <TextBlock Text="{Binding CreatedDate, Mode=OneTime, StringFormat='\{0:m\}'}"
                                                  TextAlignment="Right"
                                                  Grid.Row="0"
                                                  Grid.Column="2" />

                                        <TextBlock Text="{Binding Tweet, Mode=OneTime}"
                                                  TextWrapping="Wrap"
                                                  Grid.Row="1"
                                                  Grid.Column="1"
                                                  Grid.ColumnSpan="2"
                                                  Margin="2"/>
                                    </Grid>

                                </Border>
                        </DataTemplate>
                    </ListBox.ItemTemplate
>

I replaced the item template of the listbox. Earlier we had a stackpanel, this time I needed a layout with ability to specify different alignments for different controls. So I chose to use Grid. I placed the Image control to display the profile image on the top left corner. Then the ScreenName of the user followed by the date. The date is Right aligned. And finally the tweet in the second row of the grid spanning multiple columns. The profile image spans multiple rows. With all these changes the final output looks like


NGTweet screenshot


I think it looks pretty good compared to the scaled down text only version from previous post.


The image is displayed using the Image element and settign its source to the ProfileImageUrl property. I have also added markup for displaying the CreatedDate. Another feature I have used here is the formatting for date using StringFormat parameter of the databinding. Lets look closely at that piece of code

<TextBlock Text="{Binding CreatedDate, Mode=OneTime, StringFormat='\{0:m\}'}"
                                                  TextAlignment="Right"
                                                  Grid.Row="0"
                                                  Grid.Column="2"
/>

In previous versions of Silverlight we had to use the ValueConvertor for achieving something similar. But with Silverlight 4, we can use StringFormat which are commonly used formats for the formatting of values. Here I am using the date format which displays the date in month and day of month format.


Conclusion


When I started with this post in mind, I was thinking it might take me couple of hours to implement all these features. But it turned out to be far less than that. With Silverlight Control templates it was very easy to customize the look and feel of the controls. As we saw here that to change from a stack panel based layout to a grid based layout it was very simple. Addition of the StringFormat feature for binding has also made it easier for developers to display data in different formats lot more easier. It saves a lot of boilerplate code which needs to be written otherwise using ValueConvertors. I hope these features were useful to add to this tiny app.


As always the complete working solution is available for download at dropbox.


Until next time Happy Programming Smile

1 comment:

How Travis CI saved my time?

Background Some time back I created an Ansible playbook to install software and setup my Mac Book Pro . I put the code for this on GitHub . ...