Drawing two polylines from one PointCollection throws exception (Silverlight 5)
-
27-10-2019 - |
Pergunta
I'm using Silverlight 5 with MVVM.
I have a ViewModel that is a Singleton. The ViewModel exposes a PointCollection
that I am using to draw a Polyline
in one of my views.
If I try to draw the same Polyline
in a second view, by databinding to the PointCollection
again, I get a "value does not fall within the expected range" exception.
As far as I could find out (with my limited Silverlight knowledge), this is caused by the fact that PointCollections
are not shareable.
Is there a workaround? How can I get a second Polyline drawn that is identical to the first? I want to databind two Polylines to one PointCollection at the same time.
Edit: I haven't found a solution, but someone with the same problem here. According to Microsoft:
This MSDN page mentions that some objects are not shareable and will genereate a "value out of range" exception.
http://msdn.microsoft.com/en-us/library/system.windows.resourcedictionary(VS.95).aspx
The PointCollection page also mentions that it is not shareable.
http://msdn.microsoft.com/en-us/library/system.windows.media.pointcollection(VS.95).aspx
Currently, this is by design behavior. However, we are evaluating this to see whether we can either change the behavior or at least the exception text.
Solução
Have a look at this question: Why doesn't this data binding work?
And at this one too: 2nd time binding to PointCollection not being rendered
As you gave little details I am not quite sure what is going on but these posts might help out. If not, please post your code.
I did some testing and the best solution I can think of is this:
using System.ComponentModel;
using System.Windows;
using System.Windows.Media;
namespace SilverlightApplication6
{
public class DemoVM : INotifyPropertyChanged
{
#region PointsClone Property
private PointCollection _pointsClone;
public PointCollection PointsClone
{
get
{
return _pointsClone;
}
set
{
if (_pointsClone != value)
{
_pointsClone = value;
OnPropertyChanged("PointsClone");
}
}
}
#endregion
#region Points Property
private PointCollection _points;
public PointCollection Points
{
get
{
return _points;
}
set
{
if (_points != value)
{
_points = value;
PointsClone.Clear();
foreach (var point in _points)
{
PointsClone.Add(point);
}
OnPropertyChanged("Points");
}
}
}
#endregion
public DemoVM()
{
PointsClone = new PointCollection();
Points = new PointCollection();
}
public void AddPoint(Point point)
{
Points.Add(point);
PointsClone.Add(point);
}
public void ClearPoints()
{
Points.Clear();
PointsClone.Clear();
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
var p = PropertyChanged;
if (p != null)
{
p(this, new PropertyChangedEventArgs(propertyName));
}
}
}
}
Bind one PolyLine.Points to Points and the other PolyLine.Points to PointsClone.
It is a bit ugly because it will break when you use vm.Points.Add(point) instead of vm.AddPoint(point). By applying proper encapsulation you might be able to solve that.
Outras dicas
I found a solution here: duplicate the PointCollection in the getter.
private PointCollection sourcePoints;
public PointCollection SourcePoints
{
get
{
// create a new instance of PointCollection for binding
PointCollection newPoints = new PointCollection();
foreach (Point p in sourcePoints)
{
newPoints.Add(p);
}
return newPoints;
}
Maybe your PointCollection is Freezed and that's making the trouble.
MSDN:
Freezable Features: Because it inherits from the Freezable class, the PointCollection class provides several special features: PointCollection objects can be declared as resources, shared among multiple objects, made read-only to improve performance, cloned, and made thread-safe. For more information about the different features provided by Freezable objects, see the Freezable Objects Overview.