Monday, September 26, 2011

NGTweet Part 15–Silverlight Tooltip service

When I started the Silverlight learning series, I never thought I’ll be able to do a 15 part series. Things have gone well so far and they look good for another 15 parts. In todays post I am going to cover a very simple yet powerful feature of Silverlight which is the Tooltip service.

Tooltip is a very common feature available in all programming languages. Be it web based technologies or desktop applications, we always have need to display information to the en user in different forms. Tooltips are mainly used to enhance the user experience when the screen estate is less and the information to be presented to the user is more.  They are also used to display additional information with regards to context actions like button functionalities or an image detail. We find tooltips in almost every application. Lets see how to use tooltips in Silverlight application.

How to use Tooltip in Silverlight

Silverlight offers out of box Tooltip functionality. Same is the case with WPF and Windows Phone 7. The beauty of these technologies is they allow you to extend the default look and feel to come up with appealing UI to a customer. Tooltips are implemented as content controls and hence can be used to display any content. We can use images, graphics etc. within a tooltip. I don’t remember such a feature being available in earlier versions of desktop applications like Windows Forms 2.0.

Lets see where we can make use of the Tooltip in NGTweet. We could use it to display additional information in many places like the retweet button. It would be helpful for users to know what the button is meant for. Apart from that we can also use it to display additional text where we cannot display more information due to lack of space. One such place is the text that is displayed with the name of the user of the tweet. If the user name is long and that users tweet is retweeted by another user, we might run short of the space to display both their screen names. This is where I would like to use tooltip.

Before displaying tooltip

As can be seen from the above screenshot, first 3 tweets that are displayed are all retweets. The first and the second tweets are able to display the complete names of the two users. Notice carefully at the third one. It cannot fit in the available space. This is one place where we would use a tooltip to display the complete text.

                <TextBlock Text="{Binding ScreenName, Mode=OneTime}"
                          TextTrimming="WordEllipsis"
                          FontWeight="Bold"
                          Grid.Row="0"
                          Grid.Column="1">
                                        <ToolTipService.ToolTip>
                                            <ToolTip Content="{Binding ScreenName, Mode=OneTime}" />
                                        </ToolTipService.ToolTip>
                </TextBlock
>

In the above code snippet, I have used TextTrimming feature of TextBlock to truncate the text if it does not fit within the available space. Then I have used the ToolTipService to associate a tooltip with the textbox and bound it to the ScreenName property. We can see the output of this in the screenshot below


NGTweet with tooltip


The tooltip is displayed for the second tweet when we hover the mouse over the screen name. In general tooltips are used to display just the text. But in Silverlight, the tooltip control is a contentcontrol. This allows us to put any content as part of the tooltip. Here is a small example of displaying the profile image along with the screen name.

                    <ToolTipService.ToolTip>
                        <ToolTip>
                            <StackPanel>
                                <Image Source="{Binding ProfileImageSource, Mode=OneTime}"
                                       Height="50"
                                       Width="50" />
                                <TextBlock Text="{Binding ScreenName, Mode=OneTime}" />
                            </StackPanel>
                        </ToolTip>
                    </ToolTipService.ToolTip
>

I have stacked up the profile image and the screen name using a StackPanel. And the resulting output is


tooltip with Image


We can see the image and text both appearing as the tooltip for the 3rd tweet above. Being a content control, tooltip allows us all the other features like styling and templating.


Conclusion


Tooltip is very easy to use. Its helpful in building a good user experience. We can apply tooltip to any framework element like textbox, button, image etc.


As always the complete source code is available for download at Dropbox.


Until next time Happy Programming Smile

Monday, September 19, 2011

NGTweet Part 14–BasedOn Style in Silverlight

The two previous posts have been dedicated to styling in Silverlight. This is another one which is probably going to be the shortest of all the ones related to styling. We saw how to apply the explicit styles or named styles in the Part 12 of the NGTweet series. And in Part 13 we saw how we can use Implicit Styles to apply styles to controls without specifying any key.

This is not a third approach to apply styles in Silverlight. But using this approach we can make use of existing explicit Styles and extend them further. Although not completely related, this is much more like overriding a base class method in an object oriented language Smile.

Apply BasedOn style

Without any further delay lets see how we can make use of this feature to our advantage. This is the familiar sight of the NGTweet application screen.

