WPF自定义绘图神秘绘图错误
WPF custom drawing mysterious drawing error
我有一个直接绘制到自定义 canvas 的控件。自定义控件根据物理尺寸计算绘制多大的圆。无论我使用 Ellipse XAML 元素还是自定义绘制控件,我看到的行为都是相同的。
基本上,一旦元素的大小变得足够大,大于 canvas 的最小尺寸,它的绘制位置就会偏离剪裁矩形。最终结果是中心点(由不同的控件绘制)是正确的,但是圆被移动了——并且边缘被剪掉了。
示例:
只要我计算的中心和实际的中心在一条直线上,控件就可以正常渲染了。我在任何地方都找不到明确设置剪辑的地方。
我的问题是2折:
- 怎么回事?
怎样才能准确渲染到排列区域的中心?我使用以下方法计算中心点:
Point center = new Point( RenderSize.Width / 2, RenderSize.Height / 2);
大多数情况下都有效,但当圆超过一定大小时就不行了。
圆控制代码:
public class CirclePoint : UIElement
{
// field
double radius;
// additional properties
MetersPerPixel -- set by container, attached property, affects measure
RadiusInMeters -- set by application, affects measure
FillColor -- set by application, affects render
ObjectColor -- set by applicaiton, affects render
StrokeThickness -- set by application, affects measure, render
Location -- center point, set by application, affects layout
protected override Size MeasureOverride(Size constraint)
{
base.MeasureOverride(constraint);
radius = RadiusInMeters / MetersPerPixel;
double halfPenWidth = StrokThickness / 2;
double diameter = 2 * (radius + halfPenWidth);
return new Size(diameter, diameter);
}
protected override void OnRender(DrawingContext drawingContext)
{
base.OnRender(drawingContext);
SolidColorBrush fillBrush = // from FillColor, frozen
SolidColorBrush edgeBrush = // from ObjectColor, frozen
Pen edgePen = // from edgeBrush, StrokeThickness, frozen
double halfPenWidth = StrokeThickness / 2;
drawingContext.DrawEllipse(fillBrush, edgePen,
new Point(RenderSize.Width / 2, RenderSize.Height / 2),
radius - halfPenWidth, radius - halfPenWidth);
}
}
抱歉使用缩写符号,但我正在尝试用相关信息总结样板代码。
自定义面板要复杂得多,因为它负责消除标签冲突等,但相关信息在这里:
public class PhysicalPane : Pane
{
// Pertinent Properties
PhysicalArea // affects layout
protected override Size MeasureOverride(Size constraint)
{
Size screen = new Screen(ActualWidth, ActualHeight);
double metersPerDisplayUnit = // calculation based on screen and other context
Size newDesiredSize = // from constraint, adjusting for double.Infinity
foreach(UIElement child in InternalChildren)
{
child.Measure(constraint);
SetMetersPerPixel(child, metersPerDisplayUnit);
newDesiredSize.Width = Math.Max(newDesiredSize.Width, child.DesiredSize.Width);
newDesiredSize.Height = Math.Max(newDesiredSize.Height, child.DesiredSize.Height);
}
return newDesiredSize;
}
protected override Size ArrangeOverride(Size finalSize)
{
foreach(UIElement child in InternalChildren)
{
Location childLocation = GetLocation(child);
// LocationToPoint is very well tested.
Point displayPoint = LocationToPoint(childLocation, PhysicalArea, finalSize);
displayPoint.X -= child.DesiredSize.Width / 2;
displayPoint.Y -= child.DesiredSize.Height / 2;
Rect locationRect = new Rect(displayPoint, child.DesiredSize);
child.Arrange(locationRect);
}
return finalSize;
}
}
原来圆圈的位置放错了,因为DesiredSize和RenderSize不一样。为了修复我的 CirclePoint
中的位置,我必须像这样更改中心点逻辑:
double xOffset = (DesiredSize.Width - RenderSize.Width) / 2;
double yOffset = (DesiredSize.Width - RenderSize.Width) / 2;
Point center = new Point(xOffset + radius, yOffset + radius);
这个解决方案还有一个问题:
- 我的圈子仍然明显被剪掉了。
这应该return UIElement 的中心:
Point topLeftCorner = yourEllipse.PointToScreen(new Point(0, 0));//Get the point of the topleft corner of your UIElement
Point center = new Point(topLeftCorner.X + yourEllipse.Width / 2, topLeftCorner.Y + yourEllipse.Height / 2);//The center of your ellipse
事实证明,我的定位问题的答案是,一旦子容器在任何维度上都大于父容器,DesiredSize 和 RenderSize 之间就会存在差异。我不得不像这样调整我的 OnRender
方法:
protected override void OnRender(DrawingContext drawingContext)
{
base.OnRender(drawingContext);
SolidColorBrush fillBrush = // from FillColor, frozen
SolidColorBrush edgeBrush = // from ObjectColor, frozen
Pen edgePen = // from edgeBrush, StrokeThickness, frozen
double halfPenWidth = StrokeThickness / 2;
double xOffset = (DesiredSize.Width - RenderSize.Width) / 2;
double yOffset = (DesiredSize.Width - RenderSize.Width) / 2;
Point center = new Point(xOffset + radius, yOffset + radius);
drawingContext.DrawEllipse(fillBrush, edgePen, center,
radius - halfPenWidth, radius - halfPenWidth);
}
无论圆相对于父级有多大,都可以在正确的位置绘制圆。
我的剪辑问题是由于布局窗格有 ClipToBounds=True
。那完全是另一个问题。
我有一个直接绘制到自定义 canvas 的控件。自定义控件根据物理尺寸计算绘制多大的圆。无论我使用 Ellipse XAML 元素还是自定义绘制控件,我看到的行为都是相同的。
基本上,一旦元素的大小变得足够大,大于 canvas 的最小尺寸,它的绘制位置就会偏离剪裁矩形。最终结果是中心点(由不同的控件绘制)是正确的,但是圆被移动了——并且边缘被剪掉了。
示例:
只要我计算的中心和实际的中心在一条直线上,控件就可以正常渲染了。我在任何地方都找不到明确设置剪辑的地方。
我的问题是2折:
- 怎么回事?
怎样才能准确渲染到排列区域的中心?我使用以下方法计算中心点:
Point center = new Point( RenderSize.Width / 2, RenderSize.Height / 2);
大多数情况下都有效,但当圆超过一定大小时就不行了。
圆控制代码:
public class CirclePoint : UIElement
{
// field
double radius;
// additional properties
MetersPerPixel -- set by container, attached property, affects measure
RadiusInMeters -- set by application, affects measure
FillColor -- set by application, affects render
ObjectColor -- set by applicaiton, affects render
StrokeThickness -- set by application, affects measure, render
Location -- center point, set by application, affects layout
protected override Size MeasureOverride(Size constraint)
{
base.MeasureOverride(constraint);
radius = RadiusInMeters / MetersPerPixel;
double halfPenWidth = StrokThickness / 2;
double diameter = 2 * (radius + halfPenWidth);
return new Size(diameter, diameter);
}
protected override void OnRender(DrawingContext drawingContext)
{
base.OnRender(drawingContext);
SolidColorBrush fillBrush = // from FillColor, frozen
SolidColorBrush edgeBrush = // from ObjectColor, frozen
Pen edgePen = // from edgeBrush, StrokeThickness, frozen
double halfPenWidth = StrokeThickness / 2;
drawingContext.DrawEllipse(fillBrush, edgePen,
new Point(RenderSize.Width / 2, RenderSize.Height / 2),
radius - halfPenWidth, radius - halfPenWidth);
}
}
抱歉使用缩写符号,但我正在尝试用相关信息总结样板代码。
自定义面板要复杂得多,因为它负责消除标签冲突等,但相关信息在这里:
public class PhysicalPane : Pane
{
// Pertinent Properties
PhysicalArea // affects layout
protected override Size MeasureOverride(Size constraint)
{
Size screen = new Screen(ActualWidth, ActualHeight);
double metersPerDisplayUnit = // calculation based on screen and other context
Size newDesiredSize = // from constraint, adjusting for double.Infinity
foreach(UIElement child in InternalChildren)
{
child.Measure(constraint);
SetMetersPerPixel(child, metersPerDisplayUnit);
newDesiredSize.Width = Math.Max(newDesiredSize.Width, child.DesiredSize.Width);
newDesiredSize.Height = Math.Max(newDesiredSize.Height, child.DesiredSize.Height);
}
return newDesiredSize;
}
protected override Size ArrangeOverride(Size finalSize)
{
foreach(UIElement child in InternalChildren)
{
Location childLocation = GetLocation(child);
// LocationToPoint is very well tested.
Point displayPoint = LocationToPoint(childLocation, PhysicalArea, finalSize);
displayPoint.X -= child.DesiredSize.Width / 2;
displayPoint.Y -= child.DesiredSize.Height / 2;
Rect locationRect = new Rect(displayPoint, child.DesiredSize);
child.Arrange(locationRect);
}
return finalSize;
}
}
原来圆圈的位置放错了,因为DesiredSize和RenderSize不一样。为了修复我的 CirclePoint
中的位置,我必须像这样更改中心点逻辑:
double xOffset = (DesiredSize.Width - RenderSize.Width) / 2;
double yOffset = (DesiredSize.Width - RenderSize.Width) / 2;
Point center = new Point(xOffset + radius, yOffset + radius);
这个解决方案还有一个问题:
- 我的圈子仍然明显被剪掉了。
这应该return UIElement 的中心:
Point topLeftCorner = yourEllipse.PointToScreen(new Point(0, 0));//Get the point of the topleft corner of your UIElement
Point center = new Point(topLeftCorner.X + yourEllipse.Width / 2, topLeftCorner.Y + yourEllipse.Height / 2);//The center of your ellipse
事实证明,我的定位问题的答案是,一旦子容器在任何维度上都大于父容器,DesiredSize 和 RenderSize 之间就会存在差异。我不得不像这样调整我的 OnRender
方法:
protected override void OnRender(DrawingContext drawingContext)
{
base.OnRender(drawingContext);
SolidColorBrush fillBrush = // from FillColor, frozen
SolidColorBrush edgeBrush = // from ObjectColor, frozen
Pen edgePen = // from edgeBrush, StrokeThickness, frozen
double halfPenWidth = StrokeThickness / 2;
double xOffset = (DesiredSize.Width - RenderSize.Width) / 2;
double yOffset = (DesiredSize.Width - RenderSize.Width) / 2;
Point center = new Point(xOffset + radius, yOffset + radius);
drawingContext.DrawEllipse(fillBrush, edgePen, center,
radius - halfPenWidth, radius - halfPenWidth);
}
无论圆相对于父级有多大,都可以在正确的位置绘制圆。
我的剪辑问题是由于布局窗格有 ClipToBounds=True
。那完全是另一个问题。