控制wiggeling wround

问题描述 投票:0回答:1

我试图创建一个可以在一棵树而TreeItem加载用于例如动态圆形进度条。

不幸的是,控制是不是真的运行平稳。

preview

这是16×16,16×32 32×16和400%,大小为32×32。在GIF年初其运行,因为捕获工具的有点laggy多数民众赞成。正如你所看到的圆圈围绕有点wiggeling,这就是我想要删除的内容。

这是我的自定义控制:

代码隐藏:

/// <summary>
///     Interaction logic for CircularProgressBar.xaml
/// </summary>
public partial class CircularProgressBar
{
    public static readonly DependencyProperty DeferedVisibilityProperty = DependencyProperty.Register(nameof(DeferedVisibility), typeof(bool),
        typeof(CircularProgressBar), new PropertyMetadata
        {
            PropertyChangedCallback = OnDeferedVisibilityChanged,
            DefaultValue = false
        });

    private readonly (Ellipse, int)[] _circlesWithOffset;
    private Stopwatch _stopwatch;


    public CircularProgressBar()
    {
        InitializeComponent();
        DefaultStyleKey = typeof(CircularProgressBar);
        _circlesWithOffset = new[] {(C0, 0), (C1, 1), (C2, 2), (C3, 3), (C4, 4), (C5, 5), (C6, 6), (C7, 7), (C8, 8)};
    }


    #region Animation

    private void Start()
    {
        //Mouse.OverrideCursor = Cursors.Wait;
        if(_stopwatch == null)
            _stopwatch = new Stopwatch();
        _stopwatch.Start();
        CompositionTarget.Rendering += CompositionTarget_Rendering;
    }


    private void Stop()
    {
        //Mouse.OverrideCursor = Cursors.Arrow;
        _stopwatch.Stop();
        CompositionTarget.Rendering += CompositionTarget_Rendering;
    }


    private void CompositionTarget_Rendering(object sender, EventArgs e)
    {
        _circlesWithOffset.ToList().ForEach(x => SetCircle(x.Item1, x.Item2));
    }


    private void SetCircle(Ellipse circle, int offset)
    {
        var posOnCircle = _stopwatch.Elapsed.TotalSeconds * Math.PI - Math.PI / 5 * offset;
        var halfWidth = (Width - circle.Width) / 2;
        var halfHeight = (Height - circle.Height) / 2;
        circle.SetValue(Canvas.LeftProperty, halfWidth + Math.Sin(posOnCircle) * halfWidth);
        circle.SetValue(Canvas.TopProperty, halfHeight + -Math.Cos(posOnCircle) * halfHeight);
    }


    private void HandleUnloaded(object sender, RoutedEventArgs e)
    {
        Stop();
    }


    private void HandleVisibleChanged(object sender, DependencyPropertyChangedEventArgs e)
    {
        var isVisible = (bool)e.NewValue;
        if(isVisible)
            Start();
        else
            Stop();
    }

    #endregion Animation

    #region Visibility

    public bool DeferedVisibility
    {
        get => (bool)GetValue(DeferedVisibilityProperty);
        set => SetValue(DeferedVisibilityProperty, value);
    }

    [Obsolete("Please use DeferedVisibility")]
    public new Visibility Visibility
    {
        get => base.Visibility;
        set => base.Visibility = value;
    }


    public override void OnApplyTemplate()
    {
        base.OnApplyTemplate();
        OnDeferedVisibilityChanged();
    }


    private static void OnDeferedVisibilityChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        ((CircularProgressBar)d).OnDeferedVisibilityChanged();
    }


    private void OnDeferedVisibilityChanged()
    {
        if(DeferedVisibility)
        {
            VisualStateManager.GoToState(this, "Visible", true);
#pragma warning disable 618
            Visibility = Visibility.Visible;
#pragma warning restore 618
        } else
        {
            VisualStateManager.GoToState(this, "Collapsed", true);
#pragma warning disable 618
            Visibility = Visibility.Collapsed;
#pragma warning restore 618
        }
    }

    #endregion Visibility
}

