WP7漫画阅读器






4.91/5 (4投票s)
介绍如何构建一个简单的Windows Phone 7应用程序,用于读取和存储WP7中的漫画feed。
引言
在本文中,我将解释如何创建一个简单的应用程序,它可以读取 Dilbert、xkcd 和 Phd 漫画的订阅源,并在 WP7 上显示它们。通过对代码进行简单的修改,您可以添加其他各种漫画系列的订阅源,并在您的 Windows Phone 7 上查看它们。
背景
感谢微软提供一个出色的平台,用于使用 Silverlight 进行 WP7 应用程序开发。它简单、快速且有趣。
Using the Code
让我们从 XAML 开始。

漫画阅读器的主屏幕很容易创建。我有一个包含两行一列的网格。第一个网格包含一个堆叠面板,其中包含标题“Comic Reader”,而另一个网格包含一个 Listbox
,其中托管着每种漫画类型的按钮。
<Grid x:Name="LayoutRoot" Background="Transparent">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<!--TitlePanel contains the name of the application and page title-->
<StackPanel x:Name="TitlePanel" Grid.Row="0" Margin="12,17,0,28">
<TextBlock x:Name="PageTitle" Text="Comics Reader" Margin="9,-7,0,0"
Style="{StaticResource PhoneTextTitle1Style}"/>
<TextBlock x:Name="ApplicationTitle"
Text="Read your favorite web comics on the move!"
Style="{StaticResource PhoneTextNormalStyle}"/>
</StackPanel>
<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
<ListBox ScrollViewer.HorizontalScrollBarVisibility="Auto"
ScrollViewer.VerticalScrollBarVisibility="Auto">
<controls:ComicButton x:Name="btnComicDilbert"
Click="btnComicDilbert_Click"/>
<controls:ComicButton x:Name="btnComicXkcd" Click="btnComicXkcd_Click" />
<controls:ComicButton x:Name="btnComicPhdComics"
Click="btnComicPhdComics_Click" />
</ListBox>
</Grid>
</Grid>
在代码背后,我们通过以下方式强制横向方向:
this.SupportedOrientations = SupportedPageOrientation.PortraitOrLandscape;
每个漫画订阅源都有三个最重要的标签来提供:
- 漫画文章的图片
- 漫画文章的标题
- 文章描述
基于这个假设,comicpage.xaml,即托管漫画文章的地方,如下所示:
<Grid x:Name="LayoutRoot" Background="Transparent">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<!--TitlePanel contains the name of the application and page title-->
<StackPanel x:Name="TitlePanel" Grid.Row="0" Margin="12,17,0,28">
<TextBlock x:Name="PageTitle" Text="" Margin="9,-7,0,0"
Style="{StaticResource PhoneTextTitle1Style}"
FontStretch="SemiCondensed" />
</StackPanel>
<!--ContentPanel - place additional content here-->
<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
<phone:WebBrowser HorizontalAlignment="Stretch" Margin="0,0,0,203"
Name="webBrowser1" VerticalAlignment="Stretch" Width="Auto" />
<TextBlock Height="76" HorizontalAlignment="Left" Margin="9,451,0,0"
Name="DescriptionTextBlock" Text="" VerticalAlignment="Top" Width="441"
TextWrapping="Wrap" ScrollViewer.VerticalScrollBarVisibility="Auto"
ScrollViewer.HorizontalScrollBarVisibility="Auto" />
<Grid x:Name="Spinner" Background="Transparent" Visibility="Collapsed"
HorizontalAlignment="Center" Margin="168,0,79,-140" Width="209">
<Grid.RenderTransform>
<ScaleTransform x:Name="SpinnerScale" ScaleX="0.5" ScaleY="0.5" />
</Grid.RenderTransform>
<Canvas RenderTransformOrigin="0.5,0.5" Width="120" Height="120">
<Ellipse Width="21.835" Height="21.862"
Canvas.Left="20.1696" Canvas.Top="9.76358"
Stretch="Fill" Fill="#E6FFFFFF"/>
<Ellipse Width="21.835" Height="21.862"
Canvas.Left="2.86816" Canvas.Top="29.9581"
Stretch="Fill" Fill="#CDFFFFFF"/>
<Ellipse Width="21.835" Height="21.862"
Canvas.Left="5.03758e-006" Canvas.Top="57.9341"
Stretch="Fill" Fill="#B3FFFFFF"/>
<Ellipse Width="21.835" Height="21.862"
Canvas.Left="12.1203" Canvas.Top="83.3163"
Stretch="Fill" Fill="#9AFFFFFF"/>
<Ellipse Width="21.835" Height="21.862"
Canvas.Left="36.5459" Canvas.Top="98.138"
Stretch="Fill" Fill="#80FFFFFF"/>
<Ellipse Width="21.835" Height="21.862"
Canvas.Left="64.6723" Canvas.Top="96.8411"
Stretch="Fill" Fill="#67FFFFFF"/>
<Ellipse Width="21.835" Height="21.862"
Canvas.Left="87.6176" Canvas.Top="81.2783"
Stretch="Fill" Fill="#4DFFFFFF"/>
<Ellipse Width="21.835" Height="21.862"
Canvas.Left="98.165" Canvas.Top="54.414"
Stretch="Fill" Fill="#34FFFFFF"/>
<Ellipse Width="21.835" Height="21.862"
Canvas.Left="92.9838" Canvas.Top="26.9938"
Stretch="Fill" Fill="#1AFFFFFF"/>
<Ellipse Width="21.835" Height="21.862"
Canvas.Left="47.2783" Canvas.Top="0.5"
Stretch="Fill" Fill="#FFFFFFFF"/>
<Canvas.RenderTransform>
<RotateTransform x:Name="SpinnerRotate" Angle="0" />
</Canvas.RenderTransform>
<Canvas.Triggers>
<EventTrigger RoutedEvent="ContentControl.Loaded">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetName="SpinnerRotate"
Storyboard.TargetProperty="(RotateTransform.Angle)"
From="0" To="360" Duration="0:0:01"
RepeatBehavior="Forever" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Canvas.Triggers>
</Canvas>
</Grid>
</Grid>
</Grid>
在内容加载到浏览器之前,我使用上面显示的故事板动画运行加载器。
代码组织方式如何?
当我们查看代码时,我们有 ComicFeed
类,其中包含一个 ComicArticle
对象列表。每个 ComicArticle
对象包含所有显示ComicPage.xaml 上漫画文章的信息。
虽然所有订阅源都相似,但由于订阅源存在细微差异,我们需要分别解析每个订阅源。因此,对于每个漫画系列,我们都有一个单独的类 DilbertFeed
、XkcdFeed
和 PhdFeed
,它们继承自 ComicFeed
类。
ComicFeed
类的编写方式如下:
public abstract class ComicFeed : INotifyPropertyChanged
{
public Uri ImageSource
{
get;
set;
}
public string NotificationCount
{
get;
set;
}
public string ComicTitle
{
get;
set;
}
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
virtual protected void OnPropertyChanged(string name)
{
if (PropertyChanged != null)
{ PropertyChanged(this, new PropertyChangedEventArgs(name)); }
}
#endregion
public EventHandler ArticlesUpdated;
public TextBlock comicTitle;
public string rssFeedLink = string.Empty;
public int latestArticleNumber = 0;
public bool downloadInProgress = false;
public virtual string ModifyHtml(string html, string title)
{
return html;
}
public virtual void SyncLatestArticles()
{
WebClient webclient = new WebClient();
webclient.DownloadStringCompleted += new DownloadStringCompletedEventHandler
(this.webclient_DownloadStringCompleted);
webclient.DownloadStringAsync(new Uri(this.rssFeedLink));
}
public virtual ComicArticle GetArticle(int counter)
{
throw new NotImplementedException();
}
public virtual void webclient_DownloadStringCompleted
(object sender, DownloadStringCompletedEventArgs e)
{
}
public List<ComicArticle> comicArticles = new List<ComicArticle>();
public int limitOfArticles = 4;
}
默认情况下,每个漫画订阅源的文章数量限制为 4。当前的实现将漫画文章存储在隔离存储中,因此需要限制漫画文章的数量,以防止我们的应用程序消耗隔离存储中的所有空间。
对于每个漫画系列,将在隔离存储中创建一个相应的文件夹,并将文章存储在这些文件夹中。GetArticles(int articleNumber)
用于从隔离存储中检索文章,如下所示:
public override ComicArticle GetArticle(int counter)
{
using (var appStorage = IsolatedStorageFile.GetUserStoreForApplication())
{
if (!appStorage.DirectoryExists(this.comicTitle.Text))
{
return null;
}
List<string> articleDirectory = appStorage.GetDirectoryNames
(string.Format(@"{0}\*", this.comicTitle.Text)).ToList();
if (articleDirectory.Count <= counter)
{
return null;
}
else
{
ComicArticle article = new ComicArticle();
string filePath = System.IO.Path.Combine
(this.comicTitle.Text, articleDirectory[counter]);
using (var cachedFile = appStorage.OpenFile
(string.Format("{0}\\content.html", filePath), FileMode.Open))
using (TextReader reader = new StreamReader(cachedFile))
{
string html = reader.ReadToEnd();
article.html = html;
article.htmlFilePath = filePath;
article.publishDate = articleDirectory[counter];
}
return article;
}
}
}
结果
Dibert 漫画系列...

Phd 漫画系列...

xkcd 漫画系列...

待办事项
在创建此应用程序(2010 年 9 月)时,我想要一个快速的解决方案来渲染从订阅源下载的图像。当时没有 GIF 图像解码器,因此我使用了 Web 浏览器。最近,CodePlex 上的 ImageTools 项目提供了在 WP7 上显示所有 GIF 图像的支持。
历史
- 2010 年 11 月 30 日:初始发布