?WPF 使用 DrawingContext 繪制刻度條
控件名:Ruler
作者:WPFDevelopersOrg
原文鏈接:? ?https://github.com/WPFDevelopersOrg/WPFDevelopers
框架使用大于等于
.NET40
;Visual Studio 2022
;項目使用 MIT 開源許可協議;
定義
Interval
步長、SpanInterval
間隔步長、MiddleMask
中間步長。當步長
/
間隔步長=
需要繪制的小刻度。循環繪制小刻度,判斷當前
j
并取中間步長的模,如果模!=
零就繪制中高線。從
StartValue
開始繪制刻度到EndValue
結束刻度。CurrentGeometry
重新繪制當前刻度的Path
值。CurrentValue
當前值如果發生變化時則去重新CurrentGeometry
。
1) 準備Ruler.cs如下:
using?System;
using?System.Windows;
using?System.Windows.Controls;
using?System.Windows.Media;namespace?WPFDevelopers.Controls
{public?class?Ruler?:?Control{public?static?readonly?DependencyProperty?IntervalProperty?=DependencyProperty.Register("Interval",?typeof(double),?typeof(Ruler),?new?UIPropertyMetadata(30.0));public?static?readonly?DependencyProperty?SpanIntervalProperty?=DependencyProperty.Register("SpanInterval",?typeof(double),?typeof(Ruler),?new?UIPropertyMetadata(5.0));public?static?readonly?DependencyProperty?MiddleMaskProperty?=DependencyProperty.Register("MiddleMask",?typeof(int),?typeof(Ruler),?new?UIPropertyMetadata(2));public?static?readonly?DependencyProperty?CurrentValueProperty?=DependencyProperty.Register("CurrentValue",?typeof(double),?typeof(Ruler),new?UIPropertyMetadata(OnCurrentValueChanged));public?static?readonly?DependencyProperty?StartValueProperty?=DependencyProperty.Register("StartValue",?typeof(double),?typeof(Ruler),?new?UIPropertyMetadata(120.0));public?static?readonly?DependencyProperty?EndValueProperty?=DependencyProperty.Register("EndValue",?typeof(double),?typeof(Ruler),?new?UIPropertyMetadata(240.0));public?static?readonly?DependencyProperty?CurrentGeometryProperty?=DependencyProperty.Register("CurrentGeometry",?typeof(Geometry),?typeof(Ruler),new?PropertyMetadata(Geometry.Parse("M?257,0?257,25?264,49?250,49?257,25")));static?Ruler(){DefaultStyleKeyProperty.OverrideMetadata(typeof(Ruler),?new?FrameworkPropertyMetadata(typeof(Ruler)));}public?Ruler(){Loaded?+=?Ruler_Loaded;}public?double?Interval{get?=>?(double)GetValue(IntervalProperty);set?=>?SetValue(IntervalProperty,?value);}public?double?SpanInterval{get?=>?(double)GetValue(SpanIntervalProperty);set?=>?SetValue(SpanIntervalProperty,?value);}public?int?MiddleMask{get?=>?(int)GetValue(MiddleMaskProperty);set?=>?SetValue(MiddleMaskProperty,?value);}public?double?CurrentValue{get?=>?(double)GetValue(CurrentValueProperty);set{SetValue(CurrentValueProperty,?value);PaintPath();}}public?double?StartValue{get?=>?(double)GetValue(StartValueProperty);set?=>?SetValue(StartValueProperty,?value);}public?double?EndValue{get?=>?(double)GetValue(EndValueProperty);set?=>?SetValue(EndValueProperty,?value);}public?Geometry?CurrentGeometry{get?=>?(Geometry)GetValue(CurrentGeometryProperty);set?=>?SetValue(CurrentGeometryProperty,?value);}private?static?void?OnCurrentValueChanged(DependencyObject?d,?DependencyPropertyChangedEventArgs?e){var?ruler?=?d?as?Ruler;ruler.CurrentValue?=?Convert.ToDouble(e.NewValue);}protected?override?void?OnRender(DrawingContext?drawingContext){RenderOptions.SetEdgeMode(this,?EdgeMode.Aliased);var?nextLineValue?=?0d;var?one_Width?=?ActualWidth?/?((EndValue?-?StartValue)?/?Interval);for?(var?i?=?0;?i?<=?(EndValue?-?StartValue)?/?Interval;?i++){var?numberText?=?DrawingContextHelper.GetFormattedText((StartValue?+?i?*?Interval).ToString(),(Brush)DrawingContextHelper.BrushConverter.ConvertFromString("#FFFFFF"),?FlowDirection.LeftToRight,10);drawingContext.DrawText(numberText,?new?Point(i?*?one_Width?-?8,?0));drawingContext.DrawLine(new?Pen(new?SolidColorBrush(Colors.White),?1),?new?Point(i?*?one_Width,?25),new?Point(i?*?one_Width,?ActualHeight?-?2));var?cnt?=?Interval?/?SpanInterval;for?(var?j?=?1;?j?<=?cnt;?j++)if?(j?%?MiddleMask?==?0)drawingContext.DrawLine(new?Pen(new?SolidColorBrush(Colors.White),?1),new?Point(j?*?(one_Width?/?cnt)?+?nextLineValue,?ActualHeight?-?2),new?Point(j?*?(one_Width?/?cnt)?+?nextLineValue,?ActualHeight?-?10));elsedrawingContext.DrawLine(new?Pen(new?SolidColorBrush(Colors.White),?1),new?Point(j?*?(one_Width?/?cnt)?+?nextLineValue,?ActualHeight?-?2),new?Point(j?*?(one_Width?/?cnt)?+?nextLineValue,?ActualHeight?-?5));nextLineValue?=?i?*?one_Width;}}private?void?Ruler_Loaded(object?sender,?RoutedEventArgs?e){PaintPath();}private?void?PaintPath(){var?d_Value?=?CurrentValue?-?StartValue;var?one_Value?=?ActualWidth?/?(EndValue?-?StartValue);var?x_Point?=?one_Value?*?d_Value?+?((double)Parent.GetValue(ActualWidthProperty)?-?ActualWidth)?/?2d;CurrentGeometry?=Geometry.Parse($"M?{x_Point},0?{x_Point},25?{x_Point?+?7},49?{x_Point?-?7},49?{x_Point},25");}}
}
2) 使用RulerControlExample.xaml.cs如下:
<UserControl?x:Class="WPFDevelopers.Samples.ExampleViews.RulerControlExample"xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"?xmlns:d="http://schemas.microsoft.com/expression/blend/2008"?xmlns:local="clr-namespace:WPFDevelopers.Samples.ExampleViews"xmlns:wpfdev="https://github.com/WPFDevelopersOrg/WPFDevelopers"mc:Ignorable="d"?d:DesignHeight="450"?d:DesignWidth="800"><Grid><Slider?x:Name="PART_Slider"?IsSnapToTickEnabled="True"Value="40"Minimum="10"Maximum="210"/><UniformGrid?Rows="3"><Grid?Background="{StaticResource?CircularDualSolidColorBrush}"?Height="51"?Margin="40,0"><Path?Stroke="{StaticResource?SuccessPressedSolidColorBrush}"?StrokeThickness="1"?Fill="{StaticResource?SuccessPressedSolidColorBrush}"Data="{Binding?ElementName=PART_Ruler,?Path=CurrentGeometry,Mode=TwoWay}"?/><wpfdev:Ruler?x:Name="PART_Ruler"?Margin="40,0"?Interval="20"?StartValue="10"?EndValue="210"CurrentValue="{Binding?ElementName=PART_Slider,Path=Value,Mode=TwoWay}"/></Grid><Grid?Background="{StaticResource?DangerPressedSolidColorBrush}"?Height="51"?Margin="40,0"><Path?Stroke="{StaticResource?SuccessPressedSolidColorBrush}"?StrokeThickness="1"?Fill="{StaticResource?SuccessPressedSolidColorBrush}"Data="{Binding?ElementName=PART_Ruler1,?Path=CurrentGeometry,Mode=TwoWay}"?/><wpfdev:Ruler?x:Name="PART_Ruler1"?Margin="40,0"?Interval="20"?StartValue="10"?EndValue="210"CurrentValue="{Binding?ElementName=PART_Slider,Path=Value,Mode=TwoWay}"/></Grid><Grid?Background="{StaticResource?WarningPressedSolidColorBrush}"?Height="51"?Margin="40,0"><Path?Stroke="{StaticResource?SuccessPressedSolidColorBrush}"?StrokeThickness="1"?Fill="{StaticResource?SuccessPressedSolidColorBrush}"Data="{Binding?ElementName=PART_Ruler2,?Path=CurrentGeometry,Mode=TwoWay}"?/><wpfdev:Ruler?x:Name="PART_Ruler2"?Margin="40,0"?Interval="20"?StartValue="10"?EndValue="210"CurrentValue="{Binding?ElementName=PART_Slider,Path=Value,Mode=TwoWay}"/></Grid></UniformGrid></Grid>
</UserControl>
?鳴謝 - 帥嘉欣
Github|RulerControlExample[1]
碼云|RulerControlExample[2]
參考資料
[1]
Github|RulerControlExample: https://github.com/WPFDevelopersOrg/WPFDevelopers/blob/master/src/WPFDevelopers.Samples/ExampleViews/RulerControlExample.xaml
[2]碼云|RulerControlExample: https://gitee.com/WPFDevelopersOrg/WPFDevelopers/blob/master/src/WPFDevelopers.Samples/ExampleViews/RulerControlExample.xaml