WPF调色盘(4):自定义控件

前面的三篇文章,已经把核心部分的代码贴出来了。

有些同学可能还不太理解自动以控件怎么写,我这就把完整的代码贴出来。

这里需要注意一下几点:

1. 依赖属性有点多,可以根据自己的需要取舍;

2. AccordingPixel()方法,是将第1篇文章和第2篇文章的代码融合而来。因为在一个类中,这样可以减少创建Color对象的过程。实测提升效率40%。半径越大,提升越大。

3. HSB模式的代码,是我从网上抄的,不是我写的。我只是做了个整合。

using System;
using System.Runtime.InteropServices;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;

namespace RayLib.WPF
{
    /// <summary>
    /// 绘制ColorWheel的方法
    /// </summary>
    public enum ColorWheelMethod
    {
        HSB, Pixel
    }



    /// <summary>
    /// 颜色选择器
    /// </summary>
    [TemplatePart(Name = elementName, Type = typeof(Image))]
    public class ColorPicker : Control
    {
        const string elementName = "Part_Image";
        private Image? image;

        #region 依赖项属性

        internal static readonly DependencyProperty SourceProperty = DependencyProperty.Register(
            "Source", typeof(WriteableBitmap), typeof(ColorPicker), new PropertyMetadata(default));


        internal static readonly DependencyProperty RedPorperty = DependencyProperty.Register(
            "Red", typeof(byte), typeof(ColorPicker), new PropertyMetadata(byte.MinValue, OnValueChanged));


        internal static readonly DependencyProperty GreenProperty = DependencyProperty.Register(
            "Green", typeof(byte), typeof(ColorPicker), new PropertyMetadata(byte.MinValue, OnValueChanged));


        internal static readonly DependencyProperty BlueProperty = DependencyProperty.Register(
            "Blue", typeof(byte), typeof(ColorPicker), new PropertyMetadata(byte.MinValue, OnValueChanged));


        internal static readonly DependencyProperty AlphaProperty = DependencyProperty.Register(
            "Alpha", typeof(byte), typeof(ColorPicker), new PropertyMetadata(byte.MaxValue, OnValueChanged));


        internal static readonly DependencyProperty SelectColorProperty = DependencyProperty.Register(
            "SelectColor", typeof(Color), typeof(ColorPicker), new PropertyMetadata(Colors.LimeGreen));


        internal static readonly DependencyProperty SelectBrushProperty = DependencyProperty.Register(
            "SelectBrush", typeof(SolidColorBrush), typeof(ColorPicker), new PropertyMetadata(Brushes.LimeGreen));


        internal static readonly DependencyProperty ColorStringProperty = DependencyProperty.Register(
            "ColorString", typeof(string), typeof(ColorPicker), new PropertyMetadata(string.Empty));


        internal static readonly DependencyProperty HexStringProperty = DependencyProperty.Register(
            "HexString", typeof(string), typeof(ColorPicker), new PropertyMetadata(string.Empty));


        internal static readonly DependencyProperty MethodProperty = DependencyProperty.Register(
            "Method", typeof(ColorWheelMethod), typeof(ColorPicker), new PropertyMetadata(ColorWheelMethod.Pixel, OnMethodChange));


        internal static readonly DependencyProperty RadiusProperty = DependencyProperty.Register(
            "Radius", typeof(int), typeof(ColorPicker), new PropertyMetadata(256, OnRadiusChanged));


        internal static readonly DependencyProperty OrientationProperty = DependencyProperty.Register(
            "Orientation", typeof(Orientation), typeof(ColorPicker), new PropertyMetadata(Orientation.Horizontal));

        internal static readonly DependencyProperty VerticalOffsetProperty = DependencyProperty.Register(
            "VerticalOffset", typeof(double), typeof(ColorPicker), new FrameworkPropertyMetadata(0.0, FrameworkPropertyMetadataOptions.AffectsMeasure));

        internal static readonly DependencyProperty HorizontalOffsetProperty = DependencyProperty.Register(
            "HorizontalOffset", typeof(double), typeof(ColorPicker), new FrameworkPropertyMetadata(0.0, FrameworkPropertyMetadataOptions.AffectsMeasure));



        // 当R\G\B\A的值发生改变时,调用该方法,会设置SelectColor、SelectBrush、ColorString、HexString等属性的值
        private static void OnValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            ColorPicker instance = (ColorPicker)d;
            Color newColor = new() { A = instance.Alpha, B = instance.Blue, G = instance.Green, R = instance.Red };

            instance.SelectColor = newColor;
            instance.SelectBrush = new SolidColorBrush(newColor);
            instance.ColorString = $"( R:{newColor.R}, G:{newColor.G}, B:{newColor.B}, A:{newColor.A} )";
            instance.HexString = $"#{newColor.R:X2}{newColor.G:X2}{newColor.B:X2}{newColor.A:X2}";
            instance.OnSelectedColor();
        }



        // 当渲染方法发生改变时
        private static void OnMethodChange(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            ColorPicker instance = (ColorPicker)d;
            instance.Drawing();
        }



        // 当Radius属性的值发生改变时,调用该方法
        private static void OnRadiusChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            ColorPicker instance = (ColorPicker)d;
            instance.Drawing();
        }



        public override void OnApplyTemplate()
        {
            if (image == null) return;

            image.MouseDown -= Image_MouseDown;                       // 卸载事件


            if (GetTemplateChild(elementName) is Image temp)
            {
                image = temp;
                image.MouseDown += Image_MouseDown;
            }
        }



        private void Image_MouseDown(object sender, MouseButtonEventArgs e)
        {
            if (image == null) return;
            Point mouse = e.GetPosition(image);
            int x = (int)(image.Source.Width / image.ActualWidth * mouse.X);
            int y = (int)(image.Source.Height / image.ActualHeight * mouse.Y);

            unsafe
            {
                Source.Lock();
                IntPtr pointer = Source.BackBuffer;
                int offset = (x + y * Source.PixelWidth) * Source.Format.BitsPerPixel / 8;
                Blue = Marshal.ReadByte(pointer, offset);
                Green = Marshal.ReadByte(pointer, offset + 1);
                Red = Marshal.ReadByte(pointer, offset + 2);
                Alpha = Marshal.ReadByte(pointer, offset + 3);
                Source.Unlock();
            }
        }


        #endregion


        #region 依赖性属性包装器


        /// <summary>
        /// 调色盘图像
        /// </summary>
        public WriteableBitmap Source
        {
            get => (WriteableBitmap)GetValue(SourceProperty);
            set => SetValue(SourceProperty, value);
        }


        /// <summary>
        /// 红色值
        /// </summary>
        public byte Red
        {
            get => (byte)GetValue(RedPorperty);
            set => SetValue(RedPorperty, value);
        }



        /// <summary>
        /// 绿色值
        /// </summary>
        public byte Green
        {
            get => (byte)GetValue(GreenProperty);
            set => SetValue(GreenProperty, value);
        }



        /// <summary>
        /// 绿色值
        /// </summary>
        public byte Blue
        {
            get => (byte)GetValue(BlueProperty);
            set => SetValue(BlueProperty, value);
        }


        /// <summary>
        /// 透明度
        /// </summary>
        public byte Alpha
        {
            get => (byte)GetValue(AlphaProperty);
            set => SetValue(AlphaProperty, value);
        }


        /// <summary>
        /// 选择的颜色
        /// </summary>
        public Color SelectColor
        {
            get => (Color)GetValue(SelectColorProperty);
            set => SetValue(SelectColorProperty, value);
        }



        /// <summary>
        /// 选择的颜色构成的画刷
        /// </summary>
        public SolidColorBrush SelectBrush
        {
            get => (SolidColorBrush)GetValue(SelectBrushProperty);
            set => SetValue(SelectBrushProperty, value);
        }


        /// <summary>
        /// 颜色值的字符串表示,含Alpha值
        /// </summary>
        public string ColorString
        {
            get => (string)GetValue(ColorStringProperty);
            set => SetValue(ColorStringProperty, value);
        }


        /// <summary>
        /// 颜色值的十六进制表示,含Alpha值
        /// </summary>
        public string HexString
        {
            get => (string)GetValue(HexStringProperty);
            set => SetValue(HexStringProperty, value);
        }



        /// <summary>
        /// 生成调色盘的方法
        /// </summary>
        public ColorWheelMethod Method
        {
            get => (ColorWheelMethod)GetValue(MethodProperty);
            set => SetValue(MethodProperty, value);
        }



        /// <summary>
        /// 调色盘图像的像素点半径:与控件的宽度无关
        /// </summary>
        public int Radius
        {
            get => (int)GetValue(RadiusProperty);
            set => SetValue(RadiusProperty, value);
        }


        /// <summary>
        /// 排列方向
        /// </summary>
        public Orientation Orientation
        {
            get => (Orientation)GetValue(OrientationProperty);
            set => SetValue(OrientationProperty, value);
        }


        /// <summary>
        /// 垂直方向偏移量
        /// </summary>
        public double VerticalOffset
        {
            get => (double)GetValue(VerticalOffsetProperty);
            set => SetValue(VerticalOffsetProperty, value);
        }


        /// <summary>
        /// 水平方向偏移量
        /// </summary>
        public double HorizontalOffset
        {
            get => (double)GetValue(HorizontalOffsetProperty);
            set => SetValue(HorizontalOffsetProperty, value);
        }



        #endregion


        #region 路由事件

        internal static readonly RoutedEvent SelectedColorEvent = EventManager.RegisterRoutedEvent(
            "SelectedColor", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(ColorPicker));


        /// <summary>
        /// 选择了颜色事件
        /// </summary>
        public event RoutedEventHandler SelectedColor
        {
            add => AddHandler(SelectedColorEvent, value);
            remove => RemoveHandler(SelectedColorEvent, value);
        }


        // 触发SelectedColor事件
        protected void OnSelectedColor()
        {
            RoutedEventArgs e = new(SelectedColorEvent, this);
            RaiseEvent(e);
        }

        #endregion


        static ColorPicker()
        {
            DefaultStyleKeyProperty.OverrideMetadata(typeof(ColorPicker), new FrameworkPropertyMetadata(typeof(ColorPicker)));

        }


        public ColorPicker()
        {
            Drawing();
        }



        // 根据Method的值绘制色轮
        private void Drawing()
        {

            switch (Method)
            {
                case ColorWheelMethod.HSB:
                    AccordingHSB();
                    break;

                case ColorWheelMethod.Pixel:
                    AccordingPixel();
                    break;
            }
        }


        #region 采用逐像素点的模式


        // 采用计算逐个像素点的颜色的方法绘制色轮
        private void AccordingPixel()
        {
            int radius = Radius;
            Vector o = new(radius, 0);                                           // 定义X轴向量
            WriteableBitmap bitmap = new(radius * 2 + 1, radius * 2 + 1, 96, 96, PixelFormats.Pbgra32, null);
            unsafe
            {
                bitmap.Lock();
                IntPtr pointer = bitmap.BackBuffer;                                     // 找到内存中的图像的首地址
                byte r, g, b;
                for (int y = 0; y < bitmap.PixelHeight; y++)
                {
                    for (int x = 0; x < bitmap.PixelWidth; x++)
                    {
                        double distance = Math.Sqrt(Math.Pow(x - radius, 2) + Math.Pow(y - radius, 2));     // 到圆心的距离
                        if (distance > radius) continue;

                        Vector v1 = new(x - radius, radius - y);                                     // 坐标换算后的向量
                        double angel = Vector.AngleBetween(o, v1);                                // 计算两个向量的夹角
                        if (angel <= -60)
                        {
                            // 落在蓝色区间
                            r = CalculateSpread(distance, -angel - 60);
                            g = CalculateSpread(distance, 180 + angel);
                            b = 255;
                        }
                        else if (angel <= 60)
                        {
                            // 落在红色区间
                            r = 255;
                            g = CalculateSpread(distance, 60 - angel);
                            b = CalculateSpread(distance, 60 + angel);
                        }
                        else
                        {
                            // 落在绿色区间
                            r = CalculateSpread(distance, angel - 60);
                            g = 255;
                            b = CalculateSpread(distance, 180 - angel);
                        }

                        int offset = (x + y * bitmap.PixelWidth) * bitmap.Format.BitsPerPixel / 8;
                        Marshal.WriteByte(pointer, offset, b);
                        Marshal.WriteByte(pointer, offset + 1, g);
                        Marshal.WriteByte(pointer, offset + 2, r);
                        Marshal.WriteByte(pointer, offset + 3, 255);
                    }
                }
                Int32Rect rect = new(0, 0, bitmap.PixelWidth, bitmap.PixelHeight);   // 要更新的区域
                bitmap.AddDirtyRect(rect);
                bitmap.Unlock();
            }
            Source = bitmap;
        }


        /// <summary>
        /// 计算某个颜色的分量。distance为点到圆心的距离;angel为点与圆心的连线,与颜色的边界之间的夹角
        /// </summary>
        private byte CalculateSpread(double distance, double angel)
        {
            // 计算圆周分量:如果两个向量的夹角大于60度,则圆周分量为0;否则,计算比例递减
            double circum = angel > 60 ? 0 : 255 - angel / 60 * 255;
            return (byte)(255 - distance / Radius * (255 - circum));
        }


        #endregion



        #region 采用HSB模式

        private void AccordingHSB()
        {
            int radius = Radius;
            Vector o = new(radius, 0);                                       // 圆心的向量:x轴正向为0°
            WriteableBitmap bitmap = new(radius * 2 + 1, radius * 2 + 1, 96, 96, PixelFormats.Pbgra32, null);
            unsafe
            {
                bitmap.Lock();
                IntPtr pointer = bitmap.BackBuffer;                                                             // 找到内存中的图像的首地址

                for (int y = 0; y < bitmap.PixelHeight; y++)
                {
                    for (int x = 0; x < bitmap.PixelWidth; x++)
                    {
                        double s = Math.Sqrt(Math.Pow(x - radius, 2) + Math.Pow(y - radius, 2)) / radius;       // (x,y)点到圆心的距离,与r的比值
                        if (s > 1) continue;                                                                    // 在圆之外,跳过

                        Vector v1 = new(x - radius, radius - y);                                                // 点(x,y)以圆心为坐标系原点的坐标
                        double h = Vector.AngleBetween(o, v1);                                                  // 从o->v1的夹角,逆时针为正,顺时针为负
                        if (h <= 0) h += 360;

                        Color c = HsbToColor(h, s, 1.0);                                                        // 将 HSB模式转换为RGB颜色模式

                        int offset = (x + y * bitmap.PixelWidth) * bitmap.Format.BitsPerPixel / 8;
                        Marshal.WriteByte(pointer, offset, c.B);
                        Marshal.WriteByte(pointer, offset + 1, c.G);
                        Marshal.WriteByte(pointer, offset + 2, c.R);
                        Marshal.WriteByte(pointer, offset + 3, c.A);
                    }
                }
                Int32Rect rect = new(0, 0, bitmap.PixelWidth, bitmap.PixelHeight);   // 要更新的区域
                bitmap.AddDirtyRect(rect);
                bitmap.Unlock();
            }
            Source = bitmap;
        }



        // HSB颜色模式:h:色相(相位),s:饱和度,brightness:明度
        private static Color HsbToColor(double h, double s, double brightness)
        {
            if (h == 360) h = 0;
            double r = 0;
            double g = 0;
            double b = 0;
            if (s == 0)
            {
                r = g = b = brightness;
            }
            else
            {
                double sectorPos = h / 60f;                                 // 将圆划分为6等分,每等分60°。
                int sectorNum = (int)Math.Floor(sectorPos);                 // 取整数部分
                double fractionalSector = sectorPos - sectorNum;            // 取小数部分

                double p = brightness * (1 - s);
                double q = brightness * (1 - s * fractionalSector);
                double t = brightness * (1 - s * (1 - fractionalSector));

                switch (sectorNum)
                {
                    case 0:
                        r = brightness;
                        g = t;
                        b = p;
                        break;
                    case 1:
                        r = q;
                        g = brightness;
                        b = p;
                        break;
                    case 2:
                        r = p;
                        g = brightness;
                        b = t;
                        break;
                    case 3:
                        r = p;
                        g = q;
                        b = brightness;
                        break;
                    case 4:
                        r = t;
                        g = p;
                        b = brightness;
                        break;
                    case 5:
                        r = brightness;
                        g = p;
                        b = q;
                        break;
                }
            }

            byte byteR = (byte)(r * 255), byteG = (byte)(g * 255), byteB = (byte)(b * 255);
            return Color.FromRgb(byteR, byteG, byteB);
        }

        #endregion

    }
}
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                    xmlns:rayCtl ="clr-namespace:RayLib.WPF" >

    <ResourceDictionary.MergedDictionaries>
        <ResourceDictionary Source="/RayLib;component/Themes/Public.xaml" />
    </ResourceDictionary.MergedDictionaries>

    <Style TargetType="{x:Type rayCtl:ColorPicker}">
        <Setter Property="Width" Value="100" />
        <Setter Property="Height" Value="20"/>
        <Setter Property="Background" Value="White"/>
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type rayCtl:ColorPicker}">

                    <StackPanel Orientation="Horizontal">
                        <Rectangle Name="rect" 
                                   Width="{TemplateBinding Width}" 
                                   Height="{TemplateBinding Height}"
                                   Fill="{TemplateBinding SelectBrush}"/>

                        <Popup Name="popup" StaysOpen="False" PlacementTarget="{Binding ElementName=rect}" AllowsTransparency="True"
                               Placement="Bottom"
                               HorizontalOffset="{TemplateBinding HorizontalOffset}"
                               VerticalOffset="{TemplateBinding VerticalOffset}">

                            <Border Background="{TemplateBinding Background}"
                                    BorderBrush="{TemplateBinding BorderBrush}"
                                    BorderThickness="{TemplateBinding BorderThickness}"
                                    CornerRadius="{Binding Path=(rayCtl:CornerElement.CornerRadius), RelativeSource={RelativeSource Mode=Self}}">

                                <Border.Resources>
                                    <Style TargetType="Rectangle">
                                        <Setter Property="Width" Value="30"/>
                                        <Setter Property="Height" Value="15" />
                                        <Setter Property="Margin" Value="5"/>
                                    </Style>

                                    <Style TargetType="Slider">
                                        <Setter Property="Minimum" Value="0"/>
                                        <Setter Property="Maximum" Value="255"/>
                                        <Setter Property="Width" Value="100"/>
                                        <Setter Property="Grid.Column" Value="1"/>
                                        <Setter Property="VerticalAlignment" Value="Center"/>
                                        <Setter Property="Template" Value="{StaticResource SliderBase}"/>
                                    </Style>

                                    <Style TargetType="TextBox">
                                        <Setter Property="Grid.Column" Value="2"/>
                                        <Setter Property="VerticalContentAlignment" Value="Center"/>
                                        <Setter Property="HorizontalContentAlignment" Value="Center"/>
                                        <Setter Property="Margin" Value="5"/>
                                    </Style>

                                </Border.Resources>

                                <Grid HorizontalAlignment="Left" VerticalAlignment="Top">
                                    <Grid.RowDefinitions>
                                        <RowDefinition Height="*"/>
                                        <RowDefinition Height="auto"/>
                                    </Grid.RowDefinitions>

                                    <Grid.ColumnDefinitions>
                                        <ColumnDefinition Width="*"/>
                                        <ColumnDefinition Width="auto"/>
                                    </Grid.ColumnDefinitions>

                                    <Image Name="Part_Image" Stretch="Uniform" Cursor="Cross" Width="200" Height="200"
                                               Source="{TemplateBinding Source}" />

                                    <Grid Name="valuePanel" Grid.Column="1" HorizontalAlignment="Left" VerticalAlignment="Center">
                                        <Grid.RowDefinitions>
                                            <RowDefinition Height="auto"/>
                                            <RowDefinition Height="auto"/>
                                            <RowDefinition Height="auto"/>
                                            <RowDefinition Height="auto"/>
                                            <RowDefinition Height="auto"/>
                                        </Grid.RowDefinitions>

                                        <Grid.ColumnDefinitions>
                                            <ColumnDefinition Width="auto"/>
                                            <ColumnDefinition Width="*"/>
                                        </Grid.ColumnDefinitions>

                                        <Rectangle Fill="{TemplateBinding SelectBrush}" Grid.Row="0"/>
                                        <Rectangle Fill="Red" Grid.Row="1"/>
                                        <Rectangle Fill="Green" Grid.Row="2"/>
                                        <Rectangle Fill="Blue" Grid.Row="3"/>
                                        <Rectangle Grid.Row="4">
                                            <Rectangle.Fill>
                                                <LinearGradientBrush>
                                                    <GradientStop Color="Transparent" Offset="0"/>
                                                    <GradientStop Color="Black" Offset="1"/>
                                                </LinearGradientBrush>
                                            </Rectangle.Fill>
                                        </Rectangle >

                                        <TextBox Text="{TemplateBinding HexString}"  Grid.Row="0" IsReadOnly="True"/>
                                        <Slider Name="sliderR" Grid.Row="1" Value="{Binding Red, Mode=TwoWay,RelativeSource={RelativeSource Mode=TemplatedParent}, Converter={StaticResource byte2Double}}"   />
                                        <Slider Name="sliderG" Grid.Row="2" Value="{Binding Green, Mode=TwoWay,RelativeSource={RelativeSource Mode=TemplatedParent}, Converter={StaticResource  byte2Double}}" />
                                        <Slider Name="sliderB" Grid.Row="3" Value="{Binding Blue, Mode=TwoWay,RelativeSource={RelativeSource Mode=TemplatedParent}, Converter={StaticResource  byte2Double}}"  />
                                        <Slider Name="sliderA" Grid.Row="4" Value="{Binding Alpha, Mode=TwoWay,RelativeSource={RelativeSource Mode=TemplatedParent}, Converter={StaticResource  byte2Double}}" />

                                    </Grid>
                                </Grid>
                            </Border>
                        </Popup>

                    </StackPanel>

                    <ControlTemplate.Triggers>

                        <Trigger Property="IsMouseOver" Value="True">
                            <Setter TargetName="popup" Property="IsOpen" Value="True"/>
                        </Trigger>

                        <Trigger Property="Orientation" Value="Vertical">
                            <Setter TargetName="valuePanel" Property="Grid.Row" Value="1"/>
                            <Setter TargetName="valuePanel" Property="Grid.Column" Value="0"/>
                            <Setter TargetName="valuePanel" Property="HorizontalAlignment" Value="Center"/>
                            <Setter TargetName="valuePanel" Property="VerticalAlignment" Value="Top"/>
                        </Trigger>

                    </ControlTemplate.Triggers>

                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>

