RError.com

RError.com Logo RError.com Logo

RError.com Navigation

  • 主页

Mobile menu

Close
  • 主页
  • 系统&网络
    • 热门问题
    • 最新问题
    • 标签
  • Ubuntu
    • 热门问题
    • 最新问题
    • 标签
  • 帮助
主页 / 问题 / 1543070
Accepted
Igor Pawus
Igor Pawus
Asked:2023-09-29 01:11:46 +0000 UTC2023-09-29 01:11:46 +0000 UTC 2023-09-29 01:11:46 +0000 UTC

如何正确调整用户控件的大小?

  • 772

我正在学习 WPF,但我对 MeasureOverride 和 ArrangeOverride 的定义很困惑。我写了一个圆形进度条,但由于初始化期间的某种原因,如果您每次不指定高度和/或宽度,那么您最终可能会得到另一种形状,而不是椭圆形。如果我理解正确的话,那么理想情况下它应该填充所有可用空间。如果这是错误的,请纠正我。

用户控件本身如下所示:

<UserControl x:Class="Round_progress_bar.UserProgBar"
         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
         xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
         xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
         Name="UserProgressBar"
         mc:Ignorable="d" 
         d:DesignHeight="100" d:DesignWidth="100">
<Canvas x:Name="canv">
    <Path x:Name="path_fon" Fill="{Binding ElementName=UserProgressBar, Path=HighColor}" Canvas.Left="0" Canvas.Top="0">
        <!--фон-->
        <Path.Data>
            <EllipseGeometry x:Name="fon_ellipse" Center="50, 50" RadiusX="50" RadiusY="50"/>
        </Path.Data>
    </Path>
    <Path Fill="{Binding ElementName=UserProgressBar, Path=LowColor}" Canvas.Left="0" Canvas.Top="0"> // из-за того, что при обращении к неприсвоенному значению Canvas.Left можно получить исключение, добавил нули
        <!--вертим-->
        <Path.Data>
            <PathGeometry>
                <PathFigure StartPoint="100,50" x:Name="din_path">
                    <LineSegment Point="50,50" x:Name="horiz_line"/>
                    <LineSegment x:Name="run_point" Point="50,0"/>
                    <!--двигается-->
                    <ArcSegment x:Name="duga" Point="100,50" Size="50,50" SweepDirection="Clockwise" IsLargeArc="False" IsStroked="True"/>
                </PathFigure>
            </PathGeometry>
        </Path.Data>
    </Path>
    <Path x:Name="path_hole" Fill="{Binding ElementName=UserProgressBar, Path=CenterColor}" Canvas.Left="0" Canvas.Top="0">
        <!--дыра-->
        <Path.Data>
            <EllipseGeometry x:Name="hole_ellipse" Center="50, 50" RadiusX="40" RadiusY="40"/>
        </Path.Data>
    </Path>
</Canvas>

那些。有 2 个椭圆,一个在后面,一个在前面,中间有一个 PathGeometry。其中,有一条水平的固定线,它连接到一条可移动的线,该可移动的线的一端沿着椭圆移动。它们被 ArcSegment 关闭。我对 PathGeometry 没有直接的疑问;这个图是按预期绘制的。有时他会把椭圆扔到一边,有时他会画正方形。

我尝试尽早设置渲染。为此,我将部分代码从 ArrangeOverride 移至单独的方法中,并尝试从构造函数和 ArrangeOverride 中调用,但这没有帮助。

    protected override Size MeasureOverride(Size constraint)
    {
        double min_size = 2 * ThicknessBar;

        if (constraint.Height < min_size)
        {
            constraint.Height = min_size;
        }
        if (constraint.Width < min_size)
        {
            constraint.Width = min_size;
        }

        return base.MeasureOverride(constraint);
    }

    protected override Size ArrangeOverride(Size arrangeBounds)
    {
        a = arrangeBounds.Width / 2;
        b = arrangeBounds.Height / 2;

        Moulding();

        return base.ArrangeOverride(arrangeBounds);
    }

    private void Moulding()
    {
        double to_a = 2 * a;
        fon_ellipse.RadiusX = a;
        fon_ellipse.RadiusY = b;
        double thik = ThicknessBar;
        hole_ellipse.RadiusX = a - thik;
        hole_ellipse.RadiusY = b - thik;

        din_path.StartPoint = new Point(to_a, b);
        horiz_line.Point = new Point(a, b);

        duga.Point = new Point(to_a, b);
        duga.Size = new Size(a, b);

        //Canvas.SetLeft(canv.Children[0] as Path, a / 2); - подобные попытки улучшали ситуцию
        //Canvas.SetTop(canv.Children[0] as Path, b / 4);    в одних случаях, но портили в
        //Canvas.SetLeft(canv.Children[1] as Path, a / 2);   других
        //Canvas.SetTop(canv.Children[1] as Path, b / 2);
        //Canvas.SetLeft(canv.Children[2] as Path, a / 2);
        //Canvas.SetTop(canv.Children[2] as Path, b / 4);

        Position();
    }

为了澄清这一点,我设置了一个依赖属性来监视第二个椭圆的半径:

    private static void OnThicknessBarChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        UserProgBar progBar = d as UserProgBar;
        double fon_x = progBar.fon_ellipse.RadiusX;
        double fon_y = progBar.fon_ellipse.RadiusY;

        progBar.hole_ellipse.RadiusX = fon_x - (double)e.NewValue;
        progBar.hole_ellipse.RadiusY = fon_y - (double)e.NewValue;
    }

因此,所有这些都导致了 gif 中显示的结果: 包含所有元素的 GIF

GIF,其中一个椭圆被赋予透明颜色

我已经不知道为什么会这样了。请告诉我下一步该走哪条路。

UPD。突然我明白了如何解决这个问题。我添加字段纯粹是为了诊断目的,现在可以将其删除。我在工作时追踪了他们的价值观:

1)

<TextBlock Grid.Column="2" Grid.Row="0">
        <Run>DinCenter {</Run>
        <Run x:Name="dinct_x"></Run>
        <Run>, </Run>
        <Run x:Name="dinct_y"></Run>
        <Run>}</Run>
        <LineBreak/>
        <Run>FonCenter {</Run>
        <Run x:Name="fonct_x"/>
        <Run>, </Run>
        <Run x:Name="fonct_y"/>
        <Run>}</Run>
    </TextBlock>
dinct_x.Text = Convert.ToString(progressbar.DinCenter.X);
dinct_y.Text = Convert.ToString(progressbar.DinCenter.Y);
fonct_x.Text = Convert.ToString(progressbar.FonCenter.X);
fonct_y.Text = Convert.ToString(progressbar.FonCenter.Y);
  1. 还有田地本身

     private Point dincenter;
     private Point foncenter;
     public Point DinCenter
     {
         get { return dincenter; }
         set
         {
             dincenter = value;
             OnPropertyChanged("DinCenter");
         }
     }
    
     public Point FonCenter
     {
         get { return foncenter; }
         set
         {
             foncenter = value;
             OnPropertyChanged("FonCenter");
         }
     }
    
     public event PropertyChangedEventHandler PropertyChanged;
     protected void OnPropertyChanged([CallerMemberName] string prop = null)
     {
         PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(prop));
     }
    

现在发生的事情已经很清楚了。首先,正如评论中向我建议的那样,我需要检查画布的大小。添加了内部 ArrangeOverride

        canv.Width = arrangeBounds.Width;
        canv.Height = arrangeBounds.Height;

现在,当启动时,椭圆看起来像椭圆,而不是无形的东西。其次,我发现了自己的错误。对于 PathGeometry,我指定了所有坐标,但对于椭圆,仅指定了其中的一部分。显然编译器不明白,如果椭圆的 RadiusX = 50 和 RadiusY = 50,那么它的中心应该在点 50, 50 处。

        din_path.StartPoint = new Point(to_a, b);
        horiz_line.Point = new Point(a, b);
        fon_ellipse.Center = new Point(a, b);
        hole_ellipse.Center = new Point(a, b);

当然,这浪费了很多时间,这很遗憾,但我学到了替代方法。然后我肯定会尝试使用 ViewBox 和私有依赖属性的选项。我希望我的写作对某人有用。再次感谢所有回答我的人。 最后结果

c#
  • 1 1 个回答
  • 82 Views

