WPF製作帶明細的環形圖表

来源:http://www.cnblogs.com/tsliwei/archive/2017/07/12/7155616.html
-Advertisement-
Play Games

效果 明細用Popup實現的,錄gif時,Popup顯示不出來,不知道為什麼,所以靜態圖湊合看吧 大體思路 圖表使用Arc+Popup實現 圖表分為兩部分,一是環形部分,一是標註的明細部分. 環形部分使用Arc圖形表示.需要註意這個Arc是Blend里的圖形.用Blend建項目的話可以直接用,使用V ...


效果

明細用Popup實現的,錄gif時,Popup顯示不出來,不知道為什麼,所以靜態圖湊合看吧

大體思路

圖表使用Arc+Popup實現

圖表分為兩部分,一是環形部分,一是標註的明細部分.

環形部分使用Arc圖形表示.需要註意這個Arc是Blend里的圖形.用Blend建項目的話可以直接用,使用VS建項目需要添加引用 Microsoft.Expression.Drawing 在引用管理器=>程式集=>擴展 下(前提是已經安裝了Blend)

明細部分使用Popup控制項,IsOpen屬性綁定到Arc的IsMouseOver,也就是滑鼠進入圓弧的時候,Popup就打開顯示.

Popup內部一個橢圓控制項當作背景,一個文字顯示,一個折線虛線化當作指針

然後就是把Popup定位到對應圓弧合適的位置去顯示(這裡取的是圓弧的中間)

比較抱歉的是樣式比較醜陋,忽略吧,重點看定位.

圓弧部分

Arc有兩個重要的屬性:StartAngle起始角度和EndAngle終結角度.這兩個屬性決定了圓弧占所在圓環的比例.

每一個數據項就對應一個圓弧,把所有圓弧都放到一個容器里,首尾相連

數據項的總和為100,那麼所有圓弧也就組成一個完整的圓環.

Popup明細部分

明細部分分為四種,見圖

橢圓

從圖可知,作為背景的橢圓分為兩種情況,小於180度,橢圓靠容器的右邊對齊,大於180度,靠容器的左邊對齊

也就是代碼的這部分:

Ellipse ell = new Ellipse() { Fill = brush };
//中間點角度小於180 明細靠右顯示 否則靠左顯示
Grid detailGrid = new Grid() { Width = _popupHeight, HorizontalAlignment = HorizontalAlignment.Right };
if (middleAngle > 180)
{
    detailGrid.HorizontalAlignment = HorizontalAlignment.Left;
}
折線

折線是分為四種,每一個角度區間都對應一種

private Polyline GetPopupPolyline(double middleAngle)
{
    Polyline pLine = new Polyline() { Stroke = new SolidColorBrush(Color.FromRgb(0, 0, 0)), StrokeDashArray = new DoubleCollection(new double[] { 5, 2 }) };
    double x1 = 0, y1 = 0;
    double x2 = 0, y2 = 0;
    double x3 = 0, y3 = 0;
    if (middleAngle > 0 && middleAngle <= 90)
    {
        x1 = 0; y1 = _popupHeight;
        x2 = _popupWidth / 2; y2 = _popupHeight;
        x3 = _popupWidth * 3 / 4; y3 = _popupHeight / 2;
    }
    if (middleAngle > 90 && middleAngle <= 180)
    {
        x1 = 0; y1 = 0;
        x2 = _popupWidth / 2; y2 = 0;
        x3 = _popupWidth * 3 / 4; y3 = _popupHeight / 2;
    }
    if (middleAngle > 180 && middleAngle <= 270)
    {
        x1 = _popupWidth; y1 = 0;
        x2 = _popupWidth / 2; y2 = 0;
        x3 = _popupWidth / 4; y3 = _popupHeight / 2;
    }
    if (middleAngle > 270 && middleAngle <= 360)
    {
        x1 = _popupWidth; y1 = _popupHeight;
        x2 = _popupWidth / 2; y2 = _popupHeight;
        x3 = _popupWidth / 4; y3 = _popupHeight / 2;
    }
    pLine.Points.Add(new Point(x1, y1));
    pLine.Points.Add(new Point(x2, y2));
    pLine.Points.Add(new Point(x3, y3));
    return pLine;
}
Popup的定位

首先以0-90度為例,說明一些基本的東西,見圖

首先Popup預設的位置,都是在它容器的左下方的,Popup的左上角和容器的左下角重合.

現在要做的是Popup標記為紅點的位置,和圓環上標記為紅點的位置重合.

先來回顧一下小時候學過的公式:

1.直角三角形 a=r*sinA

2.勾股定理 c^2=a^2+b^2 b=Sqrt(c^2-a^2)

上圖的直角三角形,角A的對邊為a,臨邊為b,斜邊為c.顯然c邊於圓的半徑r相等.註意:因為圓弧是有厚度的,所以取r的時候要減去二分之一的圓弧厚度.

角A是可以通過90度減去圓弧的對應的角度求出來的,也就是sinA的值已知了,那麼就可以求出a和b的長度,然後就可以去移動Popup了

一.0-90度

X軸:1.向右移動二分之一個容器的width 2.向右移動一個b的距離

Y軸:1.向上移動二分之一個容器的height 2.向上移動一個Popup的height 3.向上移動一個a的距離

二.90-180度

X軸:1.向右移動二分之一個容器的width 2.向右移動一個a的距離

Y軸:1.上移二分之一個圓弧的Thickness,以保證標記的起點在圓弧的中央 2.上移一個(r-b)的距離

三.180-270度

