This commit is contained in:
Beatriz Stollnitz 2019-08-05 18:14:01 -07:00
Родитель e06c47ff0a
Коммит 7c2c18a51e
14 изменённых файлов: 675 добавлений и 0 удалений

Двоичные данные
32-PolygonBinding/Images/32PolygonBinding.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 38 KiB

Просмотреть файл

@ -0,0 +1,9 @@
<Application x:Class="PolygonBinding.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
StartupUri="Window1.xaml"
>
<Application.Resources>
</Application.Resources>
</Application>

Просмотреть файл

@ -0,0 +1,17 @@
using System;
using System.Windows;
using System.Data;
using System.Xml;
using System.Configuration;
namespace PolygonBinding
{
/// <summary>
/// Interaction logic for App.xaml
/// </summary>
public partial class App : System.Windows.Application
{
}
}

Просмотреть файл

@ -0,0 +1,90 @@
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{E8A326F7-B14D-448F-9BEF-04243F8CC07C}</ProjectGuid>
<ProjectTypeGuids>{60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
<RootNamespace>PolygonBinding</RootNamespace>
<AssemblyName>PolygonBinding</AssemblyName>
<WarningLevel>4</WarningLevel>
<OutputType>winexe</OutputType>
<MinFrameworkVersionRequired>3.0</MinFrameworkVersionRequired>
<Install>true</Install>
<InstallFrom>Web</InstallFrom>
<UpdateEnabled>true</UpdateEnabled>
<UpdateMode>Foreground</UpdateMode>
<UpdateInterval>7</UpdateInterval>
<UpdateIntervalUnits>Days</UpdateIntervalUnits>
<UpdatePeriodically>false</UpdatePeriodically>
<UpdateRequired>false</UpdateRequired>
<MapFileExtensions>false</MapFileExtensions>
<ApplicationVersion>1.0.0.*</ApplicationVersion>
<IsWebBootstrapper>true</IsWebBootstrapper>
<BootstrapperEnabled>true</BootstrapperEnabled>
<PublishUrl>Publish\</PublishUrl>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>.\bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugSymbols>false</DebugSymbols>
<Optimize>true</Optimize>
<OutputPath>.\bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
</PropertyGroup>
<ItemGroup>
<Reference Include="mscorlib" />
<Reference Include="System" />
<Reference Include="System.Data" />
<Reference Include="System.Xml" />
<Reference Include="WindowsBase" />
<Reference Include="PresentationCore" />
<Reference Include="PresentationFramework" />
<Reference Include="UIAutomationProvider" />
<Reference Include="UIAutomationTypes" />
<Reference Include="ReachFramework" />
<Reference Include="System.Printing" />
<Reference Include="System.ServiceModel" />
<Reference Include="System.Runtime.Serialization" />
<Reference Include="System.IdentityModel" />
</ItemGroup>
<ItemGroup>
<ApplicationDefinition Include="App.xaml" />
<Page Include="Window1.xaml" />
<Compile Include="App.xaml.cs">
<DependentUpon>App.xaml</DependentUpon>
<SubType>Code</SubType>
</Compile>
<Compile Include="Window1.xaml.cs">
<DependentUpon>Window1.xaml</DependentUpon>
<SubType>Code</SubType>
</Compile>
</ItemGroup>
<ItemGroup>
<Compile Include="Properties\AssemblyInfo.cs" />
<EmbeddedResource Include="Properties\Resources.resx">
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>Resources.cs</LastGenOutput>
<SubType>Designer</SubType>
</EmbeddedResource>
<Compile Include="Properties\Resources.cs">
<AutoGen>True</AutoGen>
<DependentUpon>Resources.resx</DependentUpon>
</Compile>
<None Include="Properties\Settings.settings">
<Generator>SettingsSingleFileGenerator</Generator>
<LastGenOutput>Settings.cs</LastGenOutput>
</None>
<Compile Include="Properties\Settings.cs">
<AutoGen>True</AutoGen>
<DependentUpon>Settings.settings</DependentUpon>
</Compile>
<AppDesigner Include="Properties\" />
</ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
<Import Project="$(MSBuildBinPath)\Microsoft.WinFX.targets" />
</Project>

Просмотреть файл

@ -0,0 +1,20 @@

Microsoft Visual Studio Solution File, Format Version 9.00
# Visual Studio 2005
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PolygonBinding", "PolygonBinding.csproj", "{E8A326F7-B14D-448F-9BEF-04243F8CC07C}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{E8A326F7-B14D-448F-9BEF-04243F8CC07C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{E8A326F7-B14D-448F-9BEF-04243F8CC07C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E8A326F7-B14D-448F-9BEF-04243F8CC07C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{E8A326F7-B14D-448F-9BEF-04243F8CC07C}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
EndGlobal

Двоичные данные
32-PolygonBinding/PolygonBinding/PolygonBinding.suo Normal file

Двоичный файл не отображается.

Просмотреть файл

@ -0,0 +1,54 @@
#region Using directives
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Resources;
using System.Globalization;
using System.Windows;
using System.Runtime.InteropServices;
#endregion
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("PolygonBinding")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("Microsoft")]
[assembly: AssemblyProduct("PolygonBinding")]
[assembly: AssemblyCopyright("Copyright @ Microsoft 2006")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
[assembly: ComVisible(false)]
//In order to begin building localizable applications, set
//<UICulture>CultureYouAreCodingWith</UICulture> in your .csproj file
//inside a <PropertyGroup>. For example, if you are using US english
//in your source files, set the <UICulture> to en-US. Then uncomment
//the NeutralResourceLanguage attribute below. Update the "en-US" in
//the line below to match the UICulture setting in the project file.
//[assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.Satellite)]
[assembly: ThemeInfo(
ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located
//(used if a resource is not found in the page,
// or application resource dictionaries)
ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located
//(used if a resource is not found in the page,
// app, or any theme specific resource dictionaries)
)]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Revision and Build Numbers
// by using the '*' as shown below:
[assembly: AssemblyVersion("1.0.*")]

Просмотреть файл

@ -0,0 +1,70 @@
//------------------------------------------------------------------------------
// <autogenerated>
// This code was generated by a tool.
// Runtime Version:2.0.50727.42
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </autogenerated>
//------------------------------------------------------------------------------
namespace PolygonBinding.Properties
{
using System;
using System.IO;
using System.Resources;
/// <summary>
/// A strongly-typed resource class, for looking up localized strings, etc.
/// </summary>
// This class was auto-generated by the Strongly Typed Resource Builder
// class via a tool like ResGen or Visual Studio.NET.
// To add or remove a member, edit your .ResX file then rerun ResGen
// with the /str option, or rebuild your VS project.
class Resources
{
private static System.Resources.ResourceManager _resMgr;
private static System.Globalization.CultureInfo _resCulture;
/*FamANDAssem*/
internal Resources()
{
}
/// <summary>
/// Returns the cached ResourceManager instance used by this class.
/// </summary>
[System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)]
public static System.Resources.ResourceManager ResourceManager
{
get
{
if ((_resMgr == null))
{
System.Resources.ResourceManager temp = new System.Resources.ResourceManager("Resources", typeof(Resources).Assembly);
_resMgr = temp;
}
return _resMgr;
}
}
/// <summary>
/// Overrides the current thread's CurrentUICulture property for all
/// resource lookups using this strongly typed resource class.
/// </summary>
[System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)]
public static System.Globalization.CultureInfo Culture
{
get
{
return _resCulture;
}
set
{
_resCulture = value;
}
}
}
}

Просмотреть файл

@ -0,0 +1,117 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
</root>

Просмотреть файл

@ -0,0 +1,42 @@
//------------------------------------------------------------------------------
// <autogenerated>
// This code was generated by a tool.
// Runtime Version:2.0.50727.42
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </autogenerated>
//------------------------------------------------------------------------------
namespace PolygonBinding.Properties
{
public partial class Settings : System.Configuration.ApplicationSettingsBase
{
private static Settings m_Value;
private static object m_SyncObject = new object();
public static Settings Value
{
get
{
if ((Settings.m_Value == null))
{
System.Threading.Monitor.Enter(Settings.m_SyncObject);
if ((Settings.m_Value == null))
{
try
{
Settings.m_Value = new Settings();
}
finally
{
System.Threading.Monitor.Exit(Settings.m_SyncObject);
}
}
}
return Settings.m_Value;
}
}
}
}

Просмотреть файл

@ -0,0 +1,7 @@
<?xml version='1.0' encoding='iso-8859-1'?>
<SettingsFile xmlns="uri:settings" CurrentProfile="(Default)">
<Profiles>
<Profile Name="(Default)" />
</Profiles>
<Settings />
</SettingsFile>

Просмотреть файл

@ -0,0 +1,17 @@
<Window x:Class="PolygonBinding.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:PolygonBinding"
Title="PolygonBinding" Height="500" Width="550"
>
<Window.Resources>
<local:PolygonItem x:Key="src"/>
<local:Converter x:Key="converter"/>
</Window.Resources>
<StackPanel>
<Button Click="ChangeSource" Width="150" Margin="20">Change data source</Button>
<Polygon Fill="#CD5C5C" Points="{Binding Source={StaticResource src}, Path=Points, Converter={StaticResource converter}}"/>
</StackPanel>
</Window>

Просмотреть файл

@ -0,0 +1,106 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
using System.Collections.ObjectModel;
using System.ComponentModel;
namespace PolygonBinding
{
/// <summary>
/// Interaction logic for Window1.xaml
/// </summary>
public partial class Window1 : System.Windows.Window
{
public Window1()
{
InitializeComponent();
}
private void ChangeSource(object sender, RoutedEventArgs e)
{
PolygonItem polygon = this.Resources["src"] as PolygonItem;
polygon.Subdivide();
}
}
public class PolygonItem : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName)
{
if (this.PropertyChanged != null)
{
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
private List<Point> points = new List<Point>();
public ReadOnlyCollection<Point> Points
{
get { return new ReadOnlyCollection<Point>(points); }
set
{
points = new List<Point>(value);
OnPropertyChanged("Points");
}
}
public void Subdivide()
{
int count = this.points.Count;
List<Point> newPoints = new List<Point>(count * 2);
for (int i = 0; i < count; i++)
{
Point previousPoint = this.points[i];
Point nextPoint = this.points[(i + 1) % count];
newPoints.Add(previousPoint);
Vector offset = nextPoint - previousPoint;
Vector normal = new Vector(offset.Y, -offset.X);
newPoints.Add(previousPoint + 0.5 * offset + 0.4 * normal);
}
this.points = newPoints;
OnPropertyChanged("Points");
}
public PolygonItem()
{
points.Add(new Point(275, 100));
points.Add(new Point(375, 200));
points.Add(new Point(275, 300));
points.Add(new Point(175, 200));
}
}
public class Converter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
IEnumerable<Point> enumerable = value as IEnumerable<Point>;
PointCollection points = null;
if (enumerable != null)
{
points = new PointCollection(enumerable);
}
return points;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotSupportedException("ConvertBack should never be called");
}
}
}

126
32-PolygonBinding/README.md Normal file
Просмотреть файл

@ -0,0 +1,126 @@
# How to data bind a Polygons Points to a data source – Part I
Today I will talk about one solution for data binding the Points property of a Polygon to a source collection.
The Polygon's Points property is of type System.Windows.Media.PointCollection, which can be found in Avalon's PresentationCore.dll. The first approach I've seen most people take when they want to bind the Points property to some data is to add a property of type PointCollection to their source, and bind to it. This works in the sense that the Polygon will display the Points it is binding to, but there are a couple of problems with this approach:
- If we change the collection of points in the data source, the change will not be reflected in the UI. This can be fixed by making the collection of points in the source a DependencyProperty.
- Adding a PointCollection to the data source introduces a dependence on an Avalon type that brings in several UI concepts (dependency properties and freezable objects). Ideally, we should strive for no coupling between the presentation technology and the data source. I'm sure those of you who are porting applications from WinForms to Avalon understand the reasons why this is good practice: If your data layer is independent from the presentation layer, it will be easier to migrate to the next UI technology, or, more commonly, to present the same data in multiple ways within the same application.
With these issues in mind, the first step is to figure out how to store the collection of points in a way that is independent of presentation-related Avalon code. Every time I need to data bind to a collection, I think of ObservableCollection&lt;T&gt; because it provides collection change notifications. This would be my collection of choice if I were binding an ItemsControl to the points, but this scenario is a little different.
Before I go any further, let me explain the two types of change notifications we support in data binding:
## Property change notifications
- Your data source needs to implement INotifyPropertyChanged and raise the PropertyChanged event, passing the appropriate property name in the event arguments, whenever a property changes. If you pass null or the empty string as the property name, the binding engine assumes that all properties of the source have changed. If you have a property in your source that is a collection, you can still benefit from property change notifications: you may need to notify the binding engine when the whole collection is being replaced by a different one. In other words, you may need to notify that the property that holds the collection has changed.
## Collection change notifications
- Your data source needs to implement INotifyCollectionChanged. Typically, when you need collection change notifications, you can simply use an ObservableCollection&lt;T&gt; which does all the hard work of implementing this interface for you. I haven't encountered a scenario where a customer needed collection change notifications but couldn't use ObservableCollection&lt;T&gt; for some reason.This interface notifies the UI when there is some change in the collection, such as when an item is added or removed. If your data source is a business object, the binding engine is smart enough to add or remove only the UI items that are needed, rather than regenerating the entire collection of UI items. If your source is XML or an IBindingList, we are as clever as we can be with the information that we are given by those APIs.The item generator associated with an ItemsControl listens to these collection change notifications and is responsible for determining the corresponding changes that will be done to elements in the UI.In this scenario, I am binding a single DP (Polygon's Points) to a collection. Since there is no ItemsControl involved, even if I provide collection change notifications in the data source, there is no item generator listening to those notifications. Now that I've determined that I don't need all the complexity of ObservableCollection&lt;T&gt;, I can go ahead and pick a simpler collection. I decided to pick List&lt;T&gt;.
But now I have a problem: if there is no one listening to collection changes in the UI, how can I automatically update my UI when I make a change to the source collection? The solution to this problem is to raise a property change notification when the collection changes. This may seem a little strange at first: there is a collection in the scenario, but there is no item generation in the UI, which makes this scenario somewhat unusual. But if you think about it, raising a property changed notification invalidates the Points property and causes the Polygon to re-render, which is what we want. This is how I implemented my data source:
public class PolygonItem : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName)
{
if (this.PropertyChanged != null)
{
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
private List<Point> points = new List<Point>();
public ReadOnlyCollection<Point> Points
{
get { return new ReadOnlyCollection<Point>(points); }
set
{
points = new List<Point>(value);
OnPropertyChanged("Points");
}
}
public void Subdivide()
{
int count = this.points.Count;
List<Point> newPoints = new List<Point>(count * 2);
for (int i = 0; i < count; i++)
{
Point previousPoint = this.points[i];
Point nextPoint = this.points[(i + 1) % count];
newPoints.Add(previousPoint);
Vector offset = nextPoint - previousPoint;
Vector normal = new Vector(offset.Y, -offset.X);
newPoints.Add(previousPoint + 0.5 * offset + 0.4 * normal);
}
this.points = newPoints;
OnPropertyChanged("Points");
}
public PolygonItem()
{
points.Add(new Point(275, 100));
points.Add(new Point(375, 200));
points.Add(new Point(275, 300));
points.Add(new Point(175, 200));
}
}
I decided to change the type of the public Points property to a ReadOnlyCollection&lt;T&gt; to prevent the user from making changes directly to the collection returned by the Points property, which would bypass change notifications.
At this point I have a source collection of type ReadOnlyCollection&lt;T&gt; and a target collection of type PointCollection. Because I have incompatible types and there is no default Converter between the two, I need to implement my own. Here is my code:
public class Converter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
IEnumerable<Point> enumerable = value as IEnumerable<Point>;
PointCollection points = null;
if (enumerable != null)
{
points = new PointCollection(enumerable);
}
return points;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotSupportedException("ConvertBack should never be called");
}
}
The UI is pretty straightforward:
<Window.Resources>
<local:PolygonItem x:Key="src"/>
<local:Converter x:Key="converter"/>
</Window.Resources>
<StackPanel>
<Button Click="ChangeSource" Width="150" Margin="20">Change data source</Button>
<Polygon Fill="#CD5C5C" Points="{Binding Source={StaticResource src}, Path=Points, Converter={StaticResource converter}}"/>
</StackPanel>
When the Window is loaded, a Polygon is displayed with the points from the source collection. I also included a Button in the UI that modifies the source collection and raises a property changed notification, which causes the UI to be updated.
private void ChangeSource(object sender, RoutedEventArgs e)
{
PolygonItem polygon = this.Resources["src"] as PolygonItem;
polygon.Subdivide();
}
This is a good solution when you make big changes to the source collection of points, like the scenario I have here. If you're simply adding or removing an item at a time from the existing collection, this solution may not scale well with very frequent changes to the source. Keep in mind that every time a property change notification is raised, we are creating a whole new PointCollection in the Converter. If you can, it's best to batch those operations and provide only one property change notification for a group of changes.
We've been discussing possible solutions that would make this scenario easier and more efficient for V2. Hopefully one of those solutions will make it to our final wish list.
Here is a screenshot of this application:
![](Images/32PolygonBinding.png)