CloudNotes之桌面客戶端篇:增強的筆記列表

dax.net發表於2015-01-07

今天,我釋出了CloudNotes的一個更新版本:1.0.5484.36793。這個版本與1.0.5472.20097不同的是,它擁有增強的筆記列表,與之前單調的列表系統相比,新的筆記列表不僅可以顯示筆記的摘要內容,而且還可以從筆記中抽取第一張圖片,並顯示圖片的詳細資訊:

image

怎麼樣?相比之前的筆記列表,現在的設計是不是能夠展示更豐富的資訊呢?

升級到最新版本

如果在讀完我的第一篇關於CloudNotes的文章,《CloudNotes:一個雲端個人筆記系統》之後,已經安裝並體驗了上一個版本的CloudNotes,那麼,當你重新開啟CloudNotes桌面客戶端時,你將在登入介面,或者主介面的狀態列部分看到發現新版本更新的通知資訊:

image

image

你可以點選這個資訊來進入新版本更新系統。然而不幸的是,更新系統會告訴你,更新失敗,原因是因為你開啟了Windows 7的使用者帳戶控制(UAC)的功能,更新程式不具備訪問C:\Program Files (x86)目錄的許可權。解決方案有如下兩種:

  1. 進入控制皮膚,關閉使用者帳戶控制(UAC)功能
  2. 點選這裡】下載CloudNotes更新程式的補丁包,下載以後,將zip檔案解壓並覆蓋CloudNotes安裝目錄下的Updater目錄中(比如:C:\Program Files (x86)\daxnet\CloudNotes Desktop Client)的所有檔案,然後重新通過CloudNotes桌面客戶端進入升級程式,即可完成升級

如果你是第一次看到有CloudNotes這麼個玩意兒,並且打算嘗試使用的話,請直接【點選這裡】下載版本1.0.5484.36793,它是截止到本文撰寫時的最新版本,包含了這個最新的日誌列表和修復的CloudNotes更新程式。

技術實現

在此大致介紹一下我是如何實現這種全新的筆記列表介面的,並簡要介紹一下如何讓應用程式在UAC下具有訪問檔案系統資源的許可權。

增強的筆記列表

說起來也有趣,有一天我看到有個同事在用Evernote,覺得它的筆記列表檢視做得挺不錯,還有縮圖:

image

給人的感覺就是能夠在簡單的檢視中體現更多的筆記資訊,讓人更容易地找到需要檢視的筆記內容。於是我也在想,我的CloudNotes桌面客戶端是否也可以實現類似的效果。

經過一番研究之後,我決定對目前筆記列表所使用的樹形控制元件進行自定義:從System.Windows.Forms.TreeView類繼承一個自定義的TreeViewEx型別,並使用OwnerDrawText的自定義繪畫方式,對每個樹狀節點(TreeNode)進行重繪,以達到類似的效果。

在TreeNode進行重繪時(就是在OnDrawNode事件處理過程中),TreeViewEx會根據當前節點上的TreeNodeExItem資料,對樹狀節點進行重繪,從而達到上面截圖中展示的效果。實現起來其實並不困難,就是需要有耐心,文字的顏色、定位、圖片的尺寸等等,都需要花時間慢慢調整。這部分程式碼我也不多做解釋了,朋友們請自行參考CloudNotes.DesktopClient專案下Controls\TreeViewEx.cs原始碼檔案中的實現即可。相比之下,更為有趣技術點主要有:

從HTML中獲得純文字資訊

這就是用於顯示筆記的摘要文字部分。由於筆記是HTML格式儲存的,而摘要部分卻僅需要筆記文字的開頭一段即可,因此,就需要從HTML中去掉HTML的標記,從中提取可讀的純文字資訊。在CloudNotes中,我是使用下面的擴充套件方法來實現這個功能的。該方法同時被WebAPI和桌面客戶端使用。通過程式碼可以看到,我僅僅將筆記中的前100個文字作為筆記的摘要資訊。

/// <summary>
/// Extract the description from the html.
/// </summary>
/// <param name="html"></param>
/// <returns></returns>
public static string ExtractDescription(this string html)
{
    var plainText = html.RemoveHtmlTags();
    return plainText.Substring(0, plainText.Length < 100 ? plainText.Length : 100);
}

/// <summary>
/// Removes all the HTML tags and bad characters from the given HTML string.
/// </summary>
/// <param name="html">The source HTML string.</param>
/// <returns></returns>
private static string RemoveHtmlTags(this string html)
{
    html = HttpUtility.UrlDecode(html);
    html = HttpUtility.HtmlDecode(html);

    html = RemoveTag(html, "<!--", "-->");
    html = RemoveTag(html, "<script", "</script>");
    html = RemoveTag(html, "<style", "</style>");

    //replace matches of these regexes with space
    html = Tags.Replace(html, " ");
    html = NotOkCharacter.Replace(html, " ");
    html = SingleSpacedTrim(html);

    return html;
}

提取筆記中的第一張圖片

為了顯示筆記圖片的縮圖,首先需要提取筆記中的第一張圖片,然後再通過影象處理函式產生縮圖。話不多說,直接上程式碼:

/// <summary>
/// The regular expression for extracting the Src value from an HTML Img tag.
/// </summary>
public const string ImgSrcFormatPattern = @"<img[^>]*?src\s*=\s*[""']?([^'"" >]+?)[ '""][^>]*?>";