</ResourceDictionary>

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
WPF 中,可以使用 ed:Arc 控件来绘制圆弧进度条,并设置两端圆角。具体步骤如下: 1. 引入 ed:Arc 命名空间: ```xaml xmlns:ed="http://schemas.efxui.com/xaml" ``` 2. 在 XAML 中使用 ed:Arc 控件,并设置 StrokeStartLineCap 和 StrokeEndLineCap 属性为 Round: ```xaml <ed:Arc Stroke="Gray" StrokeThickness="10" StrokeStartLineCap="Round" StrokeEndLineCap="Round" ArcThickness="10" ArcStart="90" ArcEnd="{Binding Progress}" /> ``` 其中,StrokeStartLineCap 和 StrokeEndLineCap 属性设置线条端点的形状,ArcThickness 属性设置圆弧的宽度,ArcStart 和 ArcEnd 属性设置圆弧的起点和终点。 3. 在 ViewModel 中定义 Progress 属性,并使用 Timer 定时更新进度: ```csharp public class MainWindowViewModel : INotifyPropertyChanged { private int _progress; private Timer _timer; public int Progress { get => _progress; set { if (_progress != value) { _progress = value; OnPropertyChanged(nameof(Progress)); } } } public MainWindowViewModel() { _timer = new Timer(100); _timer.Elapsed += (sender, e) => { Progress += 5; if (Progress > 360) { Progress = 0; } }; _timer.Start(); } public event PropertyChangedEventHandler PropertyChanged; protected void OnPropertyChanged(string propertyName) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } } ``` 完整代码示例: MainWindow.xaml: ```xaml <Window x:Class="WpfApp1.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:ed="http://schemas.efxui.com/xaml" Title="MainWindow" Height="200" Width="200"> <Grid> <Grid.RowDefinitions> <RowDefinition Height="Auto" /> <RowDefinition Height="*" /> </Grid.RowDefinitions> <TextBlock Text="Progress:" Margin="5" /> <ed:Arc Grid.Row="1" Stroke="Gray" StrokeThickness="10" StrokeStartLineCap="Round" StrokeEndLineCap="Round" ArcThickness="10" ArcStart="90" ArcEnd="{Binding Progress}" /> </Grid> </Window> ``` MainWindow.xaml.cs: ```csharp public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); DataContext = new MainWindowViewModel(); } } ``` MainWindowViewModel.cs: ```csharp public class MainWindowViewModel : INotifyPropertyChanged { private int _progress; private Timer _timer; public int Progress { get => _progress; set { if (_progress != value) { _progress = value; OnPropertyChanged(nameof(Progress)); } } } public MainWindowViewModel() { _timer = new Timer(100); _timer.Elapsed += (sender, e) => { Progress += 5; if (Progress > 360) { Progress = 0; } }; _timer.Start(); } public event PropertyChangedEventHandler PropertyChanged; protected void OnPropertyChanged(string propertyName) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } } ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值