NGTweet before BasedOn style

We have the border applied to each of the item in the listbox under both the timeline (AllFriends) and Mentions views. It uses the same style for both the borders. This is the Style definition for the border applied to items in the listbox.

    <Style x:Key="BorderStyle"
          TargetType="Border">
        <Setter Property="Margin"
               Value="2" />
        <Setter Property="Padding"
               Value="3" />
        <Setter Property="BorderBrush"
               Value="Black" />
        <Setter Property="CornerRadius"
               Value="5" />
        <Setter Property="BorderThickness"
               Value="1" />
    </Style
>

Lets modify the border on the left hand side for All Friends. We’ll add a shadow effect to this border. We don’t want to change settings like Margin and Padding which were set earlier. In most cases we would just copy the existing style and add the new elements or override the ones we are interested in. But the problem comes when we need to change any of the properties. We’ll have to change in multiple places.


Instead of that we can extend the existing style by adding or modifying only the properties we wish to change. So to add shadow to the border we can add following markup

    <Style BasedOn="{StaticResource BorderStyle}"
          TargetType="Border"
          x:Key="ThickBorderStyle">
        <Setter Property="BorderThickness"
               Value="1,1,4,4" />
        <Setter Property="BorderBrush">
            <Setter.Value>
                <LinearGradientBrush>
                    <GradientStop Color="#ccc"
                                 Offset="0" />
                    <GradientStop Color="#ddd"
                                 Offset="1" />
                </LinearGradientBrush>
            </Setter.Value>
        </Setter>
    </Style
>

The style definition is exactly the same like we define in case of implicit styles or explicit style. The only exception is the addition of BasedOn attribute which uses a StaticResource to refer to an existing style. We have overridden the BorderThickness and added a LinearGradientBrush to give the shadow effect.


To apply this new style we go to the timeline view’s markup and update the style for the border within the data template as

        <Border Style="{StaticResource ThickBorderStyle}">

We can run the application and see the effect of this change.


NGTweet after BasedOn style


We can see the difference in the border for the timeline tweets and the mentions tweets.


Conclusion


Styles is one of the most powerful feature of WPF and Silverlight. Prior to this it was very difficult for Winforms developers to apply same settings for all the controls across application. Imagine doing this in a Winforms 2.0 application. We’ll have to repeatedly apply same settings in the property window for each control.  Styles are very easy to use and extend as we saw in this example. Web developers had success by means of CSS stylesheets. But the features offered by Styles in WPF and Silverlight are powerful than in CSS. 


As always I have uploaded the complete working solution to Dropbox.


Until next time Happy Programming Smile

Sunday, September 18, 2011

NGTweet Part 13–Implicit Styles in Silverlight

In Part 12 of NGTweet learning series, I showed the use of Styles in Silverlight. The approach we used in applying style was to assign an identifier by means of a key and associate that key with the Style attribute of the control. This is an explicit way of styling the Silverlight controls. This approach works well when we have various combinations of visual styles that can be applied to a particular control. In this post I am going to demonstrate another mode of applying styles in Silverlight which is called Implicit Styling.

Need for implicit styling

Imagine a scenario where you want the same style applied to all instances of a control across the whole application. If we use the explicit styling approach, we’ll end up assigning the Style key for each instance of the control. This is bit tedious.  Also if you happen to change the name of the style from “ABC” to “XYZ”,, you’ll have to replace all those instances. It would be nice to specify the style for the control and let it be applied automatically in all places where the control is used. This is exactly what this post is all about (as you might have guessed from the title itself Smile).

How to apply implicit Styles

Many of the steps for implementing implicit style are similar to that of the explicit style. To get the right context lets look for a place where we can apply the implicit style within NGTweet application.

border with explicit style

If you observe carefully, I have added a white border around all the 3 column headings. This border has the same settings for all the three headings i.e. All Friends, Mentions and Compose. The markup applied to all 3 borders is as shown below

                <Border Margin="2"
                       Padding="3"
                       BorderBrush="White"
                       BorderThickness="1"
                       CornerRadius="5"
>

You can imagine what problem we’ll face if we have to change any of the setting like the border thickness from 1 to 2. We’ll have to change it in 3 places and may be even more if we add more columns later on to this application. So we refactor this piece of code to use the implicit style feature available in Silverlight.


