• Windows Phone 显示长文本


    文采不好,将就着看,见谅

    思路最重要,故本文不提供源码下载

    参考项目:

    http://www.windowsphone.com/zh-cn/store/app/%E5%BF%83%E7%90%86fm/41089a68-2efa-47f7-bd40-a508a1c8c5df

    由于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,将图片缓存至独立存储空间,下次加载,首先判断独立存储空间是否存在对应文件,如存在则直接加载,若不存在则在加载网络图片的同时将图片缓存至独立存储空间。

    /// <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;
            }
    其它相关方法
  • 相关阅读:
    基于fis的前端模块化和工程化方案
    网页中font-family的属性解析
    centos 基础环境配置
    golang的各种数据格式的互相转换
    golang 的编译安装以及supervisord部署
    ip地址库选择
    golang中使用mongodb的操作类以及如何封装
    golang中使用mongodb
    golang使用sqlite
    centos 安装nginx
  • 原文地址:https://www.cnblogs.com/immensity/p/WPTextBlock.html
Copyright © 2020-2023  润新知