WPF 原创旋钮 KnobButton (自定义控件)

旋钮效果

WPF 原创旋钮 KnobButton

C#代码:

public class KnobButton : Slider, IExecutable
    {
        static KnobButton()
        {
            DefaultStyleKeyProperty.OverrideMetadata(typeof(KnobButton), new FrameworkPropertyMetadata(typeof(KnobButton)));
        }

        public KnobButton()
        {
            SetCurrentValue(WidthProperty, 150d);
            SetCurrentValue(HeightProperty, 150d);
            SetCurrentValue(MaximumProperty, 100d);
            MouseDown += Path_MouseDown;
            MouseMove += Path_MouseMove;
            MouseWheel += Path_MouseWheel;
            MouseLeftButtonUp += KnobButton_MouseLeftButtonUp;
            Update();
        }
        
        #region 绘制
        private void InitTick()
        {
            // 画大刻度
            for (int i = 0; i < 11; i++)
            {
                Line line = new Line();
                line.X1 = 0;
                line.Y1 = 0;
                line.X2 = 0;
                line.Y2 = 12;
                line.Stroke = Brushes.Gray;
                line.StrokeThickness = 2;
                line.HorizontalAlignment = HorizontalAlignment.Center;
                line.RenderTransformOrigin = new Point(0.5, 0.5);
                line.RenderTransform = new RotateTransform() { Angle = -140 + i * 28 };
                bdGrid.Children.Add(line);
            }

            // 画小刻度
            for (int i = 0; i < 10; i++)
            {
                var start = -140 + 28 * i + 2.8;
                for (int j = 0; j < 9; j++)
                {
                    Line line = new Line();
                    line.X1 = 0;
                    line.Y1 = 0;
                    line.X2 = 0;
                    line.Y2 = 6;
                    line.Stroke = Brushes.Gray;
                    line.StrokeThickness = 1;
                    line.HorizontalAlignment = HorizontalAlignment.Center;
                    line.RenderTransformOrigin = new Point(0.5, 0.5);
                    line.RenderTransform = new RotateTransform() { Angle = start + j * 2.8 };
                    bdGrid.Children.Add(line);
                }
            }
        }
        #endregion
        protected override void OnValueChanged(double oldValue, double newValue)
        {
            base.OnValueChanged(oldValue, newValue);
            Update();
        }

        protected override void OnMaximumChanged(double oldMaximum, double newMaximum)
        {
            base.OnMaximumChanged(oldMaximum, newMaximum);
            Update();
        }

        protected override void OnMinimumChanged(double oldMinimum, double newMinimum)
        {
            base.OnMinimumChanged(oldMinimum, newMinimum);
            Update();
        }

        public string ValueChangedExecute
        {
            get { return (string)GetValue(ValueChangedExecuteProperty); }
            set { SetValue(ValueChangedExecuteProperty, value); }
        }
        public static readonly DependencyProperty ValueChangedExecuteProperty =
            DependencyProperty.Register("ValueChangedExecute", typeof(string), typeof(KnobButton), new PropertyMetadata(string.Empty));

        public int Step
        {
            get { return (int)GetValue(StepProperty); }
            set { SetValue(StepProperty, value); }
        }
        public static readonly DependencyProperty StepProperty =
            DependencyProperty.Register("Step", typeof(int), typeof(KnobButton), new PropertyMetadata(1));

        RotateTransform rotatevalue;
        Grid bdGrid;
        public override void OnApplyTemplate()
        {
            base.OnApplyTemplate();
            rotatevalue = GetTemplateChild("rotatevalue") as RotateTransform;
            bdGrid = GetTemplateChild("bdGrid") as Grid;
            Update();
            InitTick();
        }

        private void Update()
        {
            if (rotatevalue == null) return;
            double perangle = 280 / (Maximum - Minimum);
            double angle = (perangle * (Value-Minimum)) + 40;

            DoubleAnimation da = new DoubleAnimation();
            da.Duration = new Duration(TimeSpan.FromMilliseconds(350));
            da.EasingFunction = new CubicEase() { EasingMode = EasingMode.EaseOut };
            da.To = angle;

            rotatevalue.Angle = angle;
            rotatevalue.BeginAnimation(RotateTransform.AngleProperty, da);
        }

        Point lastpoint;
        private void Path_MouseMove(object sender, MouseEventArgs e)
        {
            if (e.LeftButton == MouseButtonState.Released) return;

            CaptureMouse();
            Point point = e.GetPosition(this);
            double xmove = point.X - lastpoint.X;
            double ymove = point.Y - lastpoint.Y;

            double changeValue = (xmove + ymove) / 10 * Step;
            if ((changeValue + Value) > Maximum)
            {
                if (Value < Maximum)
                {
                    Value = Maximum;
                }
                return;
            }

            if ((changeValue + Value) < Minimum)
            {
                if (Value > Minimum)
                {
                    Value = Minimum;
                }
                return;
            }

            Value = changeValue + Value;
            lastpoint = point;
        }

        private void Path_MouseDown(object sender, MouseButtonEventArgs e)
        {
            if (e.LeftButton == MouseButtonState.Released) return;
            lastpoint = e.GetPosition(this);
        }

        private void KnobButton_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
        {
            ReleaseMouseCapture();
        }

        private void Path_MouseWheel(object sender, MouseWheelEventArgs e)
        {
            double changeValue = (e.Delta / 120) * Step;
            if ((changeValue + Value) > Maximum)
            {
                if (Value < Maximum)
                {
                    Value = Maximum;
                }
                return;
            }

            if ((changeValue + Value) < Minimum)
            {
                if (Value > Minimum)
                {
                    Value = Minimum;
                }
                return;
            }

            Value = Value + changeValue;
            Update();
        }
    }