Create a style for Targeted control


First step is to define a style in the common place. We had done a similar thing in the earlier post as well. We’ll use the same resource dictionary from the earlier posts. You could add this style to the ApplicationResorces dictionary if you don’t want to create a separate resource dictionary file.

    <Style TargetType="Border">
        <Setter Property="Margin"
               Value="2" />
        <Setter Property="Padding"
               Value="3" />
        <Setter Property="BorderBrush"
               Value="White" />
        <Setter Property="CornerRadius"
               Value="5" />
        <Setter Property="BorderThickness"
               Value="1" />
    </Style
>

Note that we have specified the TargetType as Border. We have not specified any Key here. This is very important. This results in the behaviour that we are trying to use. By not specifying any key we are instructing the parser to apply this style to all Border elements.


Merge Resource Dictionary with Application Resources


This step is similar to what we had done previously. If you are not aware, you can refer to the earlier post.


Remove inline styles


And finally we remove the inline style applied to border elements. So our final markup is

                <Border>
                    <TextBox Style="{StaticResource ReadOnlyHeaderTextBox}"
                            Text="Mentions" />
                </Border
>

You can see that we have specified only the Border element in the markup. The styles are applied implicitly to the Border element. It is very easy to override the properties defined in the implicit style for specific instances.


Style Gotcha’s


There are couple of Gotchas I came across while working on this post.


Gotcha 1 :


If you look at the code available in the attachment towards the end, you’ll find implicit style applied to border element for the column header’s borders. Also the border that appears for each Tweet within the list box has similar settings but is applied using explicit style. This is a gotcha in the Silverlight 4 as well as 5 versions. We cannot apply implicit styles for items within a data template. There is a work around which we have already implemented by means of explicit style. Hopefully this would be rectified in the future release of Silverlight.


Gotcha 2 :


I have been using the Expression Blend Dark Theme from Silverlight toolkit. This toolkit provides styles for many of the controls in Silverlight. All these Styles are implemented as implicit Styles. As a result we cannot create styles within the application which will extend the implicit styles easily. We can use explicit styles, but those will negate the styling of Expression Blend Dark theme. This is the reason the Listbox control has inline styles in some places.


Conclusion


As we saw in this post, it is very easy to apply styles across the application to visual elements using implicit styles. I haven’t tried it myself, but I came across some reading which suggested that WPF supports extending the implicit styles. As of now this feature is not available in Silverlight. Like with UpdateSourceTrigger for PropertyChanged, we can hope that the gap between WPF and Silverlight would be narrowed very soon.


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


Until next time Happy Programming Smile

Monday, September 12, 2011

NGTweet Part 12 - Styles in Silverlight

 

This post is an extension to one of the the earlier one in this NGTweet series which I did on Data Templates. In that post we saw how to represent data using template in Silverlight. Today I am going to use the feature of applying Styles for standardizing the visual elements.

Styles are very common in web development using HTML. They are normally implemented as CSS stylesheets. Similar feature was missing in desktop clients like VB6 or Winforms applications. WPF and Silverlight added support for Styles which behave much like the CSS styles. Windows Phone 7 is also based on XAML and supports styling out of the box.

Apply Styles to controls

The steps for applying Style to any control are pretty much same to that of applying a data template. Lets look at some code which is not using Style currently and refactor it to use Style so that we can get the context right.

All this while we have been using the inline Style for ListBox in our Mentions as well as the Timeline user controls. The following code block is what I am talking about

                    <ListBox.ItemContainerStyle>
                        <Style TargetType="ListBoxItem">
                            <Setter Property="HorizontalAlignment"
                                   Value="Stretch" />
                            <Setter Property="HorizontalContentAlignment"
                                   Value="Stretch" />
                        </Style>
                    </ListBox.ItemContainerStyle
>

We are styling the ItemContainer of the ListBox control. This is the container for each and every item in the ListBox. We have set the HorizontalAlingment and HorizontalContentAlignment properties to value Stretch. Like in Data Templates, in case of Styles as well we get the Intellisence support which is quite helpful in assigning the valid values. This code is exactly the same in the two user controls I mentioned above. This is a potential code smell and we should try and avoid it as much as possible. (In the hindsight, I think sometimes its better to put some intentional code smells as they allow you to refactor the code and write an exclusive post about it Smile)


