首页 > 其他分享 >wpf 如何写一个圆形的进度条

wpf 如何写一个圆形的进度条

时间:2024-08-13 11:27:52浏览次数:11  
标签:控件 IsIndeterminate 进度条 样式 double CycleProgressBar 圆形 wpf public

先看一下效果吧

调用代码如下

<local:CycleProgressBar Width="100" Height="100" Background="#FFF68986" Foreground="#FFFA1F09"
                        Maximum="100" Minimum="0" Value="20" IsIndeterminate="False"/>

然后下面就来实现一下这个效果

第一步:先创建一个空的wpf项目

第二步:添加一个自定义控件,取名为CycleProgressBar

添加完以后,vs会自动生成一个类和一个Themes文件夹,下面有一个名为Generic的资源文件

Generic里面就是这个自定义控件的默认样式,里面只有一个border,我们就是通过改造这个默认的样式来实现圆形的进度条

到目前位置,都是vs自动生成的代码,不需要我们做任何操作

 

第三步:将父类设置成RangeBase,因为原生的progressbar就是继承的这个类,所以我们也继承这个类

 

第四步:添加依赖属性IsIndeterminate,这个属性用来控制进度条是不是一直转圈圈

        public bool IsIndeterminate
        {
            get { return (bool)GetValue(IsIndeterminateProperty); }
            set { SetValue(IsIndeterminateProperty, value); }
        }

        // Using a DependencyProperty as the backing store for IsIndeterminate.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty IsIndeterminateProperty =
            DependencyProperty.Register("IsIndeterminate", typeof(bool), typeof(CycleProgressBar), new PropertyMetadata(false));

 

第五步:绘制控件的模板样式

在绘制之前,先添加一个nuget上面的引用,搜索expression.drawing,然后添加下面的引用

再在generic文件里面引入命名空间

xmlns:ed="http://schemas.microsoft.com/expression/2010/drawing"

再写样式之前的准备工作就准备完了,后面就是开始写模板样式了,下面是样式的代码和注释

 <local:ProgressBarValueToPercentage x:Key="ProgressBarValueToPercentage"/>

 <Style TargetType="{x:Type local:CycleProgressBar}">
     <Setter Property="Template">
         <Setter.Value>
             <ControlTemplate TargetType="{x:Type local:CycleProgressBar}">
                 <Grid>
                     <!--这个椭圆就是进度条的背景部分,把高度和宽度都绑定到宽度上,当然绑定到高度上也一样,主要是为了保证高度和宽度一直,这样子才会显示成圆形
                     StrokeThickness根据自己的实际情况设置,这个表示背景部分的宽度-->
                     <Ellipse Stroke="{TemplateBinding Background}" StrokeThickness="10"
                          Height="{TemplateBinding Width}" Width="{TemplateBinding Width}"/>
                     
                     <!--这个圆弧的作用就是显示进度条的进度-->
                     <ed:Arc x:Name="PART_Track" Width="{TemplateBinding Width}" Height="{TemplateBinding Width}"
                                     StartAngle="0" EndAngle="0" Fill="{TemplateBinding Foreground}" Panel.ZIndex="1"
                                     ArcThickness="10"
                                     Stretch="None" StrokeEndLineCap="Round" StrokeStartLineCap="Round" ArcThicknessUnit="Pixel" RenderTransformOrigin="0.5,0.5">
                         <ed:Arc.RenderTransform>
                             <TransformGroup>
                                 <RotateTransform/>
                             </TransformGroup>
                         </ed:Arc.RenderTransform>
                     </ed:Arc>

                     <!--这个进度条的作用就是当我们设置IsIndeterminate为true的时候,让这个进度条一直在那里转圈圈-->
                     <ed:Arc x:Name="PART_Track_Repeat" Width="{TemplateBinding Width}" Height="{TemplateBinding Width}"
                                     StartAngle="0" EndAngle="90" Fill="{TemplateBinding Foreground}" Panel.ZIndex="1"
                                     ArcThickness="10" Visibility="Collapsed"
                                     Stretch="None" StrokeEndLineCap="Round" StrokeStartLineCap="Round" 
                                     ArcThicknessUnit="Pixel" RenderTransformOrigin="0.5,0.5">
                         <ed:Arc.RenderTransform>
                             <TransformGroup>
                                 <RotateTransform/>
                             </TransformGroup>
                         </ed:Arc.RenderTransform>
                     </ed:Arc>
                     
                     <!--这个textblock的作用就是显示进度条的百分比-->
                     <TextBlock x:Name="tbPercentage" VerticalAlignment="Center" HorizontalAlignment="Center">
                         <TextBlock.Text>
                             <MultiBinding Converter="{StaticResource ResourceKey=ProgressBarValueToPercentage}">
                                 <Binding RelativeSource="{RelativeSource Mode=FindAncestor, AncestorType=local:CycleProgressBar}" Path="Maximum"/>
                                 <Binding RelativeSource="{RelativeSource Mode=FindAncestor, AncestorType=local:CycleProgressBar}" Path="Value"/>
                                 <Binding RelativeSource="{RelativeSource Mode=FindAncestor, AncestorType=local:CycleProgressBar}" Path="Minimum"/>
                             </MultiBinding>
                         </TextBlock.Text>
                     </TextBlock>
                 </Grid>
                 <ControlTemplate.Triggers>
                     <!--设置IsIndeterminate为true的时候,就把百分比和进度条的进度的圆弧隐藏,只保留一直转圈圈的那个圆弧-->
                     <!--然后就是一个动画,让圆弧一直转圈圈-->
                     <Trigger Property="IsIndeterminate" Value="True">
                         <Setter Property="Visibility" TargetName="PART_Track" Value="Hidden"/>
                         <Setter Property="Visibility" TargetName="PART_Track_Repeat" Value="Visible"/>
                         <Setter Property="Visibility" TargetName="tbPercentage" Value="Hidden"/>
                         <Trigger.EnterActions>
                             <BeginStoryboard>
                                 <Storyboard RepeatBehavior="Forever">
                                     <DoubleAnimationUsingKeyFrames Storyboard.TargetName="PART_Track_Repeat" Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[0].(RotateTransform.Angle)">
                                         <EasingDoubleKeyFrame KeyTime="00:00:00" Value="0"/>
                                         <EasingDoubleKeyFrame KeyTime="00:00:01" Value="360"/>
                                     </DoubleAnimationUsingKeyFrames>
                                 </Storyboard>
                             </BeginStoryboard>
                         </Trigger.EnterActions>
                     </Trigger>
                     <Trigger Property="IsIndeterminate" Value="False">
                         <Setter TargetName="tbPercentage" Property="Visibility" Value="Visible"/>
                     </Trigger>
                     <MultiTrigger>
                         <MultiTrigger.Conditions>
                             <Condition Property="IsIndeterminate" Value="false"/>
                             <Condition Property="Value" Value="0"/>
                         </MultiTrigger.Conditions>
                         <Setter Property="Visibility" Value="Hidden" TargetName="PART_Track"/>
                     </MultiTrigger>
                 </ControlTemplate.Triggers>
             </ControlTemplate>
         </Setter.Value>
     </Setter>
 </Style>

