ABAP Netweaver, Hybris Commerce和SAP 云平臺的登錄認證
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 - 實際值文本和單位顯示控件
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小時內刪除侵權內容。