0

my fellow programmers!

I have searched the Internet and could not find a solution. Probably this is due to me being a newbie in WPF. I'm trying to achieve the following:

CustomControl having an ArcSegment whose Size is bound to the size of the CustomControl: enter image description here

This should illustrate a wafer with dies (semiconductor industry)

So I understood that I need to use <Path> in XAML in order to construct that 'circle with notch'

My problem is that I cannot bind the ArcSegment element to CustomControl's Size. ArcSegment has Size property and CustomControl has ActualWidth & ActualHeight. I tried several approaches but neither of them worked for me.

Here is my class for CustomControl:

    public class WaferMap : Control
    {
        public static readonly DependencyProperty ActualSizeProperty = DependencyProperty.Register(
            nameof(ActualSize), typeof(Size), typeof(WaferMap), new PropertyMetadata(default(Size)));

        public Size ActualSize
        {
            get => (Size) this.GetValue(ActualSizeProperty);
            set => this.SetValue(ActualSizeProperty, value);
        }

        public WaferMap()
        {
            var actualWidthPropertyDescriptor = DependencyPropertyDescriptor.FromProperty(ActualWidthProperty, typeof(WaferMap));
            actualWidthPropertyDescriptor.AddValueChanged(this, OnActualWidthOrHeightChanged);

            var actualHeightPropertyDescriptor = DependencyPropertyDescriptor.FromProperty(ActualHeightProperty, typeof(WaferMap));
            actualHeightPropertyDescriptor.AddValueChanged(this, OnActualWidthOrHeightChanged);
        }

        private void OnActualWidthOrHeightChanged(object? sender, EventArgs e)
        {
            this.ActualSize = new Size(ActualWidth, ActualHeight);
        }

        static WaferMap()
        {
            DefaultStyleKeyProperty.OverrideMetadata(typeof(WaferMap), new FrameworkPropertyMetadata(typeof(WaferMap)));
        }
    }

And the XAML Style that I use for it:

<ResourceDictionary
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:Zeiss.CdAnalizer.Ui">
    <Style TargetType="local:WaferMap">
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="local:WaferMap" >
                    <ControlTemplate.Resources>
                        <local:WaferMap x:Key="WaferMap"/>
                    </ControlTemplate.Resources>
                    <Grid DataContext="{Binding Source={StaticResource WaferMap}}">
                        <Path Stroke="Black" StrokeThickness="1">
                            <Path.Data>
                                <PathGeometry>
                                    <PathGeometry.Figures>
                                        <PathFigureCollection>
                                            <PathFigure StartPoint="145,300">
                                                <PathFigure.Segments>
                                                    <PathSegmentCollection>
                                                        <ArcSegment 
                                                            IsLargeArc="True"
                                                            Size="{Binding Path=ActualSize, PresentationTraceSources.TraceLevel=High}"
                                                            SweepDirection="Clockwise"
                                                            Point="155,300" />
                                                    </PathSegmentCollection>
                                                </PathFigure.Segments>
                                            </PathFigure>
                                        </PathFigureCollection>
                                    </PathGeometry.Figures>
                                </PathGeometry>
                            </Path.Data>
                        </Path>
                    </Grid>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style></ResourceDictionary>

Surely I'll need to bind also the StartPoint, but first I wanted to see that simple stuff works for me. Unfortunately, it doesn't. I see this in my Output window:

System.Windows.Data Warning: 60 : BindingExpression (hash=14976165): Default mode resolved to OneWay
System.Windows.Data Warning: 61 : BindingExpression (hash=14976165): Default update trigger resolved to PropertyChanged
System.Windows.Data Warning: 62 : BindingExpression (hash=14976165): Attach to System.Windows.Media.ArcSegment.Size (hash=24211521)
System.Windows.Data Warning: 64 : BindingExpression (hash=14976165): Use Framework mentor <null>
System.Windows.Data Warning: 67 : BindingExpression (hash=14976165): Resolving source 
System.Windows.Data Warning: 69 : BindingExpression (hash=14976165): Framework mentor not found
System.Windows.Data Warning: 65 : BindingExpression (hash=14976165): Resolve source deferred
System.Windows.Data Warning: 95 : BindingExpression (hash=14976165): Got InheritanceContextChanged event from ArcSegment (hash=24211521)
System.Windows.Data Warning: 67 : BindingExpression (hash=14976165): Resolving source 
System.Windows.Data Warning: 69 : BindingExpression (hash=14976165): Framework mentor not found
System.Windows.Data Warning: 95 : BindingExpression (hash=14976165): Got InheritanceContextChanged event from ArcSegment (hash=24211521)
System.Windows.Data Warning: 67 : BindingExpression (hash=14976165): Resolving source 
System.Windows.Data Warning: 69 : BindingExpression (hash=14976165): Framework mentor not found
System.Windows.Data Warning: 95 : BindingExpression (hash=14976165): Got InheritanceContextChanged event from ArcSegment (hash=24211521)
System.Windows.Data Warning: 67 : BindingExpression (hash=14976165): Resolving source 
System.Windows.Data Warning: 69 : BindingExpression (hash=14976165): Framework mentor not found
System.Windows.Data Warning: 95 : BindingExpression (hash=14976165): Got InheritanceContextChanged event from ArcSegment (hash=24211521)
System.Windows.Data Warning: 67 : BindingExpression (hash=14976165): Resolving source 
System.Windows.Data Warning: 69 : BindingExpression (hash=14976165): Framework mentor not found
System.Windows.Data Warning: 95 : BindingExpression (hash=14976165): Got InheritanceContextChanged event from ArcSegment (hash=24211521)
System.Windows.Data Warning: 67 : BindingExpression (hash=14976165): Resolving source 
System.Windows.Data Warning: 70 : BindingExpression (hash=14976165): Found data context element: Path (hash=10232270) (OK)
System.Windows.Data Warning: 78 : BindingExpression (hash=14976165): Activate with root item WaferMap (hash=14353717)
System.Windows.Data Warning: 108 : BindingExpression (hash=14976165):   At level 0 - for WaferMap.ActualSize found accessor DependencyProperty(ActualSize)
System.Windows.Data Warning: 104 : BindingExpression (hash=14976165): Replace item at level 0 with WaferMap (hash=14353717), using accessor DependencyProperty(ActualSize)
System.Windows.Data Warning: 101 : BindingExpression (hash=14976165): GetValue at level 0 from WaferMap (hash=14353717) using DependencyProperty(ActualSize): Size (hash=0)
System.Windows.Data Warning: 80 : BindingExpression (hash=14976165): TransferValue - got raw value Size (hash=0)
System.Windows.Data Warning: 89 : BindingExpression (hash=14976165): TransferValue - using final value Size (hash=0)

When I say it doesn't work = when I change the size of the form nothing happens (the arc stays the same small dash). Also, the WaferMap.ActualSize.get is never get called. The setter is called when I change size of the form (As you can see I have registered to change event of ActualWidth and ActualHeight).

Thank you!

Gregory Stein
  • 325
  • 4
  • 14
  • Possible solutions: https://stackoverflow.com/questions/9008525/why-actualsize-is-not-updating-its-value-on-wpf and https://stackoverflow.com/questions/658170/onewaytosource-binding-from-readonly-property-in-xaml – Bruno Jul 11 '20 at 12:22
  • Having a `` resource in the ControlTemplate of a `local:WaferMap` makes no sense at all. You are using one WaferMap as the DataContext of another WaferMap, i.e. the templated one. – Clemens Jul 11 '20 at 12:39
  • Does the shape change? Or is it always a circle with a notch? If it's constant you could build a geometry and make that a resource in a resource dictionary. Use that geometry as the data for a path and stretch it to whatever size the path or container of the path is. – Andy Jul 11 '20 at 13:00
  • @Clemens, if I remove that, the following line Grid DataContext=... will fail because it couldn't find the WaferMap... Should I remove that as well? – Gregory Stein Jul 11 '20 at 13:04
  • You don't need to set any DataContext, Just use TemplateBindings. However, it seems unclear why you are not simply deriving from the Shape class. – Clemens Jul 11 '20 at 13:08
  • @Clemens, thanks! There are many things that are not clear for me. I'm trying to learn things along the way. – Gregory Stein Jul 12 '20 at 07:13

1 Answers1

1

You may perhaps use a simple derived Shape like shown below.

It draws an ArcSegment with a notch at the bottom center, and all you would have to do is to adjust the size of the notch.

public class WaferMap : Shape
{
    private readonly StreamGeometry geometry = new StreamGeometry();

    protected override Geometry DefiningGeometry => geometry;

    protected override Size MeasureOverride(Size size)
    {
        if (double.IsInfinity(size.Width))
        {
            size.Width = StrokeThickness;
        }

        if (double.IsInfinity(size.Height))
        {
            size.Height = StrokeThickness;
        }

        return size;
    }

    protected override Size ArrangeOverride(Size size)
    {
        var x = size.Width / 2;
        var y = size.Height - StrokeThickness / 2;
        var arcSize = new Size(
            (size.Width - StrokeThickness) / 2, (size.Height - StrokeThickness) / 2);

        using (var sgc = geometry.Open())
        {
            sgc.BeginFigure(new Point(x, y - 20), true, true);
            sgc.LineTo(new Point(x - 10, y), true, true);
            sgc.ArcTo(new Point(x + 10, y), arcSize, 0, true,
                      SweepDirection.Clockwise, true, true);
        }

        return size;
    }

    protected override void OnRender(DrawingContext drawingContext)
    {
        drawingContext.DrawGeometry(Fill, new Pen(Stroke, StrokeThickness), geometry);
    }
}

You would use it like any other Shape element:

<local:WaferMap Stroke="Orange" StrokeThickness="3" Margin="10"/>
Clemens
  • 123,504
  • 12
  • 155
  • 268
  • thank you! I thought that the whole purpose of XAML is to define all this presentation-related functionality. And keep in code only 'real' functionality that actually does stuff – Gregory Stein Jul 12 '20 at 07:30
  • Just found an explanation to the Clemens' solution. Too bad I'm reaching it a week after my initial searches: https://markheath.net/post/creating-resizable-shape-controls-in – Gregory Stein Jul 13 '20 at 07:44