【问题标题】:WPF custom drawing mysterious drawing errorWPF自定义绘图神秘绘图错误
【发布时间】:2015-10-30 14:23:30
【问题描述】:

我有一个直接绘制到自定义画布的控件。自定义控件根据物理尺寸计算绘制圆的大小。无论我使用 Ellipse XAML 元素还是自定义绘制控件,我看到的行为都是相同的。

基本上,一旦元素的大小变得足够大到大于画布的最小尺寸,它的绘制位置就会偏离剪切矩形。最终结果是中心点(由不同的控件绘制)是正确的,但是圆被移动了——并且边缘被剪掉了。

例子:

只要我计算的中心和实际的中心对齐,控件就可以正确渲染。我找不到明确设置剪辑的任何地方。

我的问题是 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);

这个解决方案还有一个问题:

  • 我的圈子仍然明显被剪裁。

【问题讨论】:

  • 如果没有看过代码的相关部分就很难说。
  • 注意:ClipToBounds 无效。 @Clemens,将尝试获得最少的代码......
  • @Clemens,添加了额外代码的相关部分。
  • 到目前为止一切正常。我已经将它转储到一个测试项目中并剥离了所有缩写的东西(并在 CirclePoint 中用 MeasureCore 替换了 MeasureOverride),它按预期呈现。 “当圆圈超过一定大小时”到底是什么意思?那是什么尺寸?
  • 取容器尺寸的最小尺寸,当直径大于该尺寸时,我得到错位。容器的大小无关紧要,这就是导致问题的关系。直径越大,问题越严重。

标签: c# wpf c#-4.0 wpf-controls custom-controls


【解决方案1】:

这应该返回 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

【讨论】:

  • 这可能在容器中起作用,但从椭圆本身内部它会抵消它需要的位置之外的所有内容。试试这个把圆圈放在所需位置的右下角。
【解决方案2】:

事实证明,我的定位问题的答案是,一旦子容器在任何维度上都比父容器大,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。这完全是另一个问题。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-11-08
    • 1970-01-01
    • 2022-01-01
    • 2015-05-20
    • 1970-01-01
    • 2022-01-20
    相关资源
    最近更新 更多