经过前面的准备,现在可以动手实现SinaService接口和界面布局啦!
SinaService的实现不难,用linq解析下xml就好了。AppKey,用户名,密码就要自己去申请啦。
private void InitWebClient( ref WebClient wc)
{
wc = new WebClient();
string usernamePassword = Settings.Username + " : " + Settings.Password;
wc.Credentials = new NetworkCredential(Settings.Username, Settings.Password);
wc.Headers[ " Authorization " ] = " Basic " + Convert.ToBase64String(System.Text.Encoding.UTF8.GetBytes(usernamePassword));
}
/// <summary>
/// Gets the statuses.
/// </summary>
/// <param name="onGetStatusesCompleted"> The on get Statuses completed. </param>
/// <param name="onError"> The on error. </param>
public void GetStatuses(Int64 sinceId, Int64 maxId,
Action < List < Status >> onGetStatusesCompleted = null , Action < Exception > onError = null , Action onFinally = null )
{
WebClient wcRefreshHome = null ;
InitWebClient( ref wcRefreshHome);
// if (wcRefreshHome.IsBusy) wcRefreshHome.CancelAsync();
// register on download complete event
wcRefreshHome.DownloadStringCompleted += delegate ( object sender, DownloadStringCompletedEventArgs e)
{
try
{
// report error
if (e.Error != null )
{
if (onError != null )
{
onError(e.Error);
}
return ;
}
XElement xmlResult = XElement.Parse(e.Result);
List < Status > list = new List < Status > ();
ParseService.ParseTimeLine(xmlResult, ref list);
// notify completed callback
if (onGetStatusesCompleted != null )
{
onGetStatusesCompleted(list);
}
}
catch (Exception ex)
{
Trace.DetailMsg(ex.Message);
if (onError != null )
{
onError(ex);
}
return ;
}
finally
{
// notify finally callback
if (onFinally != null )
{
onFinally();
}
}
};
string requestUrl = Settings.RefreshHomeURL + " ?source= " + Settings.AppKey;
// The list is empty, just get some status
if (sinceId == 0 && maxId == 0 )
{
requestUrl = requestUrl + " &count= " + Settings.NumberPerRequest;
}
// Get status later than sinceId
if (sinceId != 0 && maxId == 0 )
{
requestUrl = requestUrl + " &since_id= " + (sinceId + 1 ) + " &count= " + Settings.NumberPerRequest;
}
// Get status early than maxId
if (sinceId == 0 && maxId != 0 )
{
requestUrl = requestUrl + " &max_id= " + (maxId - 1 ) + " &count= " + Settings.NumberPerRequest;
}
wcRefreshHome.DownloadStringAsync( new Uri(requestUrl), Settings.RefreshHomeURL);
}
}
相应的ParseService如下:
public static class ParseService
{
public static void ParseTimeLine(XElement xElement, ref List < Status > statusList)
{
try
{
foreach (XElement status in xElement.Descendants( " status " ))
{
statusList.Add(ParseStatus(status));
}
}
catch (FormatException ex)
{
Deployment.Current.Dispatcher.BeginInvoke(() =>
{
MessageBox.Show(ex.Message.ToString());
});
}
}
private static void ParseUserList(XElement xElement, ref List < User > userList )
{
try
{
foreach (XElement user in xElement.Descendants( " user " ))
{
User friend = new User();
userList.Add(ParseUser(user));
}
}
catch (FormatException ex)
{
Deployment.Current.Dispatcher.BeginInvoke(() =>
{
MessageBox.Show(ex.Message.ToString());
});
}
}
private static Status ParseStatus(XElement status)
{
Status newStatus = new Status();
try
{
newStatus.Id = long .Parse(status.Element( " id " ).Value);
newStatus.Text = status.Element( " text " ).Value;
Regex regex = new Regex( " >.*< " );
newStatus.Source = status.Element( " source " ).Value;
Match match = regex.Match(newStatus.Source);
if (match.Success)
{
newStatus.Source = match.ToString().Replace( " < " , string .Empty).Replace( " > " , string .Empty);
}
newStatus.CreatedAt = DateTime.ParseExact(status.Element( " created_at " ).Value,
" ddd MMM dd HH:mm:ss zzzz yyyy " ,
CultureInfo.CurrentCulture,
DateTimeStyles.AllowWhiteSpaces);
newStatus.Favorited = bool .Parse(status.Element( " favorited " ).Value);
// newTweet.Retweeted = bool.Parse(status.Element("retweeted").Value);
// newTweet.RetweetCount = int.Parse(status.Element("retweet_count").Value);
newStatus.User = ParseUser(status.Element( " user " ));
// if (!status.Element("thumbnail_pic").IsEmpty)
// {
// newStatus.ThumbnailPic = status.Element("thumbnail_pic").Value;
// }
newStatus.ThumbnailPic = status.Element( " thumbnail_pic " ).Value;
}
catch (Exception ex)
{
Trace.DetailMsg( " ParseStatus Error: " + ex.Message);
}
return newStatus;
}
private static User ParseUser(XElement User)
{
User user = new User();
try
{
user.Id = ulong .Parse(User.Element( " id " ).Value);
user.ScreenName = User.Element( " screen_name " ).Value;
user.Name = User.Element( " name " ).Value;
user.ImageUrl = User.Element( " profile_image_url " ).Value;
user.Url = User.Element( " url " ).Value;
user.Followers = int .Parse(User.Element( " followers_count " ).Value);
user.Friends = int .Parse(User.Element( " friends_count " ).Value);
user.Description = User.Element( " description " ).Value;
user.Location = User.Element( " location " ).Value;
user.CreatedAt = DateTime.ParseExact(User.Element( " created_at " ).Value,
" ddd MMM dd HH:mm:ss zzzz yyyy " ,
CultureInfo.CurrentCulture,
DateTimeStyles.AllowWhiteSpaces);
}
catch (Exception ex)
{
Trace.DetailMsg( " ParseUser Error: " + ex.Message);
}
return user;
}
}
主页面用到了Pivot Control
< Grid x:Name = " LayoutRoot " Background = " Transparent " >
< ProgressBar IsIndeterminate = " {Binding IsHomeRefreshing} " VerticalAlignment = " Top "
Visibility = " {Binding IsHomeRefreshing, Converter={StaticResource VisibilityConverter}} " />
<!-- Pivot Control -->
< controls:Pivot Title = " 围脖七 " >
<!-- Pivot item one -->
< controls:PivotItem >
< controls:PivotItem.Header >
< ContentControl >
< StackPanel >
< TextBlock Text = " 主页 " FontSize = " 54 " />
</ StackPanel >
</ ContentControl >
</ controls:PivotItem.Header >
< ListBox d:LayoutOverrides = " Height " Grid.Row = " 1 " d:IsHidden = " True "
ItemsSource = " {Binding HomeList, Mode=OneWay} " ItemTemplate = " {StaticResource TweetItemTemplate} "
SelectionChanged = " ListBox_SelectionChanged " />
</ controls:PivotItem >
<!-- Pivot item two -->
< controls:PivotItem >
< controls:PivotItem.Header >
< ContentControl >
< TextBlock Text = " 消息 " FontSize = " 54 " FontFamily = " Segoe WP Light " />
</ ContentControl >
</ controls:PivotItem.Header >
< Grid VerticalAlignment = " Top " >
< ListBox x:Name = " TopicList " ItemsSource = " {Binding TopicList, Mode=OneWay} " />
</ Grid >
</ controls:PivotItem >
</ controls:Pivot >
</ Grid >
每条微博的DataTemplate如下
< DataTemplate x:Name = " TweetItemTemplate " >
< Grid Margin = " 0,5 " >
< Grid.ColumnDefinitions >
< ColumnDefinition Width = " Auto " />
< ColumnDefinition Width = " * " />
</ Grid.ColumnDefinitions >
< Image Grid.Column = " 0 " Source = " {Binding User.ImageUrl} "
Height = " 73 " Width = " 73 " VerticalAlignment = " Top " Margin = " 0,5,7,0 "
HorizontalAlignment = " Left " />
< StackPanel Grid.Column = " 1 " VerticalAlignment = " Top " >
< Grid Width = " 380 " >
< TextBlock Text = " {Binding User.ScreenName} " VerticalAlignment = " Center " Foreground = " #FFC8AB14 " FontSize = " 24 " />
< StackPanel HorizontalAlignment = " Right " Orientation = " Horizontal " VerticalAlignment = " Center " >
< Image Source = " /Images/Tulips.png " Visibility = " {Binding ThumbnailPic, Converter={StaticResource VisibilityConverter}} " />
< TextBlock Text = " {Binding CreatedAt, Converter={StaticResource TimeConverter}} "
FontSize = " 20 " Foreground = " LightGray "
Margin = " 5,0 " />
</ StackPanel >
</ Grid >
<!-- TextBlock Text = " {Binding Text} " FontSize = " 22 " TextWrapping = " Wrap " /-->
< TextBlock Text = " {Binding Text} " TextWrapping = " Wrap " FontFamily = " Arial " />
</ StackPanel >
</ Grid >
</ DataTemplate >
记得在MainPageViewModel的构造函数中调用RefreshHomeList
if ( ! DesignerProperties.IsInDesignTool)
{
if (HomeList.Count == 0 )
{
RefreshHomeList( 0 , 0 );
}
}
最后把viewmodel和view联系起来,在App.xaml中声明
xmlns:viewModels = " clr-namespace:WeiBo7.ViewModels "
< Application.Resources >
< misc:VisibilityConverter x:Key = " VisibilityConverter " />
< misc:TimeConverter x:Key = " TimeConverter " />
< viewModels:MainPageViewModel x:Key = " MainPageViewModel " />
</ Application.Resources >
在MainPage.xaml中声明就大功告成了!
DataContext = " {StaticResource MainPageViewModel} "
因为间隔时间比较长,上面只是提到了些关键实现,有些地方可能忘记了。如有问题请留言给我!