WPF ruler
Copy from WPFDeveloper
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows.Controls.Primitives; using System.Windows.Media; using System.Windows; using System.Globalization; namespace WpfApp142 { public class ScaleBase : RangeBase//Control { //public static readonly DependencyProperty MaximumProperty = // DependencyProperty.Register("Maximum", typeof(double), typeof(ScaleBase), new UIPropertyMetadata(0.0, OnMaximumChanged)); //private static void OnMaximumChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) //{ // var ctrl = (ScaleBase)d; // ctrl.OnMaximumChanged((double)e.OldValue, (double)e.NewValue); //} //protected virtual void OnMaximumChanged(double oldValue, double newValue) //{ //} //public static readonly DependencyProperty MinimumProperty = // DependencyProperty.Register("Minimum", typeof(double), typeof(ScaleBase), new UIPropertyMetadata(0.0, OnMinimumChanged)); //private static void OnMinimumChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) //{ // var ctrl = (ScaleBase)d; // ctrl.OnMinimumChanged((double)e.OldValue, (double)e.NewValue); //} //protected virtual void OnMinimumChanged(double oldValue, double newValue) //{ //} //public static readonly DependencyProperty ValueProperty = // DependencyProperty.Register("Value", typeof(double), typeof(ScaleBase), new UIPropertyMetadata(0.0, OnValueChanged)); //private static void OnValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) //{ // var ctrl = (ScaleBase)d; // ctrl.OnValueChanged((double)e.OldValue, (double)e.NewValue); //} //protected virtual void OnValueChanged(double oldValue, double newValue) //{ //} public static readonly DependencyProperty IntervalProperty = DependencyProperty.Register("Interval", typeof(double), typeof(ScaleBase), new UIPropertyMetadata(0.0, OnIntervalChanged)); private static void OnIntervalChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { var ctrl = (ScaleBase)d; ctrl.OnIntervalChanged((double)e.OldValue, (double)e.NewValue); } protected virtual void OnIntervalChanged(double oldValue, double newValue) { } public static readonly DependencyProperty GeometryProperty = DependencyProperty.Register("Geometry", typeof(Geometry), typeof(ScaleBase), new PropertyMetadata(null, OnGeometryChanged)); private static void OnGeometryChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { var ctrl = (ScaleBase)d; ctrl.OnGeometryChanged((Geometry)e.OldValue, (Geometry)e.NewValue); } protected virtual void OnGeometryChanged(Geometry oldValue, Geometry newValue) { } //public double Maximum //{ // get => (double)GetValue(MaximumProperty); // set => SetValue(MaximumProperty, value); //} //public double Minimum //{ // get => (double)GetValue(MinimumProperty); // set => SetValue(MinimumProperty, value); //} //public double Value //{ // get => (double)GetValue(ValueProperty); // set => SetValue(ValueProperty, value); //} public double Interval { get => (double)GetValue(IntervalProperty); set => SetValue(IntervalProperty, value); } public Geometry Geometry { get => (Geometry)GetValue(GeometryProperty); set => SetValue(GeometryProperty, value); } } public static class DrawingContextHelper { public static BrushConverter BrushConverter = new BrushConverter(); public static FormattedText GetFormattedText(string text, Brush color = null, FlowDirection flowDirection = FlowDirection.RightToLeft, double textSize = 12.0D, FontWeight fontWeight = default) { if (fontWeight == default) { fontWeight = FontWeights.Thin; } return new FormattedText( text, CultureInfo.CurrentCulture, flowDirection, new Typeface( new FontFamily(), FontStyles.Normal, fontWeight, FontStretches.Normal), textSize, color == null ? null : color,1.25) { MaxLineCount = 1, TextAlignment = TextAlignment.Justify, Trimming = TextTrimming.CharacterEllipsis }; } } } using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows.Media; using System.Windows; namespace WpfApp142 { public class Ruler : ScaleBase { public static readonly DependencyProperty SpanIntervalProperty = DependencyProperty.Register("SpanInterval", typeof(double), typeof(Ruler), new UIPropertyMetadata(5.0, OnSpanIntervalChanged)); private static void OnSpanIntervalChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { var ctrl = d as Ruler; ctrl?.InvalidateVisual(); } public static readonly DependencyProperty MiddleMaskProperty = DependencyProperty.Register("MiddleMask", typeof(int), typeof(Ruler), new UIPropertyMetadata(2, OnMiddleMaskChanged)); private static void OnMiddleMaskChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { var ctrl = d as Ruler; ctrl?.InvalidateVisual(); } static Ruler() { DefaultStyleKeyProperty.OverrideMetadata(typeof(Ruler), new FrameworkPropertyMetadata(typeof(Ruler))); } public Ruler() { SetValue(MaximumProperty, 240.0); SetValue(MinimumProperty, 120.0); SetValue(IntervalProperty, 30.0); SetValue(GeometryProperty, Geometry.Parse(@"M 257,0 257,25 264,49 250,49 257,25")); Loaded += Ruler_Loaded; } protected override void OnValueChanged(double oldValue, double newValue) { PaintPath(); } protected override void OnMaximumChanged(double oldValue, double newValue) { InvalidateVisual(); } protected override void OnMinimumChanged(double oldValue, double newValue) { InvalidateVisual(); } protected override void OnIntervalChanged(double oldValue, double newValue) { InvalidateVisual(); } public double SpanInterval { get => (double)GetValue(SpanIntervalProperty); set => SetValue(SpanIntervalProperty, value); } public int MiddleMask { get => (int)GetValue(MiddleMaskProperty); set => SetValue(MiddleMaskProperty, value); } protected override void OnRender(DrawingContext drawingContext) { RenderOptions.SetEdgeMode(this, EdgeMode.Aliased); var nextLineValue = 0d; var one_Width = ActualWidth / ((Maximum - Minimum) / Interval); for (var i = 0; i <= (Maximum - Minimum) / Interval; i++) { var numberText = DrawingContextHelper.GetFormattedText((Minimum + 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)); else 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 - 5)); nextLineValue = i * one_Width; } } void Ruler_Loaded(object sender, RoutedEventArgs e) { PaintPath(); } void PaintPath() { if (Parent == null) return; var d_Value = Value - Minimum; var one_Value = ActualWidth / (Maximum - Minimum); var x_Point = one_Value * d_Value + ((double)Parent.GetValue(ActualWidthProperty) - ActualWidth) / 2d; Geometry = Geometry.Parse($"M {x_Point},0 {x_Point},25 {x_Point + 7},49 {x_Point - 7},49 {x_Point},25"); } } } //xaml <Window x:Class="WpfApp142.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:local="clr-namespace:WpfApp142" mc:Ignorable="d" Title="MainWindow" Height="450" Width="800"> <Grid> <Slider x:Name="PART_Slider" IsSnapToTickEnabled="True" Maximum="210" Minimum="10" Value="40" /> <TextBlock Margin="0,20" HorizontalAlignment="Center" IsHitTestVisible="False" Text="{Binding ElementName=PART_Ruler, Path=Value}" /> <UniformGrid Rows="3"> <Grid Height="51" Margin="40,0" Background="Red"> <Path Data="{Binding ElementName=PART_Ruler, Path=Geometry, Mode=TwoWay}" Fill="Yellow" Stroke="Blue" StrokeThickness="1" /> <local:Ruler x:Name="PART_Ruler" Margin="40,0" Interval="20" Maximum="210" Minimum="10" Value="{Binding ElementName=PART_Slider, Path=Value, Mode=TwoWay}" /> </Grid> <Grid Height="51" Margin="40,0" Background="Green"> <Path Data="{Binding ElementName=PART_Ruler1, Path=Geometry, Mode=TwoWay}" Fill="Yellow" Stroke="Blue" StrokeThickness="1" /> <local:Ruler x:Name="PART_Ruler1" Margin="40,0" Interval="20" Maximum="210" Minimum="10" Value="{Binding ElementName=PART_Slider, Path=Value, Mode=TwoWay}" /> </Grid> <Grid Height="51" Margin="40,0" Background="Black"> <Path Data="{Binding ElementName=PART_Ruler2, Path=Geometry, Mode=TwoWay}" Fill="Yellow" Stroke="Blue" StrokeThickness="1" /> <local:Ruler x:Name="PART_Ruler2" Margin="40,0" Interval="20" Maximum="210" Minimum="10" Value="{Binding ElementName=PART_Slider, Path=Value, Mode=TwoWay}" /> </Grid> </UniformGrid> </Grid> </Window>
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
2020-01-19 C# aggregateexception flatten innerexceptions