The timeline view presents a Twitter user’s list of recent tweets (status updates). The TwitterTimelineViewModel
class relies on the ITwitterService
to retrieve the list of tweets.
TwitterTimelineViewModel
contains several properties, which are described in the following list:
TimelineItems—An
IEnumerable
of TimelineItem
objects, which is populated using the ITwitterService
. This list is bound to a ListBox
control in the view.
ScreenName—The Twitter screen name that was supplied by the user on the sign-in view.
ImageUrl—The URL to the Twitter user’s avatar. This is retrieved from the Twitter web API.
Message—An arbitrary string used to supply feedback to the user during retrieval.
Busy—A
bool
value indicating whether an asynchronous operation is under way.
The viewmodel also includes a command of type DelegateCommand<string>
named loadUserTimelineCommand
, which coordinates the retrieval of the list of status updates. LoadUserTimelineCommand
receives a Twitter screen name, which is passed to the ITwitterService
. See the following excerpt:
public class TwitterTimelineViewModel : ViewModelBase
{
readonly ITwitterService twitterService;
public TwitterTimelineViewModel(ITwitterService twitterService)
{
this.twitterService = ArgumentValidator.AssertNotNull(
twitterService, "twitterService");
loadUserTimelineCommand = new DelegateCommand<string>(LoadTimeline);
}
...
}
When the LoadUserTimelineCommand
executes, the LoadTimeline
method is called. This method first ensures that a Twitter screen name has been supplied; if not, it navigates back to the sign-in page.
The ITwitterService.GetCachedTimeline
method executes asynchronously and attempts to retrieve the cached timeline from the local database. When it returns, the viewmodel is populated using the HandleGetTimelineResult
. After attempting to restore the cached timeline, the ITwitterService.GetTimeline
method is used to retrieve the latest data from the Twitter web API. See the following excerpt:
void LoadTimeline(string twitterScreenName)
{
if (string.IsNullOrWhiteSpace(twitterScreenName))
{
var messageService = Dependency.Resolve<IMessageService>();
messageService.ShowError("Please provide a twitter screen name.");
var navigationService = Dependency.Resolve<INavigationService>();
navigationService.GoBack();
return;
}
if (twitterScreenName == screenName)
{
return;
}
Busy = true;
try
{
Message = "Retrieving cached timeline from database.";
twitterService.GetCachedTimeline(twitterScreenName,
args =>
{
HandleGetTimelineResult(args);
Message = "Retrieving real data from Twitter.";
Busy = true;
twitterService.GetTimeline(
twitterScreenName, HandleGetTimelineResult);
});
}
catch (Exception ex)
{
Busy = false;
Message = string.Format("Error retrieving items.");
Console.WriteLine(ex);
}
}
The HandleGetTimelineResult
method is called twice, once for the cached items and then for the live Twitter data. The Error
property of the ResultEventArgs
is used to determine whether the call completed successfully, and if so, the viewmodel’s properties are populated accordingly:
void HandleGetTimelineResult(ResultEventArgs<TwitterUser> e)
{
Busy = false;
if (e.Error != null)
{
Message = "Unable to retrieve timeline.";
return;
}
if (e.Result == null)
{
Message = "No result";
return;
}
Message = "Received result.";
TimelineItems = e.Result.TimelineItems.ToList();
ScreenName = e.Result.ScreenName;
ImageUrl = e.Result.ImageUrl;
}
When the viewmodel is instantiated, in the view’s code-beside class, it is passed a TwitterService
object, as shown:
public TwitterTimelineView()
{
InitializeComponent();
DataContext = new TwitterTimelineViewModel(new TwitterService());
}
The view executes the LoadUserTimelineCommand
in its OnNavigatedTo
handler, as shown:
protected override void OnNavigatedTo(NavigationEventArgs e)
{
base.OnNavigatedTo(e);
string twitterName;
if (NavigationContext.QueryString.TryGetValue("name", out twitterName))
{
ViewModel.LoadUserTimelineCommand.Execute(twitterName);
}
}
The view XAML contains controls to display the Twitter timeline information, as well as the Twitter user information. A ListBox
presents each TimelineItem
using a DataTemplate
, as shown in the following excerpt:
<Grid x:Name="ContentPanel" Grid.Row="1">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid Margin="{StaticResource PhoneMargin}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Image Source="{Binding ImageUrl}"
MaxWidth="60" MaxHeight="60" />
<TextBlock Grid.Column="1" Text="{Binding ScreenName}"
Style="{StaticResource PhoneTextExtraLargeStyle}" />
</Grid>
<ListBox Grid.Row="1"
ItemsSource="{Binding TimelineItems}"
Margin="{StaticResource PhoneHorizontalMargin}">
<ListBox.ItemTemplate>
<DataTemplate>
<Border CornerRadius="10"
BorderBrush="{StaticResource PhoneBorderBrush}"
BorderThickness="2"
Margin="14,5,4,10">
<Grid Margin="10">
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="100" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<TextBlock
Text="{Binding ReceivedTime,
StringFormat='0:HH:mm:ss'}"
MaxWidth="100" />
<TextBlock Text="{Binding Text}"
Grid.Column="1"
TextWrapping="Wrap"
Style="{StaticResource PhoneTextSmallStyle}" />
</Grid>
</Border>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
A ProgressIndicator
is used to monitor the Busy
property of the viewmodel and to display the viewmodel’s Message
text, as shown:
<shell:SystemTray.ProgressIndicator>
<shell:ProgressIndicator IsIndeterminate="{Binding Busy}"
IsVisible="{Binding Busy}"
Text="{Binding Message}" />
</shell:SystemTray.ProgressIndicator>
When a result is received from the Twitter service, it is displayed in the view as shown in Figure 29.5.
18.222.20.101