#1,147 – Converting from Polar Coordinates to Cartesian Coordinates

You can represent a two dimensional point as either a cartesian coordinate (X,Y) or a polar coordinate (r,theta).  Conversions between these two coordinate systems are shown below.

From polar coordinates to cartesian:

1147-001

To convert from cartesian coordinates to polar, the following formula works, as long as the X value is positive.

1147-002

If X is 0 or negative, then the calculation for theta becomes:

1147-003

#1,146 – Polar Coordinate System

In most cases when you’re working with graphical objects, you use a cartesian coordinate system, where each point is represented as an X and a Y value, indicating the point’s distance from an origin in two different dimensions.

You can also express points in a two-dimensional system using a polar coordinate system.  Each point in a polar coordinate system is represented with two values:

  • A radius value, describing how far the point is from an origin  (range is any non-negative number)
  • An angular coordinate, describing how many degrees around the circle the point is located, typically from a horizontal ray extending to the right of the origin  (range typically [0, 360) degrees or [0, 2*pi) radians)

Below is a picture showing two sample points expressed in polar coordinates.

  • (2.0, 60) – Radius = 2, Angle = 60 degrees (counterclockwise) from horizontal
  • (1.0, 180) – Radius = 1, Angle = 180 degrees (counterclockwise) from horizontal

1146-001

 

#1,145 – Using RenderSize in Custom Shape

When drawing a geometry in a custom Shape element, you could draw using hard-coded coordinates.  It’s more common, however, to use the RenderSize property of the UIElement to render the object so that the geometry scales based on the size of the control.

Below, we create a custom shape that draws a diagonal line from the upper left corner of the control to the lower right.

    public class MyShape : Shape
    {
        protected override Geometry DefiningGeometry
        {
            get
            {
                double maxWidth = RenderSize.Width;
                double maxHeight = RenderSize.Height;

                StreamGeometry geom = new StreamGeometry();
                using (StreamGeometryContext ctx = geom.Open())
                {
                    ctx.BeginFigure(
                        new Point(0.0, 0.0),
                        false,
                        false);
                    ctx.LineTo(
                        new Point(maxWidth, maxHeight),
                        true,
                        false);
                }

                return geom;
            }
        }
    }

We can use the shape in XAML as follows:

    <StackPanel>
        <loc:MyShape Stroke="Black" StrokeThickness="1"
                     Height="50" Width="50"
                     HorizontalAlignment="Center"/>
    </StackPanel>

Now when we change the size of the underlying control, the geometry adjusts as well.

1145-001

1145-002

1145-003

#1,144 – Geometry in Custom Shape Doesn’t Automatically Scale

If you define a custom Shape by creating some Geometry, the resulting geometry will not automatically scale when shape’s size is changed.

Suppose that we have the following custom shape.

    public class MyShape : Shape
    {
        protected override Geometry DefiningGeometry
        {
            get
            {
                StreamGeometry geom = new StreamGeometry();
                using (StreamGeometryContext ctx = geom.Open())
                {
                    ctx.BeginFigure(
                        new Point(0.0, 0.0),
                        false,
                        false);
                    ctx.LineTo(
                        new Point(50.0, 50.0),
                        true,
                        false);
                }

                return geom;
            }
        }
    }

Placing this control in a StackPanel, it’s size is just large enough to accommodate the geometry.

1144-001

If we explicitly make the shape larger, the underlying geometry stays the same size.

1144-002

#1,143 – Coordinate System for StreamGeometry

You can use a StreamGeometry object, along with the StreamGeometryContext returned by its Open method, to draw simple geometric shapes.

When using the various methods of a StreamGeometryContext instance, you work with X and Y values.  The coordinate system used has the upper left corner of the drawing region at (0,0), with X values increasing from left to right and Y values increasing from top to bottom.

1143-001

Below, we have a custom shape that draws a line segment from (0,0) to (50,50) and then another line segment to (75,25).

    public class MyShape : Shape
    {
        protected override Geometry DefiningGeometry
        {
            get
            {
                return GetMyShapeGeometry();
            }
        }

        private Geometry GetMyShapeGeometry()
        {
            StreamGeometry geom = new StreamGeometry();
            using (StreamGeometryContext ctx = geom.Open())
            {
                ctx.BeginFigure(
                    new Point(0.0, 0.0),
                    false,    // is NOT filled
                    false);   // is NOT closed
                ctx.LineTo(
                    new Point(50.0, 50.0),
                    true,     // is stroked (line visible)
                    false);   // is not smoothly joined w/other segments
                ctx.LineTo(
                    new Point(75.0, 25.0),
                    true,     // is stroked (line visible)
                    false);   // is not smoothly joined w/other segments
            }

            return geom;
        }
    }

We can then use this shape from XAML.

    <Canvas>
        <loc:MyShape Canvas.Top="0" Canvas.Left="0"
                     Stroke="Black" />
    </Canvas>

1143-002

#1,142 – Setting Attached Property Value from Code

You can change the value of an attached property for a given control from code by using the SetValue or SetCurrentValue methods.  You call these methods on the control that the property is attached to, passing in a reference to the property and the new value.  (SetCurrentValue is preferred, to avoid overwriting a local value).

Below, we set a value for MyAttProps.Important in XAML, but also wire up a Click event to allow changing the value from code.

        <Label x:Name="lblHi" Content="Hi there"
               loc:MyAttProps.Important="True"
               Background="AliceBlue"/>
        <Button Content="Change Content"
                Click="Button_Click"/>

In the code-behind, we use SetCurrentValue to change the value.

        private void Button_Click(object sender, RoutedEventArgs e)
        {
            bool impValue = (bool)lblHi.GetValue(MyAttProps.ImportantProperty);

            lblHi.SetCurrentValue(MyAttProps.ImportantProperty, !impValue);
        }

#1,141 – Attached Properties Allow Customization of Existing Controls

An attached property is a dependency property defined in one class and then attached (used) on an instance of some other class.

You can define your own attached properties as a mechanism for extending the appearance or behavior of a control.

Below, we register a dependency property of type bool, named Important.  When Important is set to true, we set the foreground brush of the control to red.

    public class MyAttProps
    {
        // Surround property definition
        static PropertyMetadata ImportantMetadata =
            new PropertyMetadata(
                false,                // Default value
                OnImportantChanged,   // Changed callback
                null);                // Coerce value callback

        public static readonly DependencyProperty ImportantProperty =
            DependencyProperty.RegisterAttached(
                "Important",          // Property name
                typeof(bool),         // Property type
                typeof(MyAttProps),   // Defining class type
                ImportantMetadata);   // Metadata described above

        // Allow setting value from XAML
        public static void SetImportant(DependencyObject depObj, bool value)
        {
            depObj.SetValue(ImportantProperty, value);
        }

        public static bool GetImportant(DependencyObject depObj)
        {
            return (bool)depObj.GetValue(ImportantProperty);
        }

        // Important property has changed
        public static void OnImportantChanged(
            DependencyObject d,
            DependencyPropertyChangedEventArgs e)
        {
            Control ctrl = d as Control;
            bool important = (bool)e.NewValue;
            if ((ctrl != null) && important)
                ctrl.SetValue(Control.ForegroundProperty, Brushes.Red);
            else
                ctrl.ClearValue(Control.ForegroundProperty);
        }
    }

We can now use this new property from XAML:

        <Label x:Name="lblHi" Content="Hi there" 
               loc:MyAttProps.Important="True"
               Background="AliceBlue"/>

1141-001

Follow

Get every new post delivered to your Inbox.

Join 356 other followers