里面有一个名为ProgressBarValueToPercentage的转换,直接添加一个类,然后代码就在下面

public class ProgressBarValueToPercentage : IMultiValueConverter
{
    public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
    {
        var maximum = System.Convert.ToDouble(values[0]);
        var value = System.Convert.ToDouble(values[1]);
        var minimum = System.Convert.ToDouble(values[2]);

        if (maximum == 0)
        {
            return "0" + "%";
        }

        double progressValue = (value - minimum) / (maximum - minimum) * 100;
        return (Math.Round(progressValue)).ToString() + "%";
    }

    public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}

到现在,样式部分就写完了,然后还要去后台代码里面实现具体的功能

第六步:实现后台代码功能

public class CycleProgressBar : RangeBase
{
    public bool IsIndeterminate
    {
        get { return (bool)GetValue(IsIndeterminateProperty); }
        set { SetValue(IsIndeterminateProperty, value); }
    }

    // Using a DependencyProperty as the backing store for IsIndeterminate.  This enables animation, styling, binding, etc...
    public static readonly DependencyProperty IsIndeterminateProperty =
        DependencyProperty.Register("IsIndeterminate", typeof(bool), typeof(CycleProgressBar), new PropertyMetadata(false));

    private FrameworkElement _track;

    static CycleProgressBar()
    {
        //这段代码是创建控件的时候自带的,不用管,代码的意思就是去找generic里面名为CycleProgressBar的样式,
        //如果把这段代码删了,或者generic没有CycleProgressBar的样式,程序就会报错
        DefaultStyleKeyProperty.OverrideMetadata(typeof(CycleProgressBar), new FrameworkPropertyMetadata(typeof(CycleProgressBar)));
    }

    /// <summary>
    /// 计算进度条的值
    /// </summary>
    private void SetPartTrackValue()
    {
        double minimum = this.Minimum;
        double maximum = this.Maximum;
        double value = this.Value;
        double num = (maximum <= minimum) ? 1.0 : ((value - minimum) / (maximum - minimum));

        var EndAngle = num * 360;

        if (_track != null)
        {
            var arc = _track as Arc;
            arc.EndAngle = EndAngle;
        }
    }

    /// <summary>
    /// 应用控件模板的时候调用的方法
    /// </summary>
    public override void OnApplyTemplate()
    {
        base.OnApplyTemplate();
        this._track = GetTemplateChild("PART_Track") as FrameworkElement;
        SetPartTrackValue();
    }

    /// <summary>
    /// 进度条的进度变化时触发
    /// </summary>
    /// <param name="oldValue"></param>
    /// <param name="newValue"></param>
    protected override void OnValueChanged(double oldValue, double newValue)
    {
        base.OnValueChanged(oldValue, newValue);
        SetPartTrackValue();
    }