XAML:

<UserControl x:Class="MyProject.Views.Controls.Util.CircularProgressBar"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:util="clr-namespace:MyProject.Views.Controls.Util"
             Background="Transparent"
             IsVisibleChanged="HandleVisibleChanged">
    <UserControl.Resources>
        <util:PercentageValueConverter x:Key="PercentageValueConverter"
                                       Scaling="0.2" />
    </UserControl.Resources>

    <Grid x:Name="LayoutRoot"
          Background="Transparent"
          HorizontalAlignment="Stretch"
          VerticalAlignment="Stretch">
        <Canvas RenderTransformOrigin="0.5, 0.5"
                HorizontalAlignment="Stretch"
                VerticalAlignment="Stretch"
                Unloaded="HandleUnloaded">
            <Ellipse x:Name="C0"
                     SnapsToDevicePixels="False"
                     Width="{Binding Width,
                             Mode=OneWay,
                             Converter={StaticResource PercentageValueConverter},
                             RelativeSource={RelativeSource AncestorType={x:Type UserControl}}}"
                     Height="{Binding Height,
                             Mode=OneWay,
                             Converter={StaticResource PercentageValueConverter},
                             RelativeSource={RelativeSource AncestorType={x:Type UserControl}}}"
                     Stretch="Fill"
                     Fill="Black"
                     Opacity="0.9" />
            <Ellipse x:Name="C1"
                     SnapsToDevicePixels="False"
                     Width="{Binding Width,
                             Mode=OneWay,
                             Converter={StaticResource PercentageValueConverter},
                             RelativeSource={RelativeSource AncestorType={x:Type UserControl}}}"
                     Height="{Binding Height,
                             Mode=OneWay,
                             Converter={StaticResource PercentageValueConverter},
                             RelativeSource={RelativeSource AncestorType={x:Type UserControl}}}"
                     Stretch="Fill"
                     Fill="Black"
                     Opacity="0.8" />
            <Ellipse x:Name="C2"
                     SnapsToDevicePixels="False"
                     Width="{Binding Width,
                             Mode=OneWay,
                             Converter={StaticResource PercentageValueConverter},
                             RelativeSource={RelativeSource AncestorType={x:Type UserControl}}}"
                     Height="{Binding Height,
                             Mode=OneWay,
                             Converter={StaticResource PercentageValueConverter},
                             RelativeSource={RelativeSource AncestorType={x:Type UserControl}}}"
                     Stretch="Fill"
                     Fill="Black"
                     Opacity="0.7" />
            <Ellipse x:Name="C3"
                     SnapsToDevicePixels="False"
                     Width="{Binding Width,
                             Mode=OneWay,
                             Converter={StaticResource PercentageValueConverter},
                             RelativeSource={RelativeSource AncestorType={x:Type UserControl}}}"
                     Height="{Binding Height,
                             Mode=OneWay,
                             Converter={StaticResource PercentageValueConverter},
                             RelativeSource={RelativeSource AncestorType={x:Type UserControl}}}"
                     Stretch="Fill"
                     Fill="Black"
                     Opacity="0.6" />
            <Ellipse x:Name="C4"
                     SnapsToDevicePixels="False"
                     Width="{Binding Width,
                             Mode=OneWay,
                             Converter={StaticResource PercentageValueConverter},
                             RelativeSource={RelativeSource AncestorType={x:Type UserControl}}}"
                     Height="{Binding Height,
                             Mode=OneWay,
                             Converter={StaticResource PercentageValueConverter},
                             RelativeSource={RelativeSource AncestorType={x:Type UserControl}}}"
                     Stretch="Fill"
                     Fill="Black"
                     Opacity="0.5" />
            <Ellipse x:Name="C5"
                     SnapsToDevicePixels="False"
                     Width="{Binding Width,
                             Mode=OneWay,
                             Converter={StaticResource PercentageValueConverter},
                             RelativeSource={RelativeSource AncestorType={x:Type UserControl}}}"
                     Height="{Binding Height,
                             Mode=OneWay,
                             Converter={StaticResource PercentageValueConverter},
                             RelativeSource={RelativeSource AncestorType={x:Type UserControl}}}"
                     Stretch="Fill"
                     Fill="Black"
                     Opacity="0.4" />
            <Ellipse x:Name="C6"
                     SnapsToDevicePixels="False"
                     Width="{Binding Width,
                             Mode=OneWay,
                             Converter={StaticResource PercentageValueConverter},
                             RelativeSource={RelativeSource AncestorType={x:Type UserControl}}}"
                     Height="{Binding Height,
                             Mode=OneWay,
                             Converter={StaticResource PercentageValueConverter},
                             RelativeSource={RelativeSource AncestorType={x:Type UserControl}}}"
                     Stretch="Fill"
                     Fill="Black"
                     Opacity="0.3" />
            <Ellipse x:Name="C7"
                     SnapsToDevicePixels="False"
                     Width="{Binding Width,
                             Mode=OneWay,
                             Converter={StaticResource PercentageValueConverter},
                             RelativeSource={RelativeSource AncestorType={x:Type UserControl}}}"
                     Height="{Binding Height,
                             Mode=OneWay,
                             Converter={StaticResource PercentageValueConverter},
                             RelativeSource={RelativeSource AncestorType={x:Type UserControl}}}"
                     Stretch="Fill"
                     Fill="Black"
                     Opacity="0.2" />
            <Ellipse x:Name="C8"
                     SnapsToDevicePixels="False"
                     Width="{Binding Width,
                             Mode=OneWay,
                             Converter={StaticResource PercentageValueConverter},
                             RelativeSource={RelativeSource AncestorType={x:Type UserControl}}}"
                     Height="{Binding Height,
                             Mode=OneWay,
                             Converter={StaticResource PercentageValueConverter},
                             RelativeSource={RelativeSource AncestorType={x:Type UserControl}}}"
                     Stretch="Fill"
                     Fill="Black"
                     Opacity="0.1" />
        </Canvas>
    </Grid>