/// <summary>
/// Extract the thumbnail image from the html.
/// </summary>
/// <param name="html"></param>
/// <returns></returns>
public static string ExtractThumbnailImageBase64(this string html)
{
    var imageBase64List = html.GetImgSrcBase64FromHtml();
    string result = null;
    if (imageBase64List != null && imageBase64List.Any())
    {
        result = imageBase64List.First();
    }
    return result;
}

/// <summary>
/// 
/// </summary>
/// <param name="html"></param>
/// <returns></returns>
private static IEnumerable<string> GetImgSrcBase64FromHtml(this string html)
{
    var matchesImgSrc = Regex.Matches(html, Constants.ImgSrcFormatPattern,
        RegexOptions.IgnoreCase | RegexOptions.Singleline);
    if (matchesImgSrc.Count == 0)
        return null;
    List<string> result = new List<string>();
    foreach (Match m in matchesImgSrc)
    {
        var href = m.Groups[1].Value;
        var pos = href.IndexOf("base64,", StringComparison.InvariantCultureIgnoreCase);
        pos += 7;
        result.Add(href.Substring(pos, href.Length - pos).Trim());
    }
    return result;
} 

縮圖的產生

縮圖的產生程式碼是在TreeViewEx控制元件中實現的,基本思路其實很簡單,就是根據圖片的長寬進行等比縮小即可。程式碼如下:

private static Image FixedSize(Image imgPhoto, int width, int height, Color clearColor)
{
    int sourceWidth = imgPhoto.Width;
    int sourceHeight = imgPhoto.Height;
    int sourceX = 0;
    int sourceY = 0;
    int destX = 0;
    int destY = 0;

    float nPercent;
    float nPercentW;
    float nPercentH;

    nPercentW = ((float)width / (float)sourceWidth);
    nPercentH = ((float)height / (float)sourceHeight);
    if (nPercentH < nPercentW)
    {
        nPercent = nPercentH;
        destX = Convert.ToInt16((width - (sourceWidth * nPercent)) / 2);
    }
    else
    {
        nPercent = nPercentW;
        destY = Convert.ToInt16((height - (sourceHeight * nPercent)) / 2);
    }

    int destWidth = (int)(sourceWidth * nPercent);
    int destHeight = (int)(sourceHeight * nPercent);

    Bitmap bmPhoto = new Bitmap(width, height,
        PixelFormat.Format24bppRgb);
    bmPhoto.SetResolution(imgPhoto.HorizontalResolution,
        imgPhoto.VerticalResolution);

    Graphics grPhoto = Graphics.FromImage(bmPhoto);
    grPhoto.Clear(clearColor);
    grPhoto.InterpolationMode =
        InterpolationMode.HighQualityBicubic;

    grPhoto.DrawImage(imgPhoto,
        new Rectangle(destX, destY, destWidth, destHeight),
        new Rectangle(sourceX, sourceY, sourceWidth, sourceHeight),
        GraphicsUnit.Pixel);

    grPhoto.Dispose();
    return bmPhoto;
}

讓應用程式在UAC下具有訪問檔案系統資源的許可權

這是之前版本中CloudNotes桌面客戶端更新程式遇到的一個問題。如果使用者將CloudNotes安裝在系統目錄中,更新程式在試圖更新CloudNotes桌面客戶端的版本的時候,就會遇到錯誤,提示無法更新。根本原因是使用者在Windows系統中啟用了使用者帳戶控制(UAC),即使當前登入系統的帳戶是管理員,也並不代表該使用者對系統中的所有資源都具備管理員許可權。在這種情況下,即使是由管理員啟動的更新程式,也無法將下載並解壓好的檔案複製到系統目錄下。

要解決這一問題,就需要在.NET應用程式的Manifest裡指定requestedExecutionLevel,將其指定為requireAdministrator,於是,在執行更新程式的時候,會彈出以下標準對話方塊,讓使用者對應用程式所作出的行為進行確認:

image

此時只需點選“是”按鈕即可完成更新。

更改Manifest其實很簡單,只需要在應用程式專案上,通過Visual Studio的“新增專案”對話方塊,即可新增App.manifest檔案,將檔案中的requestedExecutionLevel改為requireAdministrator即可:

image

有關CloudNotes WebAPI以及桌面客戶端的其它內容,請大家直接上https://github.com/daxnet/CloudNotes站點直接檢視原始碼即可。不懂的地方可以在此留言。

接下來??

我是打算一步步對CloudNotes進行功能和效能完善的,接下來要做的事情還有太多。在每次完成新功能更新時,我都會出部落格文章進行介紹。如果沒有新功能,我也會穿插介紹已有功能的相關技術實現。就目前而言,打算接下來的版本逐步提供以下功能改進:

  • 本地快取和伺服器同步系統 - 目前每次開啟和儲存筆記,都是與伺服器直接相連的,不僅增大伺服器負載,而且使用者體驗也不夠流暢
  • 外掛系統 - 任何感興趣的朋友都可以通過外掛系統,為CloudNotes桌面客戶端編寫外掛,比如,直接將網頁抓取成日誌等
  • 使用者之間的互信和筆記共享服務
  • 文件結構檢視 - 通過分析HTML文件,提供層級的文件結構檢視,方便用CloudNotes進行寫作的使用者

等等等等。。。。。。讓我們一起期待吧。。

相關文章