    /// <summary>
    /// 最大值变化时触发
    /// </summary>
    /// <param name="oldMaximum"></param>
    /// <param name="newMaximum"></param>
    protected override void OnMaximumChanged(double oldMaximum, double newMaximum)
    {
        base.OnMaximumChanged(oldMaximum, newMaximum);
        SetPartTrackValue();
    }

    /// <summary>
    /// 最小值变化时触发
    /// </summary>
    /// <param name="oldMinimum"></param>
    /// <param name="newMinimum"></param>
    protected override void OnMinimumChanged(double oldMinimum, double newMinimum)
    {
        base.OnMinimumChanged(oldMinimum, newMinimum);
        SetPartTrackValue();
    }

    /// <summary>
    /// 控件大小变化时触发
    /// </summary>
    /// <param name="sizeInfo"></param>
    protected override void OnRenderSizeChanged(SizeChangedInfo sizeInfo)
    {
        base.OnRenderSizeChanged(sizeInfo);
        SetPartTrackValue();
    }
}

好了,控件的后台代码也写完了,就可以直接运行了

设置IsIndeterminate="True"以后,黄色部分就会一直转圈圈啦(脑补一下吧,没有gif,囧)

 

项目github地址:bearhanQ/WPFFramework: Share some experience (github.com)

QQ技术交流群:332035933;

 

标签:控件,IsIndeterminate,进度条,样式,double,CycleProgressBar,圆形,wpf,public
From: https://www.cnblogs.com/lvpp13/p/18356513

相关文章

  • wpf GMap
    AMapProviderBasepublicabstractclassAMapProviderBase:GMapProvider{publicAMapProviderBase(){MaxZoom=null;RefererUrl="http://www.amap.com/";//Copyright=string.Format("©{0}高德Corporation,©{0}......
  • DBGrid之进度条显示
    https://www.packtpub.com/en-us/product/delphi-cookbook-second-edition-9781785287428/chapter/delphi-basics-1/section/customizing-tdbgrid-ch01lvl1sec11procedureTForm13.DBGrid1DrawColumnCell(Sender:TObject;constRect:TRect;DataCol:Integer;Column:......
  • wpf 如何7步写一个badge控件
    首先看一下效果: 任意控件可以附加一个文字在控件的右上角,并带有红色背景第一步,新建一个空的wpf项目:第二步,创建一个类,取名为badge:第三步,将badge的父类设置成  System.Windows.Documents.AdornerpublicclassBadge:Adorner{publicBadge(UIElemen......
  • 界面控件DevExpress WPF v24.1系统环境配置要求
    DevExpressWPF 拥有120+个控件和库,将帮助您交付满足甚至超出企业需求的高性能业务应用程序。通过DevExpressWPF能创建有着强大互动功能的XAML基础应用程序,这些应用程序专注于当代客户的需求和构建未来新一代支持触摸的解决方案。无论是Office办公软件的衍伸产品,还是以数据为中......
  • wpf 使用razor
    WPFBlazorChat\RazorViews\Counter.razor<h1>Counter</h1><p>好开心,你点我了,现在是:<spanstyle="color:red;">@currentCount</span></p><buttonclass="btnbtn-primary"@onclick="IncrementCount"&g......
  • wpf项目如何使用blazor组件
    依然使用上面的工程,添加Blazor支持,此部分参考微软文档生成WindowsPresentationFoundation(WPF)Blazor应用,本小节快速略过。2.1编辑工程文件WPFBlazorChat.csproj在项目文件的顶部,将SDK更改为Microsoft.NET.Sdk.Razor。添加节点<RootNameSpace>WPFBlazorChat</RootN......
  • 简单在 WinUI 仿造 WPF 的 ColumnDefinition SharedSizeGroup 共享列宽功能
    本文将告诉大家如何在WinUI3或UNO里面,仿造WPF的ColumnDefinitionSharedSizeGroup共享列宽功能本文的实现代码是大量从https://github.com/Qiu233/WinUISharedSizeGroup抄的,感谢大佬提供的代码。我在此基础上简化了对Behavior的依赖,在本文末尾放上了全部代码的下载......
  • 鸿蒙 webview 实现顶部 Progress进度条
    1,先看效果 2,直接cv代码importweb_webviewfrom'@ohos.web.webview';interfacePerUrl{url:string,age:number}@Componentexportstructwebviews{controller:web_webview.WebviewController=newweb_webview.WebviewController();ports:......
  • 画一个圆形 极语言
    程序段窗体启动整数设备,刷子;设备=取设备(窗体);刷子=创建画刷($25022);选择对象(设备,蓝刷);画椭圆(设备,10,10,20,20);结束创建一个刷子,在画板上画出图形. 圆形就理解为正圆形了,正圆形其实就是宽度和高度相同的椭圆. 用设备这个变量存窗体的地址,用刷子......
  • android13 下拉栏修改为圆形图标
    在android13默认使用了我们想要将他改成圆形的图标,然后将字体修改到图标下方首先我们要找到图标是在哪里添加进去的在QSFactoryImpl有个方法@OverridepublicQSTileViewcreateTileView(Contextcontext,QSTiletile,booleancollapsedView){QSI......