</UserControl>

该转换器是只那里到圆的直径设定为控制的大小的20%。

这是如何在任何地方使用的控制

<util:CircularProgressBar Grid.Row="1"
                          DeferedVisibility="True"
                          Width="32"
                          Height="32" />

正如你所看到的圆圈位置获取与CompositionTarget.Rendering事件更新。

我已经尝试设置SnapsToDevicePixels为false,但这并没有改变任何东西。对于位置计算有使用双所以不应该有任何舍入误差。

c# wpf
1个回答
1
投票

这个答案可能与当前的代码解决您的问题,是相当的建议。该XAML有一些巧妙的功能和使用得当,可以实现很多毫不费力。

例如:可以定义一个Style为其设置一个UIElement的位置,优选与初始旋转一个中StartPosition一个Ellipse。然后用这个Style元素添加到某种集装箱和旋转整个容器,使它看起来像一个Ellipses在移动。理想的情况下使用它处理Ellipse的缩放的容器。

风格LoadingCircles

<!-- LoadingCircles Style for a Control-Element-->
<Style TargetType="{x:Type Control}" x:Key="LoadingCircles">
    <!-- Set default values (can be overridden) -->
    <Setter Property="Foreground" Value="Black"/>
    <Setter Property="Tag" Value="20"/>
    <!-- Hide Control when its not enabled -->
    <Setter Property="Visibility" Value="Collapsed"/>
    <!-- Define the lok of the Control -->
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate>
                <!-- Use ViewBox for auto-scaling -->
                <Viewbox Stretch="Fill">
                    <!-- Set Grid Size to absolute value to scale on 100% (like Circle Size = 20 -> 20%) -->
                    <Grid Height="100" Width="100" RenderTransformOrigin="0.5,0.5">
                        <Grid.Resources>
                            <!-- Define Template for Circle on a circular path whereas the Tag defines the initial Rotation (0 = top, 180 = bottom) -->
                            <Style TargetType="{x:Type ContentPresenter}">
                                <Setter Property="DataContext" Value="{Binding}"/>
                                <Setter Property="ContentTemplate">
                                    <Setter.Value>
                                        <DataTemplate>
                                            <Border Height="100">
                                                <Border.LayoutTransform>
                                                    <RotateTransform Angle="{Binding Tag, RelativeSource={RelativeSource TemplatedParent}}"/>
                                                </Border.LayoutTransform>
                                                <Ellipse Width="{Binding Tag, RelativeSource={RelativeSource AncestorType=Control}}" Height="{Binding Tag, RelativeSource={RelativeSource AncestorType=Control}}" Fill="{Binding Foreground, RelativeSource={RelativeSource AncestorType=Control}}" VerticalAlignment="Top"/>
                                            </Border>
                                        </DataTemplate>
                                    </Setter.Value>
                                </Setter>
                            </Style>
                        </Grid.Resources>

                        <!-- Add Circles to the circular Path with their start-roation and opacity -->
                        <ContentPresenter Opacity="0.1" Tag="36"/>
                        <ContentPresenter Opacity="0.2" Tag="72"/>
                        <ContentPresenter Opacity="0.3" Tag="108"/>
                        <ContentPresenter Opacity="0.4" Tag="144"/>
                        <ContentPresenter Opacity="0.5" Tag="180"/>
                        <ContentPresenter Opacity="0.6" Tag="216"/>
                        <ContentPresenter Opacity="0.7" Tag="252"/>
                        <ContentPresenter Opacity="0.8" Tag="288"/>
                        <ContentPresenter Opacity="0.9" Tag="324"/>
                        <ContentPresenter Opacity="1" Tag="0"/>

                        <!-- Define Roation for all the Circles in the "Container" -->
                        <Grid.RenderTransform>
                            <RotateTransform Angle="0" x:Name="AngleEverything"/>
                        </Grid.RenderTransform>

                    </Grid>
                </Viewbox>

                <!-- Define Trigger when the Control is enabled -->
                <ControlTemplate.Triggers>
                    <Trigger Property="IsEnabled" Value="True">
                        <!-- When set, start the "Container" rotation -->
                        <Trigger.EnterActions>
                            <BeginStoryboard x:Name="Rotation">
                                <Storyboard RepeatBehavior="Forever">
                                    <DoubleAnimation Storyboard.TargetName="AngleEverything" Storyboard.TargetProperty="Angle" From="0" To="359" Duration="00:00:03"/>
                                </Storyboard>
                            </BeginStoryboard>
                        </Trigger.EnterActions>
                        <!-- when unset, stop the "Container" rotation -->
                        <Trigger.ExitActions>
                            <StopStoryboard Storyboard.TargetName="Rotation"/>
                        </Trigger.ExitActions>
                        <!-- Show control when it is enabled (otherwise hide, see Setter at the top) -->
                        <Setter Property="Visibility" Value="Visible"/>
                    </Trigger>
                </ControlTemplate.Triggers>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

笔记

移动圈的Size设置与Tag Property为百分比(20对应于20%)。

圆的Color设置与Foreground Property

控制的VisibilityIsEnabled Property处理。当设置为False的LoadingCircles被隐藏。

用法示例

<!-- LoadingCircles with 15% size and purple color -->
<Control Style="{DynamicResource LoadingCircles}" IsEnabled="{Binding YourSource}" Foreground="Purple" Tag="15" ... />

个人说明

正如你所看到的,它运行流畅而无需CustomControlor的任何代码隐藏(这是一个有点自定义)。我强烈建议你采取的XAML功能细看得到有效的结果。

© www.soinside.com 2019 - 2024. All rights reserved.