1 个回答

  • Voted
  1. Best Answer
    aepot
    2023-09-29T21:21:16Z2023-09-29T21:21:16Z
    • 为了解决尺寸问题,我建议巧妙地将画布尺寸绑定到用户控件。
    • 为了解决当一条曲线叠加在另一条曲线上时显示出不同颜色的轮廓的问题,我建议使用两条ArcSegment简单地相互延续而不是相互重叠的曲线。
    • 为了避免在中间画一个圆圈,我建议使用Stroke而不是填充。同时,中心将是透明的,如果使用彩色背景,这会很方便。
    • Arrange/Measure一般来说,有机会不碰最好不要碰。
    • ViewBox可以使计算点的坐标变得更容易,但是您必须四处跳舞,Stroke以便它根据当前的缩放比例与像素大小相匹配ViewBox。试想一下,您设置了 10 个像素,但看到的是 20,或者例如 5。在当前的设计中,您可以以像素为单位设置条带的高度、宽度和厚度,这看起来很合乎逻辑。

    例子

    <UserControl x:Class="WpfAppProgressRing.ProgressRing"
                 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                 xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
                 xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
                 xmlns:local="clr-namespace:WpfAppProgressRing"
                 mc:Ignorable="d" 
                 d:DesignHeight="100" d:DesignWidth="100"
                 x:Name="Ring">
        <UserControl.Resources>
            <local:MinConverter x:Key="MinConverter"/>
            <Style TargetType="{x:Type local:ProgressRing}">
                <Setter Property="Template">
                    <Setter.Value>
                        <ControlTemplate TargetType="{x:Type local:ProgressRing}">
                            <Canvas MinWidth="{TemplateBinding MinWidth}" MinHeight="{TemplateBinding MinHeight}" MaxWidth="{TemplateBinding MaxWidth}" MaxHeight="{TemplateBinding MaxHeight}">
                                <Canvas.Width>
                                    <MultiBinding Converter="{StaticResource MinConverter}">
                                        <Binding Path="ActualWidth" RelativeSource="{RelativeSource TemplatedParent}"/>
                                        <Binding Path="ActualHeight" RelativeSource="{RelativeSource TemplatedParent}"/>
                                    </MultiBinding>
                                </Canvas.Width>
                                <Canvas.Height>
                                    <MultiBinding Converter="{StaticResource MinConverter}">
                                        <Binding Path="ActualWidth" RelativeSource="{RelativeSource TemplatedParent}"/>
                                        <Binding Path="ActualHeight" RelativeSource="{RelativeSource TemplatedParent}"/>
                                    </MultiBinding>
                                </Canvas.Height>
                                <Path Stroke="{TemplateBinding Background}" StrokeThickness="{TemplateBinding Stroke}">
                                    <Path.Data>
                                        <PathGeometry>
                                            <PathFigure StartPoint="{Binding ProgressPoint, ElementName=Ring}">
                                                <ArcSegment Size="{Binding ArcSize, ElementName=Ring}" IsLargeArc="{Binding BackgroundLarge, ElementName=Ring}" SweepDirection="Clockwise" Point="{Binding StartPoint, ElementName=Ring}" />
                                            </PathFigure>
                                        </PathGeometry>
                                    </Path.Data>
                                </Path>
                                <Path Stroke="{TemplateBinding Foreground}" StrokeThickness="{TemplateBinding Stroke}">
                                    <Path.Data>
                                        <PathGeometry>
                                            <PathFigure StartPoint="{Binding StartPoint, ElementName=Ring}" >
                                                <ArcSegment Size="{Binding ArcSize, ElementName=Ring}" IsLargeArc="{Binding ForegroundLarge, ElementName=Ring}" SweepDirection="Clockwise" Point="{Binding ProgressPoint, ElementName=Ring}" />
                                            </PathFigure>
                                        </PathGeometry>
                                    </Path.Data>
                                </Path>
                            </Canvas>
                        </ControlTemplate>
                    </Setter.Value>
                </Setter>
            </Style>
        </UserControl.Resources>
    </UserControl>
    

    一切都是在DP上完成的,所以我很少使用它x:Name。

    正如你所看到的,我使用这样的转换器:

    public class MinConverter : IMultiValueConverter
    {
        public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
        {
            if (values.Length == 2 && values[0] is double width && values[1] is double height)
                return Math.Min(width, height);
            return default(double);
        }
    
        public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
        {
            throw new NotSupportedException();
        }
    }
    

    这是代码本身,所有数学都减少到最低限度

    public partial class ProgressRing : UserControl
    {
        public static readonly DependencyProperty StrokeProperty = DependencyProperty.Register(nameof(Stroke), typeof(double), typeof(ProgressRing), new PropertyMetadata(10.0, OnStrokeChanged));
        public static readonly DependencyProperty ProgressProperty = DependencyProperty.Register(nameof(Progress), typeof(double), typeof(ProgressRing), new PropertyMetadata(0.0, OnProgressChanged));
        private static readonly DependencyProperty ArcSizeProperty = DependencyProperty.Register(nameof(ArcSize), typeof(Size), typeof(ProgressRing), new PropertyMetadata(new Size()));
        private static readonly DependencyProperty StartPointProperty = DependencyProperty.Register(nameof(StartPoint), typeof(Point), typeof(ProgressRing), new PropertyMetadata(new Point()));
        private static readonly DependencyProperty ProgressPointProperty = DependencyProperty.Register(nameof(ProgressPoint), typeof(Point), typeof(ProgressRing), new PropertyMetadata(new Point()));
        private static readonly DependencyProperty ForegroundLargeProperty = DependencyProperty.Register(nameof(ForegroundLarge), typeof(bool), typeof(ProgressRing), new PropertyMetadata(false));
        private static readonly DependencyProperty BackgroundLargeProperty = DependencyProperty.Register(nameof(BackgroundLarge), typeof(bool), typeof(ProgressRing), new PropertyMetadata(true));
    
        public double Stroke
        { 
            get => (double)GetValue(StrokeProperty);
            set => SetValue(StrokeProperty, value);
        }
    
        public double Progress
        {
            get => (double)GetValue(ProgressProperty);
            set => SetValue(ProgressProperty, value);
        }
    
        private Size ArcSize
        {
            get => (Size)GetValue(ArcSizeProperty);
            set => SetValue(ArcSizeProperty, value);
        }
    
        private Point StartPoint
        {
            get => (Point)GetValue(StartPointProperty);
            set => SetValue(StartPointProperty, value);
        }
    
        private Point ProgressPoint
        {
            get => (Point)GetValue(ProgressPointProperty);
            set => SetValue(ProgressPointProperty, value);
        }
    
        private bool ForegroundLarge
        {
            get => (bool)GetValue(ForegroundLargeProperty);
            set => SetValue(ForegroundLargeProperty, value);
        }
    
        private bool BackgroundLarge
        {
            get => (bool)GetValue(BackgroundLargeProperty);
            set => SetValue(BackgroundLargeProperty, value);
        }
    
        public ProgressRing()
        {
            InitializeComponent();
        }
    
        private void RecalcProgress()
        {
            double size = ArcSize.Width;
            double offset = size + Stroke / 2.0;
            double progress = (Math.Clamp(Progress, 0.0, 99.9999) - 25.0) / 50.0 * Math.PI;
            Point progressPoint = new(Math.Cos(progress) * size + offset, Math.Sin(progress) * size + offset);
            bool switchLarge = progressPoint.X < StartPoint.X;
    
            ProgressPoint = progressPoint;
            if (ForegroundLarge != switchLarge)
            {
                ForegroundLarge = switchLarge;
                BackgroundLarge = !switchLarge;
            }
        }
    
        private void RecalcSize(double width, double height)
        {
            double halfSize = Math.Min(width, height) / 2.0;
            double halfStroke = Stroke / 2.0;
            double arcSize = Math.Max(halfSize - halfStroke, 0.0);
            ArcSize = new Size(arcSize, arcSize);
            StartPoint = new Point(halfSize, halfStroke);
            RecalcProgress();
        }
    
        private static void OnProgressChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            ProgressRing ring = (ProgressRing)d;
            ring.RecalcProgress();
        }
    
        private static void OnStrokeChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            ProgressRing ring = (ProgressRing)d;
            double width = ring.ActualWidth;
            double height = ring.ActualHeight;
            if (width > 0 && height > 0)
                ring.RecalcSize(width, height);
        }
    
        protected override void OnRenderSizeChanged(SizeChangedInfo sizeInfo)
        {
            RecalcSize(sizeInfo.NewSize.Width, sizeInfo.NewSize.Height);
            base.OnRenderSizeChanged(sizeInfo);
        }
    }
    

    使用示例

    为了简化示例,我将使用 codebyhind 编写它

    <Window x:Class="WpfAppProgressRing.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
            xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
            xmlns:local="clr-namespace:WpfAppProgressRing"
            mc:Ignorable="d"
            Title="MainWindow" Height="450" Width="800">
        <Grid>
            <local:ProgressRing Stroke="100" x:Name="ProgressRing" Foreground="Magenta" Background="Cyan" d:Progress="15"/>
        </Grid>
    </Window>
    
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            StartProgress();
        }
    
        private async void StartProgress()
        {
            double progress = 0.0;
            while (progress < 100.0)
            {
                progress += 0.1;
                ProgressRing.Progress = progress;
                await Task.Delay(10);
            }
        }
    }
    

    这就是它的样子

    在此输入图像描述

    根据宽度和高度的最小值将环尺寸调整为容器尺寸(这就是上面显示的转换器的用途)。

    我不装天才,也许私人DP不太好,但是没有选拔通过Children。

    • 1

相关问题

  • 使用嵌套类导出 xml 文件

  • 分层数据模板 [WPF]

  • 如何在 WPF 中为 ListView 手动创建列?

  • 在 2D 空间中,Collider 2D 挂在玩家身上,它对敌人的重量相同,我需要它这样当它们碰撞时,它们不会飞向不同的方向。统一

  • 如何在 c# 中使用 python 神经网络来创建语音合成?

  • 如何知道类中的方法是否属于接口?

Sidebar

Stats

  • 问题 10021
  • Answers 30001
  • 最佳答案 8000
  • 用户 6900
  • 常问
  • 回答
  • Marko Smith

    我看不懂措辞

    • 1 个回答
  • Marko Smith

    请求的模块“del”不提供名为“default”的导出

    • 3 个回答
  • Marko Smith

    "!+tab" 在 HTML 的 vs 代码中不起作用

    • 5 个回答
  • Marko Smith

    我正在尝试解决“猜词”的问题。Python

    • 2 个回答
  • Marko Smith

    可以使用哪些命令将当前指针移动到指定的提交而不更改工作目录中的文件?

    • 1 个回答
  • Marko Smith

    Python解析野莓

    • 1 个回答
  • Marko Smith

    问题:“警告:检查最新版本的 pip 时出错。”

    • 2 个回答
  • Marko Smith

    帮助编写一个用值填充变量的循环。解决这个问题

    • 2 个回答
  • Marko Smith

    尽管依赖数组为空,但在渲染上调用了 2 次 useEffect

    • 2 个回答
  • Marko Smith

    数据不通过 Telegram.WebApp.sendData 发送

    • 1 个回答
  • Martin Hope
    Alexandr_TT 2020年新年大赛! 2020-12-20 18:20:21 +0000 UTC
  • Martin Hope
    Alexandr_TT 圣诞树动画 2020-12-23 00:38:08 +0000 UTC
  • Martin Hope
    Air 究竟是什么标识了网站访问者? 2020-11-03 15:49:20 +0000 UTC
  • Martin Hope
    Qwertiy 号码显示 9223372036854775807 2020-07-11 18:16:49 +0000 UTC
  • Martin Hope
    user216109 如何为黑客设下陷阱,或充分击退攻击? 2020-05-10 02:22:52 +0000 UTC
  • Martin Hope
    Qwertiy 并变成3个无穷大 2020-11-06 07:15:57 +0000 UTC
  • Martin Hope
    koks_rs 什么是样板代码? 2020-10-27 15:43:19 +0000 UTC
  • Martin Hope
    Sirop4ik 向 git 提交发布的正确方法是什么? 2020-10-05 00:02:00 +0000 UTC
  • Martin Hope
    faoxis 为什么在这么多示例中函数都称为 foo? 2020-08-15 04:42:49 +0000 UTC
  • Martin Hope
    Pavel Mayorov 如何从事件或回调函数中返回值?或者至少等他们完成。 2020-08-11 16:49:28 +0000 UTC

热门标签

javascript python java php c# c++ html android jquery mysql

Explore

  • 主页
  • 问题
    • 热门问题
    • 最新问题
  • 标签
  • 帮助

Footer

RError.com

关于我们

  • 关于我们
  • 联系我们

Legal Stuff

  • Privacy Policy

帮助

© 2023 RError.com All Rights Reserve   沪ICP备12040472号-5