We follow exactly the same steps we did for moving Data Templates from the user control to the resource dictionary. I’ll use the same resource dictionary to store the Styles as well, but if you want you can create a separate one for the Data Templates and Styles.


In case you are not aware how to create Resource Dictionary, refer to my earlier post. We’ll need the step 3 from this post as well to merge the resources into the main Application Resources. Assuming that the resource Dictionary is created we can move the Style definition to it.

    <Style TargetType="ListBoxItem"
           x:Key="ListBoxItemStyle">
        <Setter Property="HorizontalAlignment"
               Value="Stretch" />
        <Setter Property="HorizontalContentAlignment"
               Value="Stretch" />
    </Style
>

Note that we need to assign a unique Key for this Style element. This will be used to associate a Style with a control. If we specify the TargetType for the Style then this Style can be applied only to the controls of the target type. In the above case, we can apply this style only to ListBoxItem controls. So once we have defined the Style in the resource dictionary, we need to follow the 3rd step from the earlier post and merge the resource dictionary with the Applications resources.


As a final step we link the style to the control. So we delete the inline style for the ListBox.ItemContainerStyle and update the markup for ListBox to use the Style from resource dictionary as follows

                <ListBox Width="400"
                        Height="500"
                        HorizontalAlignment="Center"
                        HorizontalContentAlignment="Stretch"
                         ItemContainerStyle="{StaticResource ListBoxItemStyle}"
                        ScrollViewer.HorizontalScrollBarVisibility="Disabled"
                        ItemsSource="{Binding TweeterStatusViewModels}"
>

We bind the Style as a StaticResource to the ItemContainerStyle of ListBox using the key of Style. We can see the final output which is exactly the same as it was before


NGTweet with Style


Conclusion


Advantages of Styles is similar to those of using CSS. We can modify the style in a central location and all the places where it is used will get impacted. The main difference between a data template and style is that the Style is applied to visual elements while a data template is applied to a data and its representation.


What I have demonstrated here is the explicit way of using Styles. We use the Key of the Style to associate the Style with a type of control. There is also an implicit way of applying Styles to controls. I’ll cover that in another post.


We can override the Styles at the control level for specific needs. This behaves the same way like in HTML where you can assign attributes to HTML elements even though they are defined in the CSS stylesheet.


Styles provide a great way for building consistent user interfaces using XAML. It also helps in collaborating the work between the designers and developers. Designers can concentrate on making the user interface appealing to the user while the developers can concentrate on building the application logic.


Hope you enjoyed reading this post. As always the complete working solution is available for download at Dropbox.


Until next time Happy Programming Smile

Sunday, September 04, 2011

NGTweet Part 11– Silverlight 5 UpdateSourceTrigger

 

In one of the earlier post on Data Validation of this NGTweet series I had demonstrated the use of IDataErrorInfo to validate data. While the post was sufficient to demonstrate the use of IDataErrorInfo, there was a slight shortcoming in that implementation. This was due to a limitation in the Silverlight 4 DataBinding. In order to fire the validation, we had to wait till the focus was lost from the text box because the binding fires for textbox control only on lost focus event. This limitation is applicable only in case of Silverlight. WPF supports firing the change notification for databound textbox as soon as the text is entered in the control.

Use UpdateSourceTrigger to update data source

Just to highlight what I said earlier, as you can see from the screenshot below, there is text entered in the textbox. But the send button is not enabled until the focus is lost from the textbox. From usability point of view this is not really user friendly. We would want the send button to be enabled as soon as some text is entered by the user.

Without UpdateSourceTrigger

If you are not using MVVM, then this is not a very big issue. You can write code in code behind file by subscribing to the TextChanged event. The MVVM purists who don’t believe in writing code in the code behind file have used tricks like adding a behaviour to the text box and updating the data source. But it was clearly a limitation of the Silverlight in itself that we were trying to address.

Not anymore. With the latest release of Silverlight 5 this issue has been addressed. The built in data binding supports a new mode for the UpdateSourceTrigger. If we set this to the value PropertyChanged the framework takes care of updating the data source.

                <TextBox Text="{Binding TweetText, Mode=TwoWay, ValidatesOnDataErrors=True, UpdateSourceTrigger=PropertyChanged}"
                        UseLayoutRounding="True"
                        TextWrapping="Wrap"
                        Height="100"
                        Width="300"
                        Margin="5"