X軸:1.向左移動一個b的距離

Y軸:1.上移二分之一個圓弧的Thickness,以保證標記的起點在圓弧的中央 2.上移一個(r-a)的距離

四.270-360度

X軸:1.向左移動一個a的距離

Y軸:1.向上移動二分之一個容器的height 2.向上移動一個Popup的height 3.向上移動一個b的距離

代碼部分

private Popup GetPopup(double middleAngle)
{
    /*
     * 生成popup
     * 設置popup的offset 讓標記線的起點 對應到圓弧的中間點
     */
    Popup popup = new Popup() { Width = _popupWidth, Height = _popupHeight, AllowsTransparency = true, IsHitTestVisible = false };
    //直角三角形 a=r*sinA 勾股定理 c^2=a^2+b^2 b=Sqrt(c^2-a^2)
    double r = _chartWidth / 2 - _arcThickness / 2;
    double offsetX = 0, offsetY = 0;
    if (middleAngle > 0 && middleAngle <= 90)
    {
        double sinA = Math.Sin(Math.PI * (90 - middleAngle) / 180);
        double a = r * sinA;
        double c = r;
        double b = Math.Sqrt(c * c - a * a);
        offsetX = _chartWidth / 2 + b;
        offsetY = -(_chartWidth / 2 + _popupHeight + a);
    }
    if (middleAngle > 90 && middleAngle <= 180)
    {
        double sinA = Math.Sin(Math.PI * (180 - middleAngle) / 180);
        double a = r * sinA;
        double c = r;
        double b = Math.Sqrt(c * c - a * a);
        offsetX = _chartWidth / 2 + a;
        offsetY = -(_arcThickness / 2 + (r - b));
    }
    if (middleAngle > 180 && middleAngle <= 270)
    {
        double sinA = Math.Sin(Math.PI * (270 - middleAngle) / 180);
        double a = r * sinA;
        double c = r;
        double b = Math.Sqrt(c * c - a * a);
        offsetX = -b;
        offsetY = -(_arcThickness / 2 + (r - a));
    }
    if (middleAngle > 270 && middleAngle <= 360)
    {
        double sinA = Math.Sin(Math.PI * (360 - middleAngle) / 180);
        double a = r * sinA;
        double c = r;
        double b = Math.Sqrt(c * c - a * a);
        offsetX = -a;
        offsetY = -(_chartWidth / 2 + _popupHeight + b);
    }
    popup.HorizontalOffset = offsetX;
    popup.VerticalOffset = offsetY;

    return popup;
}

差不多主要的就是這些了.到這.畫圖有點累.

源碼下載:ArcChart.zip


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 背水一戰 Windows 10 之 控制項(集合類 - ListViewBase): ListView, GridView ...
  • 因為這個解法有點複雜,因此單獨開一貼介紹。《演算法(第四版)》中的題目是這樣的:1.3.49棧與隊列。用有限個棧實現一個隊列,保證每個隊列操作(在最壞情況下)都只需要常數次的棧操作。那麼這裡就使用六個棧來解決這個問題。這個演算法來自於這篇論文。原文里用的是 Pure Lisp,不過語法很簡單,還是很容易... ...
  • 一、摘要 1.1、為什麼叫本次的分享課叫《修煉手冊》? 阿笨希望本次的分享課中涉及覆蓋的一些小技巧、小技能給您帶來一些幫助。希望您在日後工作中把它作為一本實際技能手冊進行儲備,以備不時之需,一旦當手頭遇到與Dapper修煉手冊中相似用法的地方和場景,可以直接拿來進行翻閱並靈活的運用到項目中。最後阿笨 ...
  • 1 文件夾/文件 檢查、新增、複製、移動、刪除,2 文件讀寫,記錄文本日誌/讀取配置文件3 三種序列化器4 xml和json1.文件夾/文件 檢查、新增、複製、移動、刪除,2 文件讀寫,記錄文本日誌/讀取配置文件 日誌方法 3 三種序列化器 在序列化類之前先用 [Serializable] //必須 ...
  • 本文屬於原創,轉載請註明出處,謝謝! 一、開發環境 操作系統:Windows 10 開發環境:VS2015 編程語言:C# IIS版本:10.0.0.0 二、添加WCF服務、Internet Information Services(IIS) 1、進入“控制面板”,打開“程式和功能”,點擊左上角的“ ...
  • 生成解決方案 F6,生成項目Shift+F6 調試執行F5,終止調試執行Shift+F5 執行調試Ctrl+F5 查找下一個F3,查找上一個Shift+F3 附加到進程Ctrl+Alt+P,逐過程F10,逐語句執行F11 切換斷點F9(添加或取消斷點) 運行至游標處Ctrl+F10 跳出當前方法Sh ...
  • 要求大概就是,選定一段時間周期,勾選周日至周六,把時間周期符合要求的日期返回,例如:勾選了周日,就把時間周期內所有是周日的日期返回,勾選了周日和周五,就把時間周期內所有周五和周日的日期返回。重覆間隔是以時間周期內的星期為單位進行跳選,間隔為0時,每個星期都遍歷,間隔為1時,遍歷完第一周,跳到第三周再 ...
  • 看圖說話。 需求:插入兩張表,上面的表單是第一張表的內容,下麵的兩個表格是第二張詳情表的內容,跟第一張表的id關聯 第二張表有一個列是需要用戶手動填寫添加的。 國際慣例,上代碼 後面的列我就不貼出來了 後臺的代碼就不寫了,通過ajax傳到後臺,request取出來,按照插入資料庫的格式整理好即可,如 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...