文采不好,将就着看,见谅
思路最重要,故本文不提供源码下载
由于Windows Phone在考虑性能的情况下限制每个TextBlock最多显示高度为2000像素的文本,若超过此值,将自动被截断,显示空白。
网上有很多此问题的解决方案,如:计算文字像素,向容器添加多个TextBlock。但此方法效率相对较低且可能导致文本分段不正确。
据我所知常使用的方案有两种:
1.分页,监听ScrollViewer,当滚动到最下方,自动加载新页内容(类似瀑布流)
2.使用WebBrowser
方案一在纯文本的情况下可使用(也存在分段不正确的问题)
本文介绍第二种方案,使用WebBrowser
在使用之前需在网络下载以下文件
1.Newtonsoft.Json.WindowsPhone.dll
用于json解析(本示例仅使用了一小部分此程序集的功能,为提高效率和减小XAP文件大小,您可考虑自行编写解析代码)
2.HtmlAgilityPack.dll
用于解析html,修改图片尺寸(为提高效率和减小XAP文件大小,您可自行编写正则表达式,查询img标签并替换相关属性)
3.jquery-1.9.1.js(其它版本也行)
用于脚本编写(本示例并未大量使用脚本,可不使用jquery,自行编写原生js亦可)
概述
windows phone自带IE9浏览器,全面支持HTML5
可完美实现图文混排
开发时,可采用两种方案
1.将读取的文章按文件单独存放到独立存储空间,查看时直接读取相应文件即可。
优点:效率高
缺点:不利于更新,如果日后更新文章模板,在不清除原有文件的情况下,曾经保存的文章板式得不到更新
2.建立数据库,将文章内容存放到数据库,查看时动态生成html页
注:使用此方法需考虑文本被截断的问题,Windows phone使用的是SQL CE,没有text类型字段,字符串类型列最大值为4000个字符,如超过将抛出异常。
可使用如下方法解决:将获取的文本内容以3000个字符为单位截取(为什么不是4000?考虑到SQL行最大字节为8049),以多条数据的形式保存(此方法并不是最好的,如有更好解决方法请指教)
优点:方便更新,不占用手机内存
缺点:性能较方案一低
由于WebBrowser控件默认允许拖动和放大缩小
阻止缩放可在Html的Head中添加:
<meta content="width=device-width,user-scalable=no" name="viewport">
阻止拖动,我们可做如下工作,具体可参考:http://www.scottlogic.com/blog/2010/03/04/linq-to-visual-tree.html
或百度搜索:LinqToVisualTree
操作思路
1.编写主要逻辑代码
2.相应的资源文件和模板放入项目,将生成操作设置为:Resources
之所以要做这一步,是因为WebBrowser控件读取的是独立存储空间的文件,在控件初始化时,需读取相应文件将其存放到独立存储空间
运行时思路
1.将拷贝的资源文件和模板文件转移到独立存储空间
2.获取文章详细内容
3.读取模板文件
4.替换模板内容
5.保存html文件
6.显示html
关于图片加载
加载内容,难免会存在图片,图片都存在于网络且尺寸不同,若每次显示都加载网络图片,势必会浪费用户流量,耗费时间
解决尺寸不同:
通过正则表达式或其它手段获取img标签,将宽设置为100%,删除高(自适应)
解决图片路径:
通过正则表达式或其它手段获取img标签,得到图片url,将图片缓存至独立存储空间,下次加载,首先判断独立存储空间是否存在对应文件,如存在则直接加载,若不存在则在加载网络图片的同时将图片缓存至独立存储空间。
class="code_img_closed" src="/Upload/Images/2013082210/0015B68B3C38AA5B.gif" alt="" />logs_code_hide('00143993-f4f1-4b99-9820-e8e8a707249f',event)" src="/Upload/Images/2013082210/2B1B950FA3DF188F.gif" alt="" />
/// <summary> /// 调整图片 /// </summary> /// <param name="html"></param> /// <param name="imageWidth"></param> /// <returns></returns> private string RevisionImg(string html, int imageWidth) { HtmlDocument document = new HtmlDocument(); document.LoadHtml(html); if (document == null) { return html; } var images = document.DocumentNode.SelectNodes("//img"); if (images == null || images.Count == 0) { return html; } foreach (HtmlNode image in images) { if (imageWidth != 0) { image.Attributes.Remove("width"); //image.Attributes.Add("width", string.Format("{0}px", imageWidth)); image.Attributes.Add("width", string.Format("{0}%", 100)); } if (image.Attributes["height"] != null) { image.Attributes["height"].Remove(); } if (image.Attributes["src"] != null) { try { string imageUrl = image.Attributes["src"].Value; //获取图片url,若不存在,则缓存至独立存储空间 StorageCachedImage cacheImage = new StorageCachedImage(new Uri(imageUrl, UriKind.Absolute)); if (cacheImage.IsSaveLocal) { imageUrl = "/" + cacheImage.ImageUri.Replace("\\", "/").TrimStart('/'); } image.Attributes["src"].Value = imageUrl; } catch { } } } return document.DocumentNode.OuterHtml; }代码片段
关于javascript与C#互操作
设置WebBrowser控件IsScriptEnabled属性为True
注册WebBrowser控件LoadCompleted和ScriptNotify事件
在LoadCompleted事件中可编写调用js初始化方法
在ScriptNotify事件中可监听javascript,脚本中通过window.external.notify触发ScriptNotify事件
public override void OnApplyTemplate() { base.OnApplyTemplate(); this._webBrowser = this.GetTemplateChild("WebBrowser") as WebBrowser; this._webBrowser.LoadCompleted += _webBrowser_LoadCompleted; this._webBrowser.ScriptNotify += _webBrowser_ScriptNotify; } void _webBrowser_LoadCompleted(object sender, System.Windows.Navigation.NavigationEventArgs e) { this._webBrowser.InvokeScript("init"); } void _webBrowser_ScriptNotify(object sender, NotifyEventArgs e) { string keyValue = e.Value; if (string.IsNullOrWhiteSpace(keyValue)) { return; } JObject jObject = JObject.Parse(keyValue); string key = jObject["key"].Value<string>(); string value = jObject["value"].Value<string>(); if (string.IsNullOrWhiteSpace(key) || string.IsNullOrWhiteSpace(value)) { return; } if (key == "imgClick") { } else if (key == "aClick") { this.OpenLink(value); } }代码片段
源码
新建项目,创建自定义控件WebBrowserArticle
using HtmlAgilityPack; using Microsoft.Phone.Controls; using Microsoft.Phone.Tasks; using Newtonsoft.Json.Linq; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Windows; using System.Windows.Controls; using System.Windows.Input; using ZP.AppLibrary; namespace ZP.Controls { public class WebBrowserArticle : Control { private const string _htmlTemplateFilePath = "/html/content_template.html"; private const string _htmlFilePath = "/html/index.html"; private WebBrowser _webBrowser; public bool ScrollDisabled { get; set; }//是否禁用滚动 public WebBrowserArticle() { this.DefaultStyleKey = typeof(WebBrowserArticle); this.Loaded += WebBrowserArticle_Loaded; } #region 属性 public static readonly DependencyProperty TitleProperty = DependencyProperty.Register( "Title", typeof(string), typeof(WebBrowserArticle), new PropertyMetadata(null, OnTextPropertyChanged)); public string Title { get { return (string)GetValue(TitleProperty); } set { SetValue(TitleProperty, value); } } public static readonly DependencyProperty OtherDataProperty = DependencyProperty.Register( "OtherData", typeof(string), typeof(WebBrowserArticle), new PropertyMetadata(null, OnTextPropertyChanged)); /// <summary> /// 其它数据(多个用字符“~”分隔) /// </summary> public string OtherData { get { return (string)GetValue(OtherDataProperty); } set { SetValue(OtherDataProperty, value); } } public static readonly DependencyProperty BodyProperty = DependencyProperty.Register( "Body", typeof(string), typeof(WebBrowserArticle), new PropertyMetadata(null, OnTextPropertyChanged)); public string Body { get { return (string)GetValue(BodyProperty); } set { SetValue(BodyProperty, value); } } #endregion void WebBrowserArticle_Loaded(object sender, RoutedEventArgs e) { if (this._webBrowser != null) { this.SaveHtml(); var border = this._webBrowser.Descendants<Border>().Last() as Border; border.ManipulationDelta += Border_ManipulationDelta; border.ManipulationCompleted += Border_ManipulationCompleted; } } public override void OnApplyTemplate() { base.OnApplyTemplate(); this._webBrowser = this.GetTemplateChild("WebBrowser") as WebBrowser; this._webBrowser.LoadCompleted += _webBrowser_LoadCompleted; this._webBrowser.ScriptNotify += _webBrowser_ScriptNotify; } void _webBrowser_LoadCompleted(object sender, System.Windows.Navigation.NavigationEventArgs e) { this._webBrowser.InvokeScript("init"); } void _webBrowser_ScriptNotify(object sender, NotifyEventArgs e) { string keyValue = e.Value; if (string.IsNullOrWhiteSpace(keyValue)) { return; } JObject jObject = JObject.Parse(keyValue); string key = jObject["key"].Value<string>(); string value = jObject["value"].Value<string>(); if (string.IsNullOrWhiteSpace(key) || string.IsNullOrWhiteSpace(value)) { return; } if (key == "imgClick") { } else if (key == "aClick") { this.OpenLink(value); } } /// <summary> /// 打开链接 /// </summary> /// <param name="url"></param> private void OpenLink(string url) { WebBrowserTask webBrowserTask = new WebBrowserTask(); webBrowserTask.Uri = new Uri(url, UriKind.Absolute); webBrowserTask.Show(); } private static void OnTextPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { WebBrowserArticle source = (WebBrowserArticle)d; //string html = (string)e.NewValue; source.SaveHtml(); } private void SaveHtml() { if (this._webBrowser == null || this.Title == null || this.OtherData == null || this.Body == null) { return; } Common.CopyContentToIsolatedStorage("/Controls;component/WebBrowserArticle/html/content_template.html", _htmlTemplateFilePath); string template = IsolatedStorageHelper.OpenFile(_htmlTemplateFilePath); template = template.Replace("{title}", this.Title); template = template.Replace("{body}", this.Body); string otherHtml = string.Empty; string otherData = this.OtherData; if (!string.IsNullOrWhiteSpace(otherData)) { string[] otherDatas = otherData.Split(new string[] { "~" }, StringSplitOptions.RemoveEmptyEntries); foreach (string item in otherDatas) { otherHtml += string.Format("<span class=\"otherData\">{0}</span>", item); } } template = template.Replace("{otherData}", otherHtml); template = this.RevisionImg(template, (int)this._webBrowser.ActualWidth); IsolatedStorageHelper.SaveFile(_htmlFilePath, template); Common.CopyContentToIsolatedStorage("/Controls;component/WebBrowserArticle/html/js/jquery-1.9.1.js", "/html/js/jquery-1.9.1.js"); Common.CopyContentToIsolatedStorage("/Controls;component/WebBrowserArticle/html/js/content.js", "/html/js/content.js"); Common.CopyContentToIsolatedStorage("/Controls;component/WebBrowserArticle/html/css/content.css", "/html/css/content.css"); this._webBrowser.Navigate(new Uri(_htmlFilePath, UriKind.RelativeOrAbsolute)); } /// <summary> /// 调整图片 /// </summary> /// <param name="html"></param> /// <param name="imageWidth"></param> /// <returns></returns> private string RevisionImg(string html, int imageWidth) { HtmlDocument document = new HtmlDocument(); document.LoadHtml(html); if (document == null) { return html; } var images = document.DocumentNode.SelectNodes("//img"); if (images == null || images.Count == 0) { return html; } foreach (HtmlNode image in images) { if (imageWidth != 0) { image.Attributes.Remove("width"); //image.Attributes.Add("width", string.Format("{0}px", imageWidth)); image.Attributes.Add("width", string.Format("{0}%", 100)); } if (image.Attributes["height"] != null) { image.Attributes["height"].Remove(); } if (image.Attributes["src"] != null) { try { string imageUrl = image.Attributes["src"].Value; //获取图片url,若不存在,则缓存至独立存储空间 StorageCachedImage cacheImage = new StorageCachedImage(new Uri(imageUrl, UriKind.Absolute)); if (cacheImage.IsSaveLocal) { imageUrl = "/" + cacheImage.ImageUri.Replace("\\", "/").TrimStart('/'); } image.Attributes["src"].Value = imageUrl; } catch { } } } return document.DocumentNode.OuterHtml; } #region 阻止拖动 private void Border_ManipulationCompleted(object sender, ManipulationCompletedEventArgs e) { if (e.FinalVelocities.ExpansionVelocity.X != 0.0 || e.FinalVelocities.ExpansionVelocity.Y != 0.0) { e.Handled = true; } } private void Border_ManipulationDelta(object sender, ManipulationDeltaEventArgs e) { if (e.DeltaManipulation.Scale.X != 0.0 || e.DeltaManipulation.Scale.Y != 0.0) { e.Handled = true; } if (ScrollDisabled) { if (e.DeltaManipulation.Translation.X != 0.0 || e.DeltaManipulation.Translation.Y != 0.0) { e.Handled = true; } } } #endregion } }代码片段
新建目录:themes
在目录themes新建:generic.xaml
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:system="clr-namespace:System;assembly=System" xmlns:controls="clr-namespace:当前控件命名空间;assembly=当前控件程序集名称" xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone" xmlns:windows="clr-namespace:System.Windows;assembly=System.Windows"> <Style TargetType="controls:WebBrowserArticle"> <Style.Setters> <Setter Property="Template"> <Setter.Value> <ControlTemplate> <phone:WebBrowser Name="WebBrowser" IsScriptEnabled="True" /> </ControlTemplate> </Setter.Value> </Setter> </Style.Setters> </Style> </ResourceDictionary>代码片段
将“当前控件命名空间”替换为你所建立自定义控件的命名空间
将“当前空间程序集名称”替换为你所建立自定义控件的程序集名称
如:
新建html模板,并导入相关文件
body { margin: 0; padding: 0px; -ms-text-size-adjust: none; } header { padding: 0px 0 20px; } .title { font-size: 25px; font-weight: bold; line-height: 30px; color: #454545; margin: 10px 0 20px 0px; } .otherInfo { font-size: 12px; color: #afafaf; } .otherData { margin: 0px 5px 0 5px; } article { margin-top: -15px; line-height: 25px; margin-right: 0px; margin-bottom:50px; font-size: 14px; color: #7c7c7c; } article div { clear: both; float: right; margin: 10px 2px 5px 5px; overflow: hidden; height: 125px; width: 125px; } article div img { border: 0; } article p { margin: 0; }content.css
var xapKEY = { imgClick: "imgClick", aClick: "aClick" }; function callXAP(key, value) { var jsonData = "{key:\"" + key + "\",value:\"" + value + "\"}"; window.external.notify(jsonData); } function init() { //$("img").click(function () { // var currObj = $(this); // var imageUrl = currObj.attr("src").toLowerCase(); // if (imageUrl.indexOf('.gif') > 0) { // currObj.remove(); // } else { // currObj.mousedown(function () { // callXAP(xapKEY.imgClick, imageUrl); // }); // } //}); //$("a img").unbind("click"); $("a").click(function () { var currObj = $(this); var link = currObj.attr("href"); if (link && link.indexOf("javascript:") == -1) { callXAP(xapKEY.aClick, link); } return false; }); $("embed,object").remove(); }content.js
<!DOCTYPE html> <html> <head> <title></title> <meta charset="UTF-8"> <meta content="width=device-width,user-scalable=no" name="viewport"> <link href="css/content.css" rel="stylesheet" /> <script type="text/javascript" src="js/jquery-1.9.1.js"></script> </head> <body> <header> <div class="title">{title}</div> <div class="otherInfo">{otherData}</div> </header> <article> {body} </article> <script type="text/javascript" src="js/content.js"></script> </body> </html>content_template.html
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Windows; using System.Windows.Media; namespace ZP.AppLibrary { /// <summary> /// Adapts a DependencyObject to provide methods required for generate /// a Linq To Tree API /// </summary> public class VisualTreeAdapter : ILinqTree<DependencyObject> { private DependencyObject _item; public VisualTreeAdapter(DependencyObject item) { _item = item; } public IEnumerable<DependencyObject> Children() { int childrenCount = VisualTreeHelper.GetChildrenCount(_item); for (int i = 0; i < childrenCount; i++) { yield return VisualTreeHelper.GetChild(_item, i); } } public DependencyObject Parent { get { return VisualTreeHelper.GetParent(_item); } } } /// <summary> /// Defines an interface that must be implemented to generate the LinqToTree methods /// </summary> /// <typeparam name="T"></typeparam> public interface ILinqTree<T> { IEnumerable<T> Children(); T Parent { get; } } public static class TreeExtensions { /// <summary> /// Returns a collection of descendant elements. /// </summary> public static IEnumerable<DependencyObject> Descendants(this DependencyObject item) { ILinqTree<DependencyObject> adapter = new VisualTreeAdapter(item); foreach (var child in adapter.Children()) { yield return child; foreach (var grandChild in child.Descendants()) { yield return grandChild; } } } /// <summary> /// Returns a collection containing this element and all descendant elements. /// </summary> public static IEnumerable<DependencyObject> DescendantsAndSelf(this DependencyObject item) { yield return item; foreach (var child in item.Descendants()) { yield return child; } } /// <summary> /// Returns a collection of ancestor elements. /// </summary> public static IEnumerable<DependencyObject> Ancestors(this DependencyObject item) { ILinqTree<DependencyObject> adapter = new VisualTreeAdapter(item); var parent = adapter.Parent; while (parent != null) { yield return parent; adapter = new VisualTreeAdapter(parent); parent = adapter.Parent; } } /// <summary> /// Returns a collection containing this element and all ancestor elements. /// </summary> public static IEnumerable<DependencyObject> AncestorsAndSelf(this DependencyObject item) { yield return item; foreach (var ancestor in item.Ancestors()) { yield return ancestor; } } /// <summary> /// Returns a collection of child elements. /// </summary> public static IEnumerable<DependencyObject> Elements(this DependencyObject item) { ILinqTree<DependencyObject> adapter = new VisualTreeAdapter(item); foreach (var child in adapter.Children()) { yield return child; } } /// <summary> /// Returns a collection of the sibling elements before this node, in document order. /// </summary> public static IEnumerable<DependencyObject> ElementsBeforeSelf(this DependencyObject item) { if (item.Ancestors().FirstOrDefault() == null) yield break; foreach (var child in item.Ancestors().First().Elements()) { if (child.Equals(item)) break; yield return child; } } /// <summary> /// Returns a collection of the after elements after this node, in document order. /// </summary> public static IEnumerable<DependencyObject> ElementsAfterSelf(this DependencyObject item) { if (item.Ancestors().FirstOrDefault() == null) yield break; bool afterSelf = false; foreach (var child in item.Ancestors().First().Elements()) { if (afterSelf) yield return child; if (child.Equals(item)) afterSelf = true; } } /// <summary> /// Returns a collection containing this element and all child elements. /// </summary> public static IEnumerable<DependencyObject> ElementsAndSelf(this DependencyObject item) { yield return item; foreach (var child in item.Elements()) { yield return child; } } /// <summary> /// Returns a collection of descendant elements which match the given type. /// </summary> public static IEnumerable<DependencyObject> Descendants<T>(this DependencyObject item) { return item.Descendants().Where(i => i is T).Cast<DependencyObject>(); } /// <summary> /// Returns a collection of the sibling elements before this node, in document order /// which match the given type. /// </summary> public static IEnumerable<DependencyObject> ElementsBeforeSelf<T>(this DependencyObject item) { return item.ElementsBeforeSelf().Where(i => i is T).Cast<DependencyObject>(); } /// <summary> /// Returns a collection of the after elements after this node, in document order /// which match the given type. /// </summary> public static IEnumerable<DependencyObject> ElementsAfterSelf<T>(this DependencyObject item) { return item.ElementsAfterSelf().Where(i => i is T).Cast<DependencyObject>(); } /// <summary> /// Returns a collection containing this element and all descendant elements /// which match the given type. /// </summary> public static IEnumerable<DependencyObject> DescendantsAndSelf<T>(this DependencyObject item) { return item.DescendantsAndSelf().Where(i => i is T).Cast<DependencyObject>(); } /// <summary> /// Returns a collection of ancestor elements which match the given type. /// </summary> public static IEnumerable<DependencyObject> Ancestors<T>(this DependencyObject item) { return item.Ancestors().Where(i => i is T).Cast<DependencyObject>(); } /// <summary> /// Returns a collection containing this element and all ancestor elements /// which match the given type. /// </summary> public static IEnumerable<DependencyObject> AncestorsAndSelf<T>(this DependencyObject item) { return item.AncestorsAndSelf().Where(i => i is T).Cast<DependencyObject>(); } /// <summary> /// Returns a collection of child elements which match the given type. /// </summary> public static IEnumerable<DependencyObject> Elements<T>(this DependencyObject item) { return item.Elements().Where(i => i is T).Cast<DependencyObject>(); } /// <summary> /// Returns a collection containing this element and all child elements. /// which match the given type. /// </summary> public static IEnumerable<DependencyObject> ElementsAndSelf<T>(this DependencyObject item) { return item.ElementsAndSelf().Where(i => i is T).Cast<DependencyObject>(); } } public static class EnumerableTreeExtensions { /// <summary> /// Applies the given function to each of the items in the supplied /// IEnumerable. /// </summary> private static IEnumerable<DependencyObject> DrillDown(this IEnumerable<DependencyObject> items, Func<DependencyObject, IEnumerable<DependencyObject>> function) { foreach (var item in items) { foreach (var itemChild in function(item)) { yield return itemChild; } } } /// <summary> /// Applies the given function to each of the items in the supplied /// IEnumerable, which match the given type. /// </summary> public static IEnumerable<DependencyObject> DrillDown<T>(this IEnumerable<DependencyObject> items, Func<DependencyObject, IEnumerable<DependencyObject>> function) where T : DependencyObject { foreach (var item in items) { foreach (var itemChild in function(item)) { if (itemChild is T) { yield return (T)itemChild; } } } } /// <summary> /// Returns a collection of descendant elements. /// </summary> public static IEnumerable<DependencyObject> Descendants(this IEnumerable<DependencyObject> items) { return items.DrillDown(i => i.Descendants()); } /// <summary> /// Returns a collection containing this element and all descendant elements. /// </summary> public static IEnumerable<DependencyObject> DescendantsAndSelf(this IEnumerable<DependencyObject> items) { return items.DrillDown(i => i.DescendantsAndSelf()); } /// <summary> /// Returns a collection of ancestor elements. /// </summary> public static IEnumerable<DependencyObject> Ancestors(this IEnumerable<DependencyObject> items) { return items.DrillDown(i => i.Ancestors()); } /// <summary> /// Returns a collection containing this element and all ancestor elements. /// </summary> public static IEnumerable<DependencyObject> AncestorsAndSelf(this IEnumerable<DependencyObject> items) { return items.DrillDown(i => i.AncestorsAndSelf()); } /// <summary> /// Returns a collection of child elements. /// </summary> public static IEnumerable<DependencyObject> Elements(this IEnumerable<DependencyObject> items) { return items.DrillDown(i => i.Elements()); } /// <summary> /// Returns a collection containing this element and all child elements. /// </summary> public static IEnumerable<DependencyObject> ElementsAndSelf(this IEnumerable<DependencyObject> items) { return items.DrillDown(i => i.ElementsAndSelf()); } /// <summary> /// Returns a collection of descendant elements which match the given type. /// </summary> public static IEnumerable<DependencyObject> Descendants<T>(this IEnumerable<DependencyObject> items) where T : DependencyObject { return items.DrillDown<T>(i => i.Descendants()); } /// <summary> /// Returns a collection containing this element and all descendant elements. /// which match the given type. /// </summary> public static IEnumerable<DependencyObject> DescendantsAndSelf<T>(this IEnumerable<DependencyObject> items) where T : DependencyObject { return items.DrillDown<T>(i => i.DescendantsAndSelf()); } /// <summary> /// Returns a collection of ancestor elements which match the given type. /// </summary> public static IEnumerable<DependencyObject> Ancestors<T>(this IEnumerable<DependencyObject> items) where T : DependencyObject { return items.DrillDown<T>(i => i.Ancestors()); } /// <summary> /// Returns a collection containing this element and all ancestor elements. /// which match the given type. /// </summary> public static IEnumerable<DependencyObject> AncestorsAndSelf<T>(this IEnumerable<DependencyObject> items) where T : DependencyObject { return items.DrillDown<T>(i => i.AncestorsAndSelf()); } /// <summary> /// Returns a collection of child elements which match the given type. /// </summary> public static IEnumerable<DependencyObject> Elements<T>(this IEnumerable<DependencyObject> items) where T : DependencyObject { return items.DrillDown<T>(i => i.Elements()); } /// <summary> /// Returns a collection containing this element and all child elements. /// which match the given type. /// </summary> public static IEnumerable<DependencyObject> ElementsAndSelf<T>(this IEnumerable<DependencyObject> items) where T : DependencyObject { return items.DrillDown<T>(i => i.ElementsAndSelf()); } } }LinqToVisualTree.cs
using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.IO.IsolatedStorage; using System.Linq; using System.Net; using System.Text; using System.Windows.Media.Imaging; namespace ZP.AppLibrary { /// <summary> /// 独立存储缓存图片 /// </summary> public sealed class StorageCachedImage : BitmapSource { private readonly Uri uriSource; private readonly string filePath; private const string CacheDirectory = "Shared/ShellContent/CachedImages"; /// <summary> /// 是否保存在本地 /// </summary> public bool IsSaveLocal { get; set; } static StorageCachedImage() { //创建缓存目录 using (var isolatedStorageFile = IsolatedStorageFile.GetUserStoreForApplication()) { if (!isolatedStorageFile.DirectoryExists(CacheDirectory)) { isolatedStorageFile.CreateDirectory(CacheDirectory); } } } /// <summary> /// 创建一个独立存储缓存的图片源 /// </summary> /// <param name="uriSource"></param> public StorageCachedImage(Uri uriSource) { this.uriSource = uriSource; string imageAbsolutePath = uriSource.AbsolutePath.ToLower(); string extension = Path.GetExtension(imageAbsolutePath).ToLower(); imageAbsolutePath = imageAbsolutePath.Replace(extension, ""); //去除扩展名其它字符如:*****.jpg!80、*****.jpg#80 if (extension.IndexOf(".jpeg") != -1) { extension = ".jpeg"; } else if (extension.IndexOf(".jpg") != -1) { extension = ".jpg"; } else if (extension.IndexOf(".png") != -1) { extension = ".png"; } else if (extension.IndexOf(".bmp") != -1) { extension = ".bmp"; } else if (extension.IndexOf(".gif") != -1) { extension = ".gif"; } imageAbsolutePath += extension; imageAbsolutePath = imageAbsolutePath.TrimStart('/').Replace('/', '_').Replace(':', '_'); //文件路径 filePath = Path.Combine(CacheDirectory, imageAbsolutePath); OpenCatchSource(); } /// <summary> /// 打开缓存源 /// </summary> private void OpenCatchSource() { bool exist; using (var isolatedStorageFile = IsolatedStorageFile.GetUserStoreForApplication()) { exist = isolatedStorageFile.FileExists(filePath); } this.IsSaveLocal = exist; if (exist) { SetCacheStreamSource(); } else { SetWebStreamSource(); } } /// <summary> /// 设置缓存流到图片 /// </summary> private void SetCacheStreamSource() { using (var isolatedStorageFile = IsolatedStorageFile.GetUserStoreForApplication()) { using (var stream = isolatedStorageFile.OpenFile(filePath, FileMode.Open, FileAccess.Read)) { SetSource(stream); } } } /// <summary> /// 下载Uri中的图片 /// </summary> private void SetWebStreamSource() { var httpWebRequest = (HttpWebRequest)WebRequest.Create(uriSource); httpWebRequest.AllowReadStreamBuffering = true; httpWebRequest.BeginGetResponse(ResponseCallBack, httpWebRequest); } /// <summary> /// 下载回调 /// </summary> /// <param name="asyncResult"></param> private void ResponseCallBack(IAsyncResult asyncResult) { var httpWebRequest = asyncResult.AsyncState as HttpWebRequest; if (httpWebRequest == null) return; try { var response = httpWebRequest.EndGetResponse(asyncResult); using (var stream = response.GetResponseStream()) { using (var isolatedStorageFile = IsolatedStorageFile.GetUserStoreForApplication()) { using (var fileStream = isolatedStorageFile.OpenFile (filePath, FileMode.OpenOrCreate, FileAccess.Write)) { stream.CopyTo(fileStream); } } Dispatcher.BeginInvoke(SetCacheStreamSource); } } catch (Exception err) { Debug.WriteLine(err.Message); } } /// <summary> /// 当前图片Uri /// </summary> public string ImageUri { get { bool exist; using (var isolatedStorageFile = IsolatedStorageFile.GetUserStoreForApplication()) { exist = isolatedStorageFile.FileExists(this.filePath); } if (exist) { return this.filePath; } else { return uriSource.ToString(); } } } } }StorageCachedImage.cs
/// <summary> /// 复制文件到独立存储空间 /// </summary> /// <param name="sourceFileUrl">源文件</param> /// <param name="saveFileUrl">保存文件</param> public static void CopyContentToIsolatedStorage(string sourceFileUrl, string saveFileUrl = null) { using (IsolatedStorageFile iso = IsolatedStorageFile.GetUserStoreForApplication()) { saveFileUrl = string.IsNullOrWhiteSpace(saveFileUrl) ? sourceFileUrl : saveFileUrl; //if (iso.FileExists(saveFileUrl)) { iso.DeleteFile(saveFileUrl); } if (!iso.FileExists(saveFileUrl)) { var fullDirectory = System.IO.Path.GetDirectoryName(saveFileUrl); if (!iso.DirectoryExists(fullDirectory)) { iso.CreateDirectory(fullDirectory); } using (Stream input = Application.GetResourceStream(new Uri(sourceFileUrl, UriKind.Relative)).Stream) { using (IsolatedStorageFileStream output = iso.CreateFile(saveFileUrl)) { byte[] readBuffer = new byte[4096]; int bytesRead = -1; while ((bytesRead = input.Read(readBuffer, 0, readBuffer.Length)) > 0) { output.Write(readBuffer, 0, bytesRead); } } } } } } /// <summary> /// 保存文件 /// </summary> /// <param name="fileName"></param> /// <param name="content"></param> public static void SaveFile(string fileName, string content) { using (var store = System.IO.IsolatedStorage.IsolatedStorageFile.GetUserStoreForApplication()) { if (store.FileExists(fileName)) { store.DeleteFile(fileName); } using (var stream = store.CreateFile(fileName)) { byte[] buffer = System.Text.Encoding.UTF8.GetBytes(content); stream.Write(buffer, 0, buffer.Length); } } } /// <summary> /// 打开文件 /// </summary> /// <param name="fileName"></param> /// <returns></returns> public static string OpenFile(string fileName) { string data = string.Empty; using (IsolatedStorageFile myIsolatedStorage = IsolatedStorageFile.GetUserStoreForApplication()) { if (myIsolatedStorage.FileExists(fileName)) { using (IsolatedStorageFileStream fileStream = myIsolatedStorage.OpenFile(fileName, FileMode.Open, FileAccess.ReadWrite)) { StreamReader sr = new StreamReader(fileStream, System.Text.Encoding.UTF8); data = sr.ReadToEnd(); sr.Close(); } } } return data; }其它相关方法