/>

With that one change you can see the impact, the moment I enter any text the send button is enabled.


With UpdateSourceTrigger


Conclusion


This might not be a big change and people working on WPF wouldn’t feel much about it. But in Silverlight context it is very helpful. It makes life easier for MVVM practitioners. As mentioned earlier there are alternatives to overcome this shortcoming. Having it as part of the framework is more like a necessity. Its good to see that Silverlight is adding more and more features which makes life easier for MVVM developers.


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


Until next time Happy Programming Smile

Friday, September 02, 2011

NGTweet Silverlight learning series

 

Some time back I started working on the Silverlight client for Twitter. This was named NGTweet. Here is a collection of all the posts related to NGTweet series.

Part 1 – TweetSharp. This is the first part of the series which starts with the groundwork of using a third party API called TweetSharp to connect to Twitter. We display the tweets from the public timeline.

Part 2 – OAuth Authentication. Twitter allows multiple ways to connect to the API. OAuth is one of them and this post is all about how to get OAuth Authentication working using a Silverlight client. This post also demonstrates the use of HTML Bridge to call Javascript function from the Silverlight code. I also make use of the IsolatedStorage to store local data in Silverlight which is used across application start-ups as well as system restarts.

Part 3 – Theames. A simple way of standardizing user interface using the concept of theme. I am using the Themes provided by Silverlight Toolkit. I am using the ExpressionBlendDark theme.

Part 4 – Control Templates. A very important feature of Silverlight which allows the controls to be extended and enhanced as per the user requirements. An example of  extending the Listbox control.

Part 5 – Refactoring NGTweet. A step towards restructuring things. I refactored the code with a set of unit tests, abstracted the IsolatedStorage, integrated MVVMLight toolkit, deployed application to IIS, unit tested View Model and added test data using NBuilder.

Part 6 – Value converter. This is an example of separating the concerns related to the view and representation of the data. A convertor can be used to elegantly display data whose representation is different from what is presented to the user in the view or the presentation layer. In this demo, I have shown how to display a date time value in relative time i.e. internally the value is stored as a date time object itself. While representing it on the GUI, I am showing time relative to the current time in terms of seconds, minutes, hours, days etc.

Part 7 – Decoupled communication between View Models. MVVM Light is one of the very lightweight framework. Its very simple to use and it offers  some of the powerful features. One such feature is to decouple the communication between view models. This post demonstrates the use of Messenger class from MVVM Light toolkit.

Part 8 – Data Validation using IDataErrorInfo. In any Line of Business (LOB) application validating input is a very integral part. Silverlight offers IDataErrorInfo interface which is very straight forward to use. A small demo of using IDataErroInfo to validate user input.

Part 9 – Data Triggers. This post demonstrates the use of Data Triggers using Microsoft.Expression.Interactions dll. There is no default support for Data Triggers as of Silverlight 4.

Part 10 – Data Templates. Data Templates offers us a way of centralizing styles in one place similar to the CSS Styles in HTML. Data Templates are equally powerful as CSS.

Part 11 – Silverlight 5 PropertyChanged when using UpdateSourceTrigger. This post demonstrates the new feature available in Silverlight 5 with regards to Data Binding. This is an extension to the data validation post done earlier in Part 8 of the same series.

Part 12 – Styles in Silverlight. Styles are similar to data templates. They are used for styling the visual elements in the user interface.

Part 13 – Implicit Styles. In part 12 we saw how to apply common settings to visual elements using Styles. Silverlight also supports implicit styling which can be applied to all the controls across the application. We do not provide a key for the style which results in the style being applied implicitly to all the target elements. This features is used by the Themes provided by Silverlight Toolkit like the Expression Blend Dark Theme which I have been using in this series.

Part 14 – Extend styles using BasedOn. Many times we want to apply the same style in two different places with just a minor change like in case of a normal textbox and a read only text box. We might need almost the same settings except for one or two values. In such cases we can create a style with the common settings and extend it for specific cases using the BasedOn approach.

Part 15 – Silverlight Tooltip Service. Tooltips are a common way to display context based information. They are supported in all types of graphical user interfaces. This post shows how we can make use of the Silverlight Tooltip service to display tooltips.

