windowsgm
windowsgm

Reputation: 1616

Changing a Polygon's Points

SOLUTION Dynamic Margin on Window Drag

So I'm trying to get my polygon to move as the window is moved. I have;

    private void ResetPolygon(Point Point1, Point Point2, Point Point3)
    {
        SpeechPoly.Points.Clear();
        ObservableCollection<Point> myPointCollection = new ObservableCollection<Point>();
        myPointCollection.Add(Point3);
        myPointCollection.Add(Point2);
        myPointCollection.Add(Point1);
        foreach (Point p in myPointCollection)
        {
            SpeechPoly.Points.Add(p);
        }
    }

    private void Window_LocationChanged(object sender, EventArgs e)
    {
        if (this.IsLoaded)
        {
            Point Point1 = new Point(newPoint3);
            Point Point2 = new Point(newPoint2);
            Point Point3 = new Point(newPoint1);
            ResetPolygon(newPoint1, newPoint2, newPoint3);

//Write out the values of both the list and the polygon to screen!
            txtBlock.Text = newPoint1.X.ToString("N2") + ", " + newPoint1.Y.ToString("N2") + 
"\n" + newPoint2.X.ToString("N2") + ", " + newPoint2.Y.ToString("N2") + "\n" + 
newPoint3.X.ToString("N2") + ", " + newPoint3.Y.ToString("N2");

            txtBlock.Text += "\n" + SpeechPoly.Points[0].X.ToString("N2") + ", " + 
SpeechPoly.Points[0].Y.ToString("N2") + "\n" + SpeechPoly.Points[1].X.ToString("N2") + ", " +        
SpeechPoly.Points[1].Y.ToString("N2") + "\n" + SpeechPoly.Points[2].X.ToString("N2") + ", "+ 
SpeechPoly.Points[2].Y.ToString("N2");
                        }
                    }

But the Polygon remains the same shape no matter what, even though the Textblock clearly shows the values of all the Points in the List and the Polygon's points are definitely changing.

I've also tried to bind the Points property of the Polygon to my code.

<Polygon
    Name="SpeechPoly"
    Points="{Binding myPointCollection, RelativeSource={RelativeSource FindAncestor, AncestorType=Window}}"
    Stroke="Black" 
    StrokeThickness="2"
    </Polygon>

I've also tried using a pointsCollection as opposed List<Points> but same result. Almost seems like the Polygon is not refreshing.

Upvotes: 3

Views: 8212

Answers (3)

GameAlchemist
GameAlchemist

Reputation: 19294

Well what you do in the Window_LocationChanged handler, is to add points so no wonder you end up with more polygons. Your myPointCollection must be an ObservableCollection, and you must clear the collection before adding the points. I was wondering also if you could use a class inheriting from Point (System.Windows.Point) but overloading x and y to NotifyingPropertyChanged them so the binding can update. If it works, you don't have to change the Collection any more, but the Collection content.

Edit after update:

It looks like the problem is in your update: since you update it with the same Collection no update occurs.
This is perfectly normal behaviour since the Collection (a PointCollection) does not implement CollectionChanged, so even if you clear it, all that the clr 'sees' is the same object, so no change, hence no update.

You should NOT modify(clear) the point collection of the Xaml Polygon object itself, since the binding is enough.

  1. Try creating a new PointCollection each time, add the points, and assign the list to the Polygon Points .

  2. OR try to make notifying points and change the points. Maybe you can notify both X and Y with empty string "" which means everything changed, OR notify X with "X" and Y with "Y".

Upvotes: 0

G.Y
G.Y

Reputation: 6159

I was not being satisfied with the previous answer I gave as it is after all a workaround..

I found a better solution to the problem which will not require to reset the databinds.

So the binding from XAML is being directed to a property with INCC however the data itself is converted to Points for the polygon to use when drawing.

<Window x:Class="WpfApplication9.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:WpfApplication9"
    Title="MainWindow" Height="350" Width="525" LocationChanged="Window_LocationChanged" >
