As you’ve seen, when a property backed by a dependency property is changed at runtime, the element with that property changes to reflect that change. This is a result of the support for a property-changed handler built into dependency properties.
Certain collections will also respond to changes. Collection classes that derive from PresentationFrameworkCollection
respond to changes when an object is added to or removed from a
collection. A notification is funneled up to the element containing the
collection. In some cases, changes to dependency properties in the
members of the collection also trigger notifications. (Unfortunately,
the exact nature of this notification process is hidden from the
application programmer.) The UIElementCollection that the Panel classes uses for its Children property derives from this class, as does the PointCollection in Polyline and Polygon.
At runtime, you can dynamically add Point objects to the PointCollection, or remove them from the PointCollection, and a Polyline or Polygon will change.
The GrowingPolygons project has a MainPage.xaml file that instantiates a Polygon element and gives it a couple properties:
Example 1. Silverlight Project: GrowingPolygons File: MainPage.xaml (excerpt)
<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0"> <Polygon Name="polygon" Stroke="{StaticResource PhoneForegroundBrush}" StrokeThickness="{StaticResource PhoneStrokeThickness}" /> </Grid>
|
The code-behind file waits until the Loaded
event is fired before determining the size of the content panel (just
as in the Spiral program) and it begins by obtaining similar
information. But the OnLoaded handler just adds two points to the Points collection of the Polygon to define a vertical line; everything else happens during TickDispatcherTimer (which of course requires a using directive for System.Windows.Threading): events of a
Example 2. Silverlight Project: GrowingPolygons File: MainPage.xaml.cs (excerpt)
public partial class MainPage : PhoneApplicationPage { Point center; double radius; int numSides = 2;
public MainPage() { InitializeComponent(); Loaded += OnLoaded; }
void OnLoaded(object sender, RoutedEventArgs args) { center = new Point(ContentPanel.ActualWidth / 2 - 1, ContentPanel.ActualHeight / 2 - 1); radius = Math.Min(center.X, center.Y);
polygon.Points.Add(new Point(center.X, center.Y - radius)); polygon.Points.Add(new Point(center.X, center.Y + radius));
DispatcherTimer tmr = new DispatcherTimer(); tmr.Interval = TimeSpan.FromSeconds(1); tmr.Tick += OnTimerTick; tmr.Start(); }
void OnTimerTick(object sender, EventArgs args) { numSides += 1;
for (int vertex = 1; vertex < numSides; vertex++) { double radians = vertex * 2 * Math.PI / numSides; double x = center.X + radius * Math.Sin(radians); double y = center.Y - radius * Math.Cos(radians); Point point = new Point(x, y);
if (vertex < numSides - 1) polygon.Points[vertex] = point; else polygon.Points.Add(point); }
PageTitle.Text = "" + numSides + " sides"; } }
|
Every second, the program replaces all but one of the Point objects in the Points collection of the Polygon. The first Point in the collection—which is the Point at the top center of the content area—is the only one that remains the same. In addition, the Tick handler adds a new Point object at the end of the collection. The result is a polygon that gains one new side every second:
You can see for yourself how many points are needed before this polygon visually turns into a circle!
Notice that the program entirely replaces the Point object in the collection rather than attempting to modify the X and Y properties of the existing object in the collection. Point is a structure, and it implements no notification mechanism. There is no way for the PointCollection to know if a property of a particular Point in the collection has been changed. Only when the entire Point object is replaced does the PointCollection know about it.
If you’re doing something like this is in a real application, you might want to detach the PointCollection from the Polygon when you’re making a lot of changes to it. This prevents a long series of notifications firing that inform the Polygon that the PointCollection has changed. The code would look something like this:
PointCollection points = polygon.Points;
polygon.Points = null;
//. . .make changes to points collection
polygon.Points = points;
The PointCollection is detached by saving a reference to it and setting the Points property to null. When all changes have been made, the PointCollection is reattached to the Polygon, and the Polygon responds to the new collection of points.