New UWP Community Toolkit 6.0 - RadialGauge

      網友投稿 672 2025-04-01

      概述


      New UWP Community Toolkit? V6.0 的版本發布日志中提到了?RadialGauge 的調整,本篇我們結合代碼詳細講解? RadialGauge?的實現。

      RadialGauge 是一種徑向儀表盤控件,使用圓盤面上的指針來顯示一定范圍的值,這種顯示和交互方式,讓數據可視化的表現力和吸引力都有很大提高。在實際應用中也有很廣泛的使用,如時鐘顯示,數據展示,儀表盤模擬等等。我們來看一下官方的介紹和官網示例中的展示:

      Source:?https://github.com/Microsoft/UWPCommunityToolkit/tree/master/Microsoft.Toolkit.Uwp.UI.Controls/RadialGauge

      Doc:?https://docs.microsoft.com/zh-cn/windows/uwpcommunitytoolkit/controls/radialgauge

      Namespace:?Microsoft.Toolkit.Uwp.UI.Controls;?Nuget:?Microsoft.Toolkit.Uwp.UI.Controls;

      開發過程

      代碼分析

      先來看看?RadialGauge 的結構組成:

      RadialGauge.cs -?RadialGauge 的控件定義和事件處理類

      RadialGauge.xaml -?RadialGauge 的樣式文件

      1.?RadialGauge.xaml

      RadialGauge 控件的樣式文件,結合上面官方示例的顯示圖,我們看 Template 部分;主要由以下幾個部分組成:

      PART_Container - 底層容器,包含了下面三個控件部分

      PART_Scale - 比例尺控件

      PART_Trail - 儀表盤實際值顯示控件

      Value and Unit - 實際值文本和單位顯示控件

      ???? ???? ???? ???????? ???????????? ???????????????? ???????????????????? ???????????????????????? ???????????????????????? ???????????????????????? ???????????????????????? ???????????????????????? ???????????????????????? ???????????????????????????? ???????????????????????????? ???????????????????????? ???????????????????? ???????????????? ???????????? ???????? ????

      New UWP Community Toolkit 6.0 - RadialGauge

      2.?RadialGauge.cs

      我們先看看?RadialGauge 類的組成:

      從上面第一張圖中,我們可以看到?RadialGauge 注冊了很多依賴屬性,不一一列舉了,大致分為幾個類型:取值和角度屬性,顯示畫刷屬性,單位相關屬性;屬性也對應了修改時的回調事件,下面我們找出幾個重點的事件處理方法來講解:

      ①?OnValueChanged(d)

      在數值變化后,觸發?OnValueChanged(d) 事件的方法;首先根據設置的取舍值,矯正當前的 Value,計算出對應的角度;給儀表盤的指針賦值,讓指針指向當前角度;然后是給顯示當前值區間的弧形賦值,如果當前角度值為 360,則整個填充儀表盤,否則根據角度計算出填充的區域,給 ArcSegment,PathFigure,PathGeometry 賦值;最后給儀表盤的數值文本控件賦值;

      OnScaleChanged(d) 在刻度修改時觸發,本質上講,數值修改和刻度修改是相通的,所以處理方式也類似,這里不做贅述;

      private?static?void?OnValueChanged(DependencyObject?d) { ????RadialGauge?radialGauge?=?(RadialGauge)d;????if?(!double.IsNaN(radialGauge.Value)) ????{????????if?(radialGauge.StepSize?!=?0) ????????{ ????????????radialGauge.Value?=?radialGauge.RoundToMultiple(radialGauge.Value,?radialGauge.StepSize); ????????}????????var?middleOfScale?=?100?-?radialGauge.ScalePadding?-?(radialGauge.ScaleWidth?/?2);????????var?valueText?=?radialGauge.GetTemplateChild(ValueTextPartName)?as?TextBlock; ????????radialGauge.ValueAngle?=?radialGauge.ValueToAngle(radialGauge.Value);????????//?Needle ????????if?(radialGauge._needle?!=?null) ????????{ ????????????radialGauge._needle.RotationAngleInDegrees?=?(float)radialGauge.ValueAngle; ????????}????????//?Trail ????????var?trail?=?radialGauge.GetTemplateChild(TrailPartName)?as?Path;????????if?(trail?!=?null) ????????{????????????if?(radialGauge.ValueAngle?>?radialGauge.NormalizedMinAngle) ????????????{ ????????????????trail.Visibility?=?Visibility.Visible;????????????????if?(radialGauge.ValueAngle?-?radialGauge.NormalizedMinAngle?==?360) ????????????????{????????????????????//?Draw?full?circle. ????????????????????var?eg?=?new?EllipseGeometry(); ????????????????????eg.Center?=?new?Point(100,?100); ????????????????????eg.RadiusX?=?100?-?radialGauge.ScalePadding?-?(radialGauge.ScaleWidth?/?2); ????????????????????eg.RadiusY?=?eg.RadiusX; ????????????????????trail.Data?=?eg; ????????????????}????????????????else ????????????????{????????????????????//?Draw?arc. ????????????????????var?pg?=?new?PathGeometry();????????????????????var?pf?=?new?PathFigure(); ????????????????????pf.IsClosed?=?false; ????????????????????pf.StartPoint?=?radialGauge.ScalePoint(radialGauge.NormalizedMinAngle,?middleOfScale);????????????????????var?seg?=?new?ArcSegment(); ????????????????????seg.SweepDirection?=?SweepDirection.Clockwise; ????????????????????seg.IsLargeArc?=?radialGauge.ValueAngle?>?(180?+?radialGauge.NormalizedMinAngle); ????????????????????seg.Size?=?new?Size(middleOfScale,?middleOfScale); ????????????????????seg.Point?=?radialGauge.ScalePoint(Math.Min(radialGauge.ValueAngle,?radialGauge.NormalizedMaxAngle),?middleOfScale);??//?On?overflow,?stop?trail?at?MaxAngle.????????????????????pf.Segments.Add(seg); ????????????????????pg.Figures.Add(pf); ????????????????????trail.Data?=?pg; ????????????????} ????????????}????????????else ????????????{ ????????????????trail.Visibility?=?Visibility.Collapsed; ????????????} ????????}????????//?Value?Text ????????if?(valueText?!=?null) ????????{ ????????????valueText.Text?=?radialGauge.Value.ToString(radialGauge.ValueStringFormat); ????????} ????} }

      ②?OnFaceChanged(d)

      任何外觀有變化,或刻度值有變化時就會觸發,控件整體的 UI 重繪;首先是 Ticks 重繪,然后是 Scale 重繪,后面是 Needle 的重繪,可以看到三種重繪的實現都很類似;最后是執行處理數值變化的方法;

      private?static?void?OnFaceChanged(DependencyObject?d) { ????RadialGauge?radialGauge?=?(RadialGauge)d;????var?container?=?radialGauge.GetTemplateChild(ContainerPartName)?as?Grid;????if?(container?==?null?||?DesignTimeHelpers.IsRunningInLegacyDesignerMode) ????{????????//?Bad?template. ????????return; ????} ????radialGauge._root?=?container.GetVisual(); ????radialGauge._root.Children.RemoveAll(); ????radialGauge._compositor?=?radialGauge._root.Compositor;????//?Ticks.????SpriteVisual?tick;????for?(double?i?=?radialGauge.Minimum;?i?<=?radialGauge.Maximum;?i?+=?radialGauge.TickSpacing) ????{ ????????tick?=?radialGauge._compositor.CreateSpriteVisual(); ????????tick.Size?=?new?Vector2((float)radialGauge.TickWidth,?(float)radialGauge.TickLength); ????????tick.Brush?=?radialGauge._compositor.CreateColorBrush(radialGauge.TickBrush.Color); ????????tick.Offset?=?new?Vector3(100?-?((float)radialGauge.TickWidth?/?2),?0.0f,?0); ????????tick.CenterPoint?=?new?Vector3((float)radialGauge.TickWidth?/?2,?100.0f,?0); ????????tick.RotationAngleInDegrees?=?(float)radialGauge.ValueToAngle(i); ????????radialGauge._root.Children.InsertAtTop(tick); ????}????//?Scale?Ticks. ????for?(double?i?=?radialGauge.Minimum;?i?<=?radialGauge.Maximum;?i?+=?radialGauge.TickSpacing) ????{ ????????tick?=?radialGauge._compositor.CreateSpriteVisual(); ????????tick.Size?=?new?Vector2((float)radialGauge.ScaleTickWidth,?(float)radialGauge.ScaleWidth); ????????tick.Brush?=?radialGauge._compositor.CreateColorBrush(radialGauge.ScaleTickBrush.Color); ????????tick.Offset?=?new?Vector3(100?-?((float)radialGauge.ScaleTickWidth?/?2),?(float)radialGauge.ScalePadding,?0); ????????tick.CenterPoint?=?new?Vector3((float)radialGauge.ScaleTickWidth?/?2,?100?-?(float)radialGauge.ScalePadding,?0); ????????tick.RotationAngleInDegrees?=?(float)radialGauge.ValueToAngle(i); ????????radialGauge._root.Children.InsertAtTop(tick); ????}????//?Needle. ????radialGauge._needle?=?radialGauge._compositor.CreateSpriteVisual(); ????radialGauge._needle.Size?=?new?Vector2((float)radialGauge.NeedleWidth,?(float)radialGauge.NeedleLength); ????radialGauge._needle.Brush?=?radialGauge._compositor.CreateColorBrush(radialGauge.NeedleBrush.Color); ????radialGauge._needle.CenterPoint?=?new?Vector3((float)radialGauge.NeedleWidth?/?2,?(float)radialGauge.NeedleLength,?0); ????radialGauge._needle.Offset?=?new?Vector3(100?-?((float)radialGauge.NeedleWidth?/?2),?100?-?(float)radialGauge.NeedleLength,?0); ????radialGauge._root.Children.InsertAtTop(radialGauge._needle); ????OnValueChanged(radialGauge); }

      下面來看一下?RadialGauge 的鼠標點擊和觸摸手勢交互事件處理方法,主要處理邏輯在?SetGaugeValueFromPoint(point) 方法中:

      首先計算出當前點擊或觸摸點相對比儀表盤圓心的坐標,根據坐標計算出角度;再根據最大角度和最小角度的值,計算出可變化的實際區間;最后用當前角度與最小角度的差值,與實際區間做一個比例換算,得到當前角度對應在儀表盤里的數值;

      private?void?SetGaugeValueFromPoint(Point?p) {????var?pt?=?new?Point(p.X?-?(ActualWidth?/?2),?-p.Y?+?(ActualHeight?/?2));????var?angle?=?Math.Atan2(pt.X,?pt.Y)?/?Degrees2Radians;????var?divider?=?Mod(NormalizedMaxAngle?-?NormalizedMinAngle,?360);????if?(divider?==?0) ????{ ????????divider?=?360; ????}????var?value?=?Minimum?+?((Maximum?-?Minimum)?*?Mod(angle?-?NormalizedMinAngle,?360)?/?divider);????if?(value??Maximum) ????{????????//?Ignore?positions?outside?the?scale?angle. ????????return; ????} ????Value?=?value; }

      另外,RadialGauge 控件還支持鍵盤快捷鍵操作,當按下 Ctrl 鍵時,數值變化的幅度是正常變化的 5 倍;而當按下 Left 或 Right 鍵時,數值會變為最小值或最大值。

      調用示例

      我們給 RadialGauge 控件設置的范圍是 0~180,當前值是 116;最小角度是 210,最大角度是 150;以及每個部分的顏色設置,可以從示例運行圖中看出:

      總結

      到這里我們就把 UWP Community Toolkit 中的?RadialGauge 控件的源代碼實現過程和簡單的調用示例講解完成了,希望能對大家更好的理解和使用這個控件有所幫助。歡迎大家多多交流,謝謝!

      最后,再跟大家安利一下 UWPCommunityToolkit 的官方微博:https://weibo.com/u/6506046490,?大家可以通過微博關注最新動態。

      計算

      版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。

      版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。

      上一篇:excel2007利用公式求和的方法
      下一篇:[Atlas200DK] 制卡腳本解讀
      相關文章
      亚洲人成人无码网www电影首页| 亚洲自偷自偷在线成人网站传媒| 亚洲精品无码久久久久久| 亚洲美女视频网址| 久久精品亚洲综合专区| 国产l精品国产亚洲区在线观看| 国产亚洲一区二区手机在线观看 | 亚洲xxxx18| 中文字幕亚洲综合小综合在线| 国产成人精品日本亚洲网址| 日本亚洲色大成网站www久久| 中文字幕在线观看亚洲日韩| 亚洲妇女熟BBW| 亚洲精品国产第一综合99久久| 亚洲AV无码成人精品区狼人影院| 亚洲AV无码一区二区三区牲色| 亚洲AV色无码乱码在线观看| mm1313亚洲国产精品美女| 亚洲高清无码专区视频| 国产成人综合亚洲亚洲国产第一页| 丁香五月亚洲综合深深爱| 国产亚洲高清不卡在线观看| 久久夜色精品国产嚕嚕亚洲av| 亚洲久本草在线中文字幕| 久久亚洲AV成人出白浆无码国产 | 最新亚洲人成无码网站| 亚洲精品无码AV中文字幕电影网站| 亚洲伦乱亚洲h视频| 在线亚洲97se亚洲综合在线| 亚洲成AV人片在线观看| 亚洲伊人久久大香线蕉苏妲己| 亚洲第一网站免费视频| 亚洲中文字幕无码av在线| 亚洲精品宾馆在线精品酒店| 亚洲国产精品自产在线播放| 国产亚洲AV无码AV男人的天堂| 亚洲AV人人澡人人爽人人夜夜 | 久久综合亚洲色hezyo| 亚洲人成电影在线播放| 亚洲国产精品嫩草影院在线观看| 久久综合图区亚洲综合图区|