NGTweet Part 10 : Data Templates in Silverlight

 

This is the 10th part of the NGTweet series. This is the first time I have written a 10 part series. It feels great. Hopefully there will be many more to come. In part 9, we saw the use of Data Triggers. In this part I am going to refactor the XAML code for couple of reasons.

First and foremost, there is lot of duplication. I have strong feeling about code quality and I don’t like code duplication. To manage the codebase better I would like to use the Styles and Templates feature available in Silverlight.

Secondly, there is a bug in inline data templates with Silverlight. It leads to memory leaks. The list controls used in the NGTweet application have been using the inline data templates all along. I want to be on the safer side.

Resources and Data Templates

To avoid the memory leak problem mentioned above, we make use of the data template feature available in Silverlight. We have already used this feature. But in this post I am going to demonstrate how we can extend it to make it reusable across elements like user controls and pages. This allows us to build consistent user interfaces. We can define data templates at multiple levels.

If a template is going to be used in only one user control or a single page, we can add it to the resources section of that particular user control or a page.

If a template is going to be used across the application, we can add it to the applications resources.

Usually templates definitions are lengthy and can be quite difficult to manage. For better manageability, we can split the template definitions into smaller chunks known as Resource Dictionaries. And then merge these dictionaries into the Application Resources. In this post I am going to use this method.

1. Create Resource Dictionary

The first step towards refactoring the inline data template is to create a new Resource Dictionary. This has an extension of XAML itself. Add a new item to the project and select item type as Silverlight Resource Dictionary

Silverlight Resource Dictionary

Note : Usually all the resources for a particular project like Styles, Images etc. are grouped under an Assets folder under the root directory. I have the path Assets/Styles for storing the resource dictionaries.

This file will have the root tag named ResourceDictionary.

2. Move Data Templates from user control to resource dictionary

The second step involves removing the inline data template from user control and moving it to the newly created resource dictionary. For simplicity I will not copy the code for data template here. You can refer to the attached source code to get the details. As we move the data template, its related dependencies like converters and reference namespaces also needs to be moved to the resource dictionary.

In order to identify the Data Template uniquely, we need to assign a identifier to it using the Key property. I have named the data template as TimeLineListDataTemplate.

3. Merge resources with the Application’s ResourceDictionary

As I mentioned earlier, we can have resources at different levels. We can merge resources at individual control level or at the application level as well. If a particular resource is likely to be used across the whole application its easier to add it in one central place which happens to be the Application Resource Dictionary. We use the following Xaml to achieve this :

    <Application.Resources>
        <ResourceDictionary x:Key="ResourceDictionaries">
            <ResourceDictionary.MergedDictionaries>
                <ResourceDictionary Source="Assets/Styles/TimeLineListItemStyle.xaml" />
            </ResourceDictionary.MergedDictionaries>
        </ResourceDictionary>

        <ViewModel:ViewModelLocator x:Key="Locator" />

    </Application.Resources
>

This is added to the App.xaml file. Note that the ResorceDictionary needs to have the Key as well.


4. Refer Data Template from Resource Dictionary in the user control

The last step is to reference the Data Template in the user control or page. This is done using ItemTemplate dependency property of the list box. The syntax is similar to that of data binding, but we use the StaticResource markup extension.

                <ListBox Width="400"
                        Height="500"
                        HorizontalAlignment="Center"
                        HorizontalContentAlignment="Stretch"
                        ScrollViewer.HorizontalScrollBarVisibility="Disabled"
                         ItemTemplate="{StaticResource TimeLineListDataTemplate}"
                        ItemsSource="{Binding TweeterStatusViewModels}"
>

If we run the application now, we see the same output as before.


NGTweet screen


Conclusion


Data Templates as well as styles are similar to CSS Stylesheets in HTML. They provide us a way of defining the visual styles in a central place. Using data template also improves performance of the application. The XAML parser is optimized for parsing data templates and rendering is faster. Externalizing data templates also helps us to indirectly address the memory leak issue with inline data templates. Although that cannot be a valid reason to use external data templates. Its sad that this issue has been around for quite sometime. Hopefully Microsoft will address it in near future. 


As always I have uploaded the complete working solution to Dropbox.


Until next time Happy Programming Smile


Note : The memory leak issue related to inline data templates is applicable only in Silverlight 4.

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 . ...