Reputation: 1616
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
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.
Try creating a new PointCollection
each time, add the points, and assign the list to the Polygon Points .
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
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
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