简体   繁体   中英

How to implement circle progress bar (WPF)?

I need something very simple like just show loading process without progress. Like user click on button and process start it takes 5 sec for this time I would like to show to user progress implementation like this

在此处输入图像描述

I tried to find how to implement this, but actually what I found is people build a complete libraries or heavy xaml animation implementation. Actually I thought it should be something simple out of the box option like put in xaml Progress bar and in cs file when you need call myProgressBar.Show() or myProgressBar.Hide() .

That it, I don't need to implement libraries or 200 hundreds lines in my xaml.

How to make this simple implementation?

There is no native WPF control to produce such a display, so you're going to need some code (in a library) that you can use as required. The amount of code depends on just how much flexibility you require for the control.

Here's my version - designed to give some options for the display, but ensures minimal use of system resources, and stops any processing once no longer in use.

<UserControl
    x:Class="Peregrine.WPF.View.Controls.perBusySpinner"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:ctrl="clr-namespace:Peregrine.WPF.View.Controls"
    Width="{Binding RelativeSource={RelativeSource Self}, Path=Height, Mode=TwoWay}"
    Height="120">

    <Viewbox
        HorizontalAlignment="Stretch"
        VerticalAlignment="Stretch">
        <Grid Background="{Binding Path=Background, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type ctrl:perBusySpinner}}}">
            <Canvas
                Width="{Binding Path=Diameter, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type ctrl:perBusySpinner}}}"
                Height="{Binding Path=Diameter, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type ctrl:perBusySpinner}}}"
                HorizontalAlignment="Center"
                VerticalAlignment="Center"
                Background="Transparent"
                RenderTransformOrigin="0.5,0.5"
                UseLayoutRounding="False">

                <Canvas.Resources>
                    <Style TargetType="Ellipse">
                        <Setter Property="Fill" Value="{Binding Path=Foreground, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type ctrl:perBusySpinner}}}" />
                        <Setter Property="Height" Value="{Binding Path=ItemDiameter, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type ctrl:perBusySpinner}}}" />
                        <Setter Property="Stretch" Value="Fill" />
                        <Setter Property="StrokeThickness" Value="0" />
                        <Setter Property="Width" Value="{Binding Path=ItemDiameter, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type ctrl:perBusySpinner}}}" />
                    </Style>
                </Canvas.Resources>

                <Ellipse
                    x:Name="Item1"
                    Opacity="1.0" />
                <Ellipse
                    x:Name="Item2"
                    Opacity="0.92" />
                <Ellipse
                    x:Name="Item3"
                    Opacity="0.84" />
                <Ellipse
                    x:Name="Item4"
                    Opacity="0.76" />
                <Ellipse
                    x:Name="Item5"
                    Opacity="0.68" />
                <Ellipse
                    x:Name="Item6"
                    Opacity="0.60" />
                <Ellipse
                    x:Name="Item7"
                    Opacity="0.52" />
                <Ellipse
                    x:Name="Item8"
                    Opacity="0.44" />
                <Ellipse
                    x:Name="Item9"
                    Opacity="0.36" />
                <Ellipse
                    x:Name="Item10"
                    Opacity="0.28" />
                <Ellipse
                    x:Name="Item11"
                    Opacity="0.20" />
                <Ellipse
                    x:Name="Item12"
                    Opacity="0.12" />

                <Canvas.RenderTransform>
                    <RotateTransform x:Name="SpinnerRotateTransform" Angle="0" />
                </Canvas.RenderTransform>
            </Canvas>
        </Grid>
    </Viewbox>

</UserControl>

.

public partial class perBusySpinner
{
    private readonly DispatcherTimer _spinnerTimer;

    // the nominal size of the spinner - the actual size is determined by the Width / Height as the spinner is contained within a ViewBox
    public double Diameter => 100.0; 
    public double ItemDiameter => Diameter / 6.0;
    public double ItemRadius => ItemDiameter / 2.0;
    public double ItemPositionRadius => (Diameter - ItemDiameter) / 2.0;

    public perBusySpinner()
    {
        InitializeComponent();

        _spinnerTimer = new DispatcherTimer(DispatcherPriority.Normal, Dispatcher);
        _spinnerTimer.Tick += (s, e) => SpinnerRotateTransform.Angle = (SpinnerRotateTransform.Angle + 30) % 360;

        Loaded += (s, e) => OnLoaded();
        Unloaded += (s, e) => Stop();
        IsVisibleChanged += (s, e) => OnIsVisibleChanged((bool)e.NewValue);
    }   

    /// <summary>
    /// IsVisibleChanged also covers the case where the spinner is placed inside another control which itself is collapsed or hidden
    /// </summary>
    /// <param name="isVisible">
    /// </param>
    private void OnIsVisibleChanged(bool isVisible)
    {
        // disable spinning in the Visual Studio designer
        if (perViewModelHelper.IsInDesignMode)
            return;

        if (isVisible)
            Start();
        else
            Stop();
    }

    /// <summary>
    /// Rotations per minute
    /// </summary>
    public int Speed
    {
        get => (int)GetValue(SpeedProperty);
        set => SetValue(SpeedProperty, value);
    }

    public static readonly DependencyProperty SpeedProperty =
        DependencyProperty.Register("Speed", typeof(int), typeof(perBusySpinner), new PropertyMetadata(60));

    private void OnLoaded()
    {
        SetItemPosition(Item1, 0);
        SetItemPosition(Item2, 1);
        SetItemPosition(Item3, 2);
        SetItemPosition(Item4, 3);
        SetItemPosition(Item5, 4);
        SetItemPosition(Item6, 5);
        SetItemPosition(Item7, 6);
        SetItemPosition(Item8, 7);
        SetItemPosition(Item9, 8);
        SetItemPosition(Item10, 9);
        SetItemPosition(Item11, 10);
        SetItemPosition(Item12, 11);
    }

    private void SetItemPosition(DependencyObject item, int index)
    {
        item.SetValue(Canvas.LeftProperty, Diameter / 2.0 + (Math.Sin(Math.PI * (index / 6.0)) * ItemPositionRadius) - ItemRadius);
        item.SetValue(Canvas.TopProperty, Diameter / 2.0 + (Math.Cos(Math.PI * (index / 6.0)) * ItemPositionRadius) - ItemRadius);
    }

    private void Stop()
    {
        _spinnerTimer.Stop();
    }

    private void Start()
    {
        // each tick of the timer is 1 step of revolution
        _spinnerTimer.Interval = TimeSpan.FromMilliseconds(60000 / (12.0 * Speed));
        _spinnerTimer.Start();
    }
}

Once defined in a library, usage of such a control only required a minimal amount of xaml.

<vctrl:perBusySpinner
    Width="32"
    Background="Transparent"
    Foreground="Blue" />

To show and activate the spinner, just set it's Visibilty property to Visible or bind to a boolean property in the ViewModel via a BooleanToVisibility converter.

More detail at my blog post .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM