#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

#1,140 – Using a Value Converter in a Template

You can use a value converter anywhere in XAML where you are using data binding.

Below is an example of using a value converter within a data template.  The Visibility property is bound to the underlying Actor object that is the data context for the item template.  The value converter then derives a value for Visibility from several properties within the Actor object.  (Assume that we have an ActorList property that is a collection of Actor instances).

The XAML includes:

    <Window.Resources>
        <loc:DeadFredConverter x:Key="deadFredConverter"/>
    </Window.Resources>

    <StackPanel>
        <ListBox Margin="15" Width="270" Height="320"
             ItemsSource="{Binding ActorList}">
            <ListBox.ItemTemplate>
                <DataTemplate>
                    <StackPanel Orientation="Horizontal">
                        <Image Source="{Binding Image}" Height="80"/>
                        <StackPanel Margin="5">
                            <TextBlock Text="{Binding FullName}" FontSize="12" FontWeight="Bold"/>
                            <TextBlock Text="{Binding Dates}"/>
                            <TextBlock Text="{Binding KnownFor}" Margin="0,5,0,0" FontStyle="Italic"/>
                        </StackPanel>
                        <Label Content="Dead Fred !" Foreground="Red"
                               FontWeight="Bold"
                               Visibility="{Binding Converter={StaticResource deadFredConverter}}"/>
                    </StackPanel>
                </DataTemplate>
            </ListBox.ItemTemplate>
        </ListBox>
    </StackPanel>

The body of the value converter is:

    class DeadFredConverter : IValueConverter
    {
        // Convert to Visibility, deriving from properties on Actor object
        public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            Actor a = (Actor)value;

            Visibility vis = Visibility.Hidden;

            if ((a.FirstName == "Fred") &&
                a.DeathYear.HasValue &&
                (a.DeathYear <= DateTime.Today.Year))
                vis = Visibility.Visible;

            return vis;
        }

        public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            throw new NotImplementedException();
        }
    }

1140-001

#1,139 – Value Converter Example for Input

The ConvertBack method of a value converter is used to convert from a binding target (e.g. the attribute of a control) to a binding source (e.g. a property).  Below is a simple example, showing how we can convert from a Slider value to the square root of the selected value.

In XAML, we have a Slider that ranges from 1-100 and binds to a property that is meant to store the square root of the selected value.  We specify a binding Mode to indicate that binding should only map from the target (the Value property) to the source (the SqRootValue property) and not in the other direction.  We then include labels that bind to the Slider’s Value property as well as the SqRootValue property.

<Window x:Class="WpfApplication1.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:sys="clr-namespace:System;assembly=mscorlib"
        xmlns:local="clr-namespace:WpfApplication1"
        Title="Value Converter" SizeToContent="WidthAndHeight">

    <Window.Resources>
        <local:IntToRootConverter x:Key="intToRootConverter"/>
    </Window.Resources>

    <StackPanel Margin="15">
        <Slider x:Name="slider" Minimum="1" Maximum="100"
                IsSnapToTickEnabled="True"
                Value="{Binding Path=SqRootValue,
                                Converter={StaticResource intToRootConverter},
                                Mode=OneWayToSource}"/>
        <Label Content="{Binding ElementName=slider, Path=Value}"/>
        <Label Content="{Binding SqRootValue}"/>
    </StackPanel>
</Window>

The code-behind is straightforward.

    public partial class MainWindow : Window, INotifyPropertyChanged
    {
        public MainWindow()
        {
            this.DataContext = this;
            InitializeComponent();
        }

        public event PropertyChangedEventHandler PropertyChanged = delegate { };

        protected virtual void OnPropertyChanged(string prop)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(prop));
        }

        private double sqRootValue;
        public double SqRootValue
        {
            get { return sqRootValue; }
            set
            {
                if (sqRootValue != value)
                {
                    sqRootValue = value;
                    OnPropertyChanged("SqRootValue");
                }
            }
        }
    }

In the value converter, we just take the square root of the current value, calculating the result in ConvertBack.

    public class IntToRootConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            throw new NotImplementedException();
        }

        // Convert from int (target of binding) to double representing square root (source of binding)
        public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            double root = Math.Sqrt((double)value);
            return root;
        }
    }

At run-time:

1139-001

#1,138 – Value Converter Example for Output

The Convert method of a value converter is used to convert from a binding source (e.g. a property) to a binding target (e.g. the attribute of a control).  Below is a simple example, showing how we can convert from an integer to a brush of a particular color.

In XAML, we have a Slider that ranges from 0-255 and binds to a property, storing the integer value that the user selects.  We then bind to the same property for a rectangle shape’s Fill property.  The Fill property wants a Brush object, so we use a value converter to convert from the integer to a brush.

<Window x:Class="WpfApplication1.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:sys="clr-namespace:System;assembly=mscorlib"
        xmlns:local="clr-namespace:WpfApplication1"
        Title="Value Converter" SizeToContent="WidthAndHeight">

    <Window.Resources>
        <local:IntToBlueBrushValueConverter x:Key="intToBlueBrushValueConverter"/>
    </Window.Resources>

    <StackPanel Margin="15">
        <Slider Minimum="0" Maximum="255"
                Value="{Binding BlueValue}"/>
        <Label Content="{Binding BlueValue}"/>
        <Rectangle Height="80" Width="80"
            Fill="{Binding Path=BlueValue, Converter={StaticResource intToBlueBrushValueConverter}}"/>
    </StackPanel>
</Window>

The code-behind is straightforward.

    public partial class MainWindow : Window, INotifyPropertyChanged
    {
        public MainWindow()
        {
            this.DataContext = this;
            InitializeComponent();
        }

        public event PropertyChangedEventHandler PropertyChanged = delegate { };

        protected virtual void OnPropertyChanged(string prop)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(prop));
        }

        private int blueValue;
        public int BlueValue
        {
            get { return blueValue; }
            set
            {
                if (blueValue != value)
                {
                    blueValue = value;
                    OnPropertyChanged("BlueValue");
                }
            }
        }
    }

In the value converter, we create a solid color brush whose R and G values are 0 and whose B value is derived from the integer (slider position).

    public class IntToBlueBrushValueConverter : IValueConverter
    {
        // Convert from int to System.Windows.Media.Brush
        // where brush is color with R=0, G=0 and B=int
        public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {

            int blueVal = (int)value;
            blueVal = Math.Min(Math.Max(0, blueVal), 255);

            return new SolidColorBrush(Color.FromRgb(0, 0, (byte)blueVal));
        }

        public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            return null;
        }
    }

At run-time, the color of the rectangle changes as we move the slider.

1138-001

1138-002

1138-003