<Window.Resources>
    <local:MyPointCollectionConverter x:Key="mcolconv" />
</Window.Resources>
<Canvas>
    <Polygon Name="SpeechPoly" Stroke="Black" StrokeThickness="2" 
             Points="{Binding Path=myPointCollection, Converter={StaticResource mcolconv}}" />
</Canvas>

using System;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Windows;
using System.Windows.Data;
using System.Windows.Media;

namespace WpfApplication9
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window, INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    private ObservableCollection<Point> _myPointCollection = new ObservableCollection<Point>();
    public ObservableCollection<Point> myPointCollection { get { return _myPointCollection; } }

    public MainWindow()
    {
        myPointCollection.Add(new Point(100, 50));
        myPointCollection.Add(new Point(150, 100));
        myPointCollection.Add(new Point(50, 100));
        InitializeComponent();
        DataContext = this;
    }

    private void ResetPolygon(Point Point1, Point Point2, Point Point3)
    {
        myPointCollection.Clear();
        myPointCollection.Add(Point1);
        myPointCollection.Add(Point2);
        myPointCollection.Add(Point3);
        if (PropertyChanged != null)
            PropertyChanged(this, new PropertyChangedEventArgs("myPointCollection"));
    }

    private void Window_LocationChanged(object sender, EventArgs e)
    {
        if (this.IsLoaded)
        {
            Random rnd = new Random();
            Point Point1 = new Point(rnd.Next(50, 200), rnd.Next(50, 200));
            Point Point2 = new Point(rnd.Next(50, 200), rnd.Next(50, 200));
            Point Point3 = new Point(rnd.Next(50, 200), rnd.Next(50, 200));
            ResetPolygon(Point1, Point2, Point3);
        }

    }
}

public class MyPointCollectionConverter : IValueConverter
{
    #region IValueConverter Members

    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        var regPtsColl = new PointCollection(); //regular points collection.
        var obsPtsColl = (ObservableCollection<Point>)value; //observable which is used to raise INCC event.
        foreach (var point in obsPtsColl)
            regPtsColl.Add(point);
        return regPtsColl;
    }

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

    #endregion
}
}

Upvotes: 3

G.Y
G.Y

Reputation: 6159

The PointsCollection is not using the INotifyCollectionChanged..
so binding will never know that somthing has changed.

You can go around this by refreshing the binding.

Here is an example that do what you asked for:

<Window x:Class="WpfApplication9.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="MainWindow" Height="350" Width="525" LocationChanged="Window_LocationChanged" >
<Canvas>
    <Polygon Name="SpeechPoly" Stroke="Black" StrokeThickness="2" Points="{Binding Path=myPointCollection}" />
</Canvas>

using System;
using System.Windows;
using System.Windows.Media;

namespace WpfApplication9
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
    public PointCollection myPointCollection { get; set; }

    public MainWindow()
    {
        myPointCollection = new PointCollection
        {
            new Point(100, 50),
            new Point(150, 100),
            new Point(50, 100)
        };
        InitializeComponent();
        DataContext = this;
    }

    private void ResetPolygon(Point Point1, Point Point2, Point Point3)
    {
        myPointCollection[0] = Point1;
        myPointCollection[1] = Point2;
        myPointCollection[2] = Point3;
        DataContext = null;
        DataContext = this;
    }

    private void Window_LocationChanged(object sender, EventArgs e)
    {
        if (this.IsLoaded)
        {
            Random rnd = new Random();
            Point Point1 = new Point(rnd.Next(50, 200), rnd.Next(50, 200));
            Point Point2 = new Point(rnd.Next(50, 200), rnd.Next(50, 200));
            Point Point3 = new Point(rnd.Next(50, 200), rnd.Next(50, 200));
            ResetPolygon(Point1, Point2, Point3);
        }

    }
}

}

Upvotes: 0

Related Questions