Xaml代码:

<Style TargetType="{x:Type ctrl:KnobButton}">
        <Setter Property="Background" Value="#0068F4"/>
        <Setter Property="BorderBrush" Value="LightGray"/>
        <Setter Property="Foreground" Value="Black"/>
        <Setter Property="FontSize" Value="20"/>
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="ctrl:KnobButton">
                    <Grid x:Name="bdGrid" Width="{Binding RelativeSource={RelativeSource Self}, Path=ActualHeight}">
                        <Grid Margin="16" RenderTransformOrigin="0.5,0.5">
                            <Grid.RenderTransform>
                                <RotateTransform x:Name="rotatevalue" Angle="00"/>
                            </Grid.RenderTransform>

                            <Ellipse Margin="4" Fill="#FFF6F6F6" Stroke="{StaticResource ControlBorderBrush}" >
                                <Ellipse.Effect>
                                    <DropShadowEffect ShadowDepth="2" BlurRadius="8" Direction="-90" Color="{Binding RelativeSource={RelativeSource Mode=TemplatedParent},Path=Background.(SolidColorBrush.Color)}"/>
                                </Ellipse.Effect>
                            </Ellipse>
                            <Ellipse Margin="12" Fill="{TemplateBinding Background}" Width="8" Height="8" VerticalAlignment="Bottom">
                            </Ellipse>
                        </Grid>
                        <TextBlock Text="{Binding Value,RelativeSource={RelativeSource Mode=TemplatedParent}, StringFormat={}{0:F2}}" 
                                   VerticalAlignment="Center" HorizontalAlignment="Center" Foreground="{TemplateBinding Foreground}" FontSize="{TemplateBinding FontSize}"/>
                    </Grid>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
WPF MVVM模式中,可以通过在ViewModel中使用ObservableCollection来动态添加自定义控件。ObservableCollection是.NET Framework提供的一个集合类,它能够在集合元素发生变化时自动通知界面进行更新。 首先,在ViewModel中声明一个ObservableCollection属性,用于存储自定义控件的集合。然后,在需要添加自定义控件的地方,通过操作ObservableCollection来添加新的控件。ViewModel会自动通知界面进行更新。 接下来,界面需要绑定这个ObservableCollection属性,并使用数据模板来定义如何渲染每个自定义控件。在XAML中,可以使用ItemsControl或者ListBox等控件来展示这个集合,并通过绑定将集合和数据模板关联起来。 在这个过程中,可以根据需要使用拖放、缩放、旋转等功能。可以参考中的示例代码,了解如何实现这些功能。 最后,通过实例化ViewModel,并将其赋值给界面的DataContext属性,从而建立ViewModel和View之间的关联。可以参考中的代码。 总结起来,实现在WPF MVVM模式中动态添加自定义控件的步骤为: 1. 在ViewModel中声明一个ObservableCollection属性,用于存储自定义控件的集合; 2. 在需要添加自定义控件的地方,通过操作ObservableCollection来添加新的控件; 3. 在界面中,绑定这个ObservableCollection属性,并使用数据模板来定义如何渲染每个自定义控件; 4. 根据需要使用拖放、缩放、旋转等功能; 5. 实例化ViewModel,并将其赋值给界面的DataContext属性,建立ViewModel和View之间的关联。 希望这个实现思路对你有帮助,如果需要更详细的代码示例,可以参考中的文章。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *3* [用WPF mvvm如何动态添加自定义控件问题](https://blog.csdn.net/netyou/article/details/104371498)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] - *2* [WPF Prism MVVM【动态添加控件并可用鼠标、拖动、缩放、旋转】](https://blog.csdn.net/redfox6843/article/details/126117819)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

不知名君

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值