我正在学习 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;
}
我已经不知道为什么会这样了。请告诉我下一步该走哪条路。
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);
还有田地本身
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 和私有依赖属性的选项。我希望我的写作对某人有用。再次感谢所有回答我的人。
ArcSegment
简单地相互延续而不是相互重叠的曲线。Stroke
而不是填充。同时,中心将是透明的,如果使用彩色背景,这会很方便。Arrange
/Measure
一般来说,有机会不碰最好不要碰。ViewBox
可以使计算点的坐标变得更容易,但是您必须四处跳舞,Stroke
以便它根据当前的缩放比例与像素大小相匹配ViewBox
。试想一下,您设置了 10 个像素,但看到的是 20,或者例如 5。在当前的设计中,您可以以像素为单位设置条带的高度、宽度和厚度,这看起来很合乎逻辑。例子
一切都是在DP上完成的,所以我很少使用它
x:Name
。正如你所看到的,我使用这样的转换器:
这是代码本身,所有数学都减少到最低限度
使用示例
为了简化示例,我将使用 codebyhind 编写它
这就是它的样子
根据宽度和高度的最小值将环尺寸调整为容器尺寸(这就是上面显示的转换器的用途)。
我不装天才,也许私人DP不太好,但是没有选拔通过
Children
。