简体   繁体   中英

I am having difficulty rendering a WPF ArcSegment. The Stroke color is not working

I am attempting to render part of a model as a semicircle using ArcSegment. My code uses a number of 2D Path based objects. All are working except the module that uses ArcSegment. For example a similar module using LineSegment works correctly.

I can see that the basic geometry of my ArcSegment is correct as the Fill property makes the arc visible by fillng the area inside the expected semicircle.

I am not using XAML. All shapes are built using C# / WPF functions.

Following is the code that renders all of my Geometry objects using a WPF Path object:

        // builder constructs a collection of Geometry objects and returns the in a GeometryGroup via ToGeometry()
        GeometryGroup model2D = builder.ToGeometry();

        model2D.Transform = BuildLocalTransform2D(visualModel);
        var visual2D = new System.Windows.Shapes.Path();

        if (typeof(Foundation.Visualisation.Elements2D.ShapeElement).IsAssignableFrom(visualModel.GetType()))
            visual2D.Fill = ((Foundation.Visualisation.Elements2D.ShapeElement)visualModel).ShapeStyle.BackgroundBrush;
        else
            // ArcSegment and LineSegment based geometries always follow this path
            // was using transparent but needed to add a color to make ArcSegment based Path visible
            // the assignment to Stroke below does not work (line remains transparent) when model2D contains a PathGeometry that contains one ArcSegment
            // I can see that the arc has the correct shape when the fill is not transparent
            //visual2D.Fill = Brushes.Transparent;
            visual2D.Fill = Brushes.Cyan;

        // debug shows StrokeThickness is always 2
        visual2D.StrokeThickness = ((PathElement)visualModel).LineStyle.LineThickness;
        // debug shows that Stroke is always Brushes.Black
        visual2D.Stroke = ((PathElement)visualModel).LineStyle.LineBrush;
        visual2D.StrokeStartLineCap = PenLineCap.Round;
        visual2D.StrokeEndLineCap = PenLineCap.Round;
        visual2D.StrokeLineJoin = PenLineJoin.Round;

        visual2D.Data = model2D;

        WpfVisual2DProxy node = new WpfVisual2DProxy(visual2D, visualModel);
        visualModel.AddProxy(node);

        // the following adds the visual to an ItemsControl using a Canvas based template:
        //  this.ItemsPanel = new ItemsPanelTemplate(new FrameworkElementFactory(typeof(Canvas)));     
        ((VisualContainer)((WpfVisual2DProxy)parentNode).Visual2D).AddChild(visual2D);

Following is the code I use to create the ArcSegment:

    public void AddArc(ModelPoint2D arcStart, ModelPoint2D arcEnd, double ellipseFactor, double degrees, bool isLarge, SweepDirection sweepDirection, LineVisualStyle lineStyle)
    {
        double radius = ellipseFactor * arcStart.Distance(arcEnd) / 2.0;
        List<ArcSegment> list = new List<ArcSegment>(2);
        Point start = converter.ToPoint2D(arcStart);           
        list.Add(new ArcSegment(converter.ToPoint2D(arcEnd), new Size(radius, radius), Extrusion.DegreesToRadians(degrees), isLarge, SweepDirection.Clockwise, false));            
        var pathFigure = new PathFigure(start, list, false);
        var pfList = new List<PathFigure>(1);
        pfList.Add(pathFigure);

        group.Children.Add(new PathGeometry(pfList));
    }

The geometry produced here always has a transparent stroke but the arc can be made visible using the Fill property.

Below is the code I used to create a rectangle equiv of the semicircle:

    public void AddPath(IList<ModelPoint2D> Points, LineVisualStyle lineStyle)
    {
        List<LineSegment> list = new List<LineSegment>(Points.Count);
        Point start = converter.ToPoint2D(Points[0]);
        for (int i = 1; i < Points.Count; i++)
        {
            list.Add(new LineSegment(converter.ToPoint2D(Points[i]), true));
        }
        var pathFigure = new PathFigure(start, list, false);
        var pfList = new List<PathFigure>(1);
        pfList.Add(pathFigure);
        group.Children.Add(new PathGeometry(pfList));
    }

The code using LineSegment produces a Geometry that responds to both Stroke and Fill Settings.

The resultant render is shown below:

The Semicircle at the top has Fill visible but not Stroke

I have tried many combinations including Adding the PathGeometry directly to Path.Data rather than wrapped in a GeometryGroup.

Any suggestions / assistance would be much appreciated.

The requested VisualModel code is below:

public abstract class VisualModel
{
    /// <summary>
    /// Recursive hierarchy of VisualModel objects that describe the visual structure of an entity for a specific ViewportType.
    /// The top level of the hierarchy is owned by EntityViews via a ViewportTypeVisualModel entry
    /// </summary>
    /// <param name="id">An optional model component identifier - useful for debug</param>
    public VisualModel(string id)
    {
        domainBindings = null;
        proxies = null;
        Id = id;
        ParentVisualModel = null;
        LocalTransforms = null;
        IsStatic = true;
    }

    public bool HasDomainBindings
    {
        get
        {
            if (domainBindings == null)
                return false;
            return domainBindings.Count > 0;
        }
    }

    public bool HasProxies
    {
        get
        {
            if (proxies == null)
                return false;
            return proxies.Count > 0;
        }
    }

    public string Id { get; private set; }

    /// <summary>
    /// Static objects are not affected by transforms that change after the render is constructed.
    /// Non-Static (Dynamic) objects have transforms that may be changed by the application after the render is constructed
    /// Dynamic objects have attached event handlers that adjust the transforms in response to application events
    /// </summary>
    public bool IsStatic { get; private set; }

    public List<TransformBase> LocalTransforms { get; private set; }

    public VisualModel ParentVisualModel { get; internal set; }

    public void AddDomainBinding(BindingBase binding)
    {
        DomainBindings.Add(binding);
    }

    public void AddLocalTransform(TransformBase transform)
    {
        if (LocalTransforms == null)
            LocalTransforms = new List<TransformBase>(4);
        LocalTransforms.Add(transform);
        if (transform.IsDynamic)
            IsStatic = false;
    }

    public void AddProxy(VisualProxy visualProxy)
    {
        Proxies.Add(visualProxy);
    }

    protected List<BindingBase> DomainBindings
    {
        get
        {
            if (domainBindings == null)
                domainBindings = new List<BindingBase>();
            return domainBindings;
        }
    }

    protected List<VisualProxy> Proxies
    {
        get
        {
            if (proxies == null)
                proxies = new List<VisualProxy>();
            return proxies;
        }
    }

    public virtual void UnbindAll()
    {
        if (domainBindings == null)
            return;
        foreach (BindingBase binding in this.DomainBindings)
            binding.UnBind();
    }

    private List<BindingBase> domainBindings;
    private List<VisualProxy> proxies;
}

Note that VisualModel represents a graphic / render technology agnostic model container. The VisualProxy objects it contains are the base class for technology specific graphics objects. In this case WpfVisual2DProxy objects wrap WPF UIElement objects that contain my WPF renderable representations.

Following is VisualProxy:

public abstract class VisualProxy
{
    public VisualModel.VisualModel VisualModel { get; private set; }
    public VisualProxy Parent { get; private set; }

    public VisualProxy(VisualModel.VisualModel visualModel)
    {
        VisualModel = visualModel;
        Parent = null;
    }

    public VisualProxy(VisualModel.VisualModel visualModel, VisualProxy parent)
    {
        VisualModel = visualModel;
        Parent = parent;
    }
}

And following is WpfVisual2DProxy:

public class WpfVisual2DProxy : VisualProxy
{
    public UIElement Visual2D { get; private set; }

    public WpfVisual2DProxy(UIElement visual2D, VisualModel visualModel) : base(visualModel)
    {
        Visual2D = visual2D;
    }

    public WpfVisual2DProxy(UIElement visual2D, VisualModel visualModel, WpfVisual2DProxy parent) : base(visualModel, parent)
    {
        Visual2D = visual2D;
    }
}

Many thanks to all who turned their minds to my problem.

Clemens is correct. I did not have isStroked set to true on my ArcSegment.

Embarrassingly simple.

Thank you

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM