I got a weird problem.
I got a collection of spaceObjects (planets) in an observablecollection. These objects move around the screen so their X and Y axis change all the time. This is the xaml code for it:
<DataTemplate DataType="{x:Type spaceObjects:SpaceObject}">
<Path Fill="{Binding Color, Converter={StaticResource ColorConverter}}"
Stroke="{Binding Border, Converter={StaticResource ColorConverter}}"
StrokeThickness="{Binding BorderThickness}" Tag="{Binding Name}">
<Path.Data>
<EllipseGeometry RadiusY="{Binding Radius}" RadiusX="{Binding Radius}">
<EllipseGeometry.Center>
<MultiBinding Converter="{StaticResource XYPositionConverter}">
<Binding Path="X"></Binding>
<Binding Path="Y"></Binding>
</MultiBinding>
</EllipseGeometry.Center>
</EllipseGeometry>
</Path.Data>
</Path>
</DataTemplate>
I shortened the xaml code by a lot because there is a lot of redundant information. Just know that it's an observablecollection with multiple kinds of objects (that's why I use the datatemplate), and the itemscontrol in which this datatemplate exists also is bound to another collection. I don't think this has anything to do with the issue though.
So, before we get to the faulty code, I'm going to show you some code that doesn't throw errors. This method moves the spaceObjects and does some other stuff that aren't UI related. No problems.
private void MoveAndCheckLegacy(Object source, ElapsedEventArgs e)
{
Parallel.For(0, _spaceObjects.Count, i =>
{
var isSpaceObject = _spaceObjects[i] is SpaceObject;
if (isSpaceObject)
{
var spaceObject = (SpaceObject) _spaceObjects[i];
spaceObject.Move();
CheckForCollisionLegacy(spaceObject);
}
else
{
var lineObject = (LineSpaceObject) _spaceObjects[i];
lineObject.UpdatePosition();
}
});
}
It just loops through the spaceObjects in the observablecollection, changes their X and Y. The multibinding notices this and acts accordingly.
Now I wrote a bigger method which also inserts elements into a QuadTree, but the moving part is just part of a for loop like before (used to be parralel too, but I changed it while debugging this problem).
var qtree = new QuadTree(new Rectangle(0, 0, 800, 600), "base");
for (int i = 0; i < _spaceObjects.Count - 1; i++)
{
var isSpaceObject = _spaceObjects[i] is SpaceObject;
if (isSpaceObject)
{
var spaceObject = (SpaceObject)_spaceObjects[i];
spaceObject.Move();
// Printing X and Y and Name for debugging purposes
Trace.WriteLine($"{spaceObject.Name}: ({spaceObject.X}, {spaceObject.Y})");
}
else // Some stuff to do with other objects.
{
var lineObject = (LineSpaceObject)_spaceObjects[i];
lineObject.UpdatePosition();
}
}
// Insert spaceObjects into QuadTree
for (int i = 0; i < _spaceObjects.Count - 1; i++)
{
var so = _spaceObjects[i] is SpaceObject;
if (so) qtree.Insert((SpaceObject)_spaceObjects[i]);
}
// Show Quadtree on UI
ShowQtree(qtree);
This runs fine for a while but then my converter throws the DependencyProperty.UnsetValue error. So I started printing the values that my converter receives before it does any converting like this:
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
Trace.WriteLine($"({values[0]}. {values[1]})");
var i = (decimal) values[0];
var y = (decimal) values[1];
var a = (int) i;
var b = (int) y;
return new Point(a, b);
}
Now the weird part. I also print the x,y values of the planets (for debugging purposes). Here is the output if just one spaceObject is present in the observable collection.
Kobol: (127,5, 12,0)
(127,5. 18,0)
Kobol: (127,8, 11,4)
(127,5. 11,4) <<<<< right here
({DependencyProperty.UnsetValue}. 11,4)
It seems that just before it tries to convert anything, the converter has the correct values! I put 'right here' at the place where you can see the values inside the converter. Then the next moment it seem to go into the converter again, and now it doesn't have a value. I can bet you that the actual planet does have a X and Y value (can't check this though, because the application freezes when being debugged :( ). This moving algorithm hasn't changed and has worked with no problems (and still works with no problems), outside of this method.
Does anyone have ANY ideas or suggestions?
If it isn't clear just let me know. I'll remove and add details where needed.
EDIT**
After commenting out most of the method that involves moving and quadtree stuff, I got it to work like the lest complicated move method. Could it be that my method is too big? I forgot to mention that it runs inside of a Timer thread.... Maybe it's too slow which causes problems?
Your XYPositionConverter
must handle this special value. WPF
will use DependencyProperty.UnsetValue when probing the converter. This typically occurs just as your DataTemplate
is created, perhaps out of view.
So, just make sure to handle this in your converter:
public class XYPositionConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
if (values == null ||
values.Any(x => x == null || x == DependencyProperty.UnsetValue))
{
return DependencyProperty.UnsetValue;
}
// ...
return